簡體   English   中英

Eigen:對於這個模板表達式,為什么 Map 比 Vector3d 慢?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM