[英]Eigen: Why is Map slower than Vector3d for this template expression?
我在 x、y、z 模式中的std::vector<double>
和索引的std::vector<int>
中有一組點,其中每個連續整數的三元組是一個面的連通性。 基本上是一個簡單的三角形網格數據結構。
我必須計算所有面孔的面積,並且我正在對幾種方法進行基准測試:
我可以像這樣將大塊數據包裝在Eigen::Map<const Eigen::Vector3d>
中:
static void face_areas_eigenmap(const std::vector<double>& V,
const std::vector<int>& F,
std::vector<double>& FA) {
// Number of faces is size / 3.
for (auto f = 0; f < F.size() / 3; ++f) {
// Get vertex indices of face f.
auto v0 = F[f * 3];
auto v1 = F[f * 3 + 1];
auto v2 = F[f * 3 + 2];
// View memory at each vertex position as a vector.
Eigen::Map<const Eigen::Vector3d> x0{&V[v0 * 3]};
Eigen::Map<const Eigen::Vector3d> x1{&V[v1 * 3]};
Eigen::Map<const Eigen::Vector3d> x2{&V[v2 * 3]};
// Compute and store face area.
FA[f] = 0.5 * (x1 - x0).cross(x2 - x0).norm();
}
}
或者我可以選擇像這樣創建Eigen::Vector3d
:
static void face_areas_eigenvec(const std::vector<double>& V,
const std::vector<int>& F,
std::vector<double>& FA) {
for (auto f = 0; f < F.size() / 3; ++f) {
auto v0 = F[f * 3];
auto v1 = F[f * 3 + 1];
auto v2 = F[f * 3 + 2];
// This is the only change, swap Map for Vector3d.
Eigen::Vector3d x0{&V[v0 * 3]};
Eigen::Vector3d x1{&V[v1 * 3]};
Eigen::Vector3d x2{&V[v2 * 3]};
FA[f] = 0.5 * (x1 - x0).cross(x2 - x0).norm();
}
}
最后,我還在考慮具有顯式叉積和范數的硬編碼版本:
static void face_areas_ptr(const std::vector<double>& V,
const std::vector<int>& F, std::vector<double>& FA) {
for (auto f = 0; f < F.size() / 3; ++f) {
const auto* x0 = &V[F[f * 3] * 3];
const auto* x1 = &V[F[f * 3 + 1] * 3];
const auto* x2 = &V[F[f * 3 + 2] * 3];
std::array<double, 3> s0{x1[0] - x0[0], x1[1] - x0[1], x1[2] - x0[2]};
std::array<double, 3> s1{x2[0] - x0[0], x2[1] - x0[1], x2[2] - x0[2]};
std::array<double, 3> c{s0[1] * s1[2] - s0[2] * s1[1],
s0[2] * s1[0] - s0[0] * s1[2],
s0[0] * s1[1] - s0[1] * s1[0]};
FA[f] = 0.5 * std::sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]);
}
}
我已經對這些方法進行了基准測試,使用Eigen::Map
的版本總是最慢的,盡管它與使用Eigen::Vector3d
的方法完全相同,但我預計性能不會發生變化,因為 map 基本上是一個指針。
-----------------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------------
BM_face_areas_eigenvec 59757936 ns 59758018 ns 11
BM_face_areas_ptr 58305018 ns 58304436 ns 11
BM_face_areas_eigenmap 62356850 ns 62354710 ns 10
我嘗試在 map 版本中使用與指針版本相同的代碼切換 Eigen 模板表達式:
std::array<double, 3> s0{x1[0] - x0[0], x1[1] - x0[1], x1[2] - x0[2]};
std::array<double, 3> s1{x2[0] - x0[0], x2[1] - x0[1], x2[2] - x0[2]};
std::array<double, 3> c{s0[1] * s1[2] - s0[2] * s1[1],
s0[2] * s1[0] - s0[0] * s1[2],
s0[0] * s1[1] - s0[1] * s1[0]};
FA[f] = 0.5 * std::sqrt(c[0] * c[0] + c[1] * c[1] + c[2] * c[2]);
神奇的是,時間是可比的:
-----------------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------------
BM_face_areas_array 58967864 ns 58967891 ns 11
BM_face_areas_ptr 60034545 ns 60034682 ns 11
BM_face_areas_eigenmap 60382482 ns 60382027 ns 11
Eigen::Map
在 Eigen 表達式中是否有問題需要注意?
查看編譯器 output 似乎第二個版本通過將其中一些負載聚合為向量負載,使編譯器發出更少的 memory 負載。 https://godbolt.org/z/qs38P41eh
Eigen 的cross
代碼不包含任何顯式矢量化。 這取決於編譯器是否能很好地處理它。 而且因為你在一個表達式(減法)上調用了 cross,編譯器很快就放棄了。 基本上,沒有找到相同的優化是編譯器的錯。
您的第三個代碼與第二個代碼的工作方式相同,因為編譯器將減法(創建 s0 和 s1)識別為可以向量化的操作,從而產生等效代碼。 如果您這樣做,您可以使用 Eigen 實現相同的效果:
Eigen::Map<const Eigen::Vector3d> x0{&V[v0 * 3]};
Eigen::Map<const Eigen::Vector3d> x1{&V[v1 * 3]};
Eigen::Map<const Eigen::Vector3d> x2{&V[v2 * 3]};
Eigen::Vector3d s0 = x1 - x0;
Eigen::Vector3d s1 = x2 - x0;
// Compute and store face area.
FA[f] = 0.5 * s0.cross(s1).norm();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.