【发布时间】:2021-01-09 07:10:46
【问题描述】:
我正在使用 C++ 和 Eigen 库来完成匹配任务。但是,结果非常低效。下面将描述任务和我当前的解决方案。
任务
该任务涉及两个相同大小的大地图,mapL 和 mapR。匹配过程按行进行。例如,mapL 的r-th 行中的元素将与mapR 的r-th 行中的元素匹配。两个匹配元素的值可能不完全相同,所以我需要在mapR中寻找差异最小的元素。
为了计算差异,我执行以下步骤:
- 在
mapL的r-th行中提取一个元素。 - 提取
mapR的r-th行。 -
mapR的行减去mapL的元素)。 - 对
mapL中的每个元素重复上述步骤。
示例
一个例子将有助于理解上述过程。假设我们有
mapL = [ 1 2 3 4, 5;
6 7 8 9 10;
11 12 13 14 15];
mapR = [ 5 4 3 2, 1;
10 9 8 7 6;
15 14 13 12 11];
对于mapL中的第一个元素,我想获取一个向量difference用于后续的一些计算
difference = [5 4 3 2 1] - [1] = [4 3 2 1 0];
当前解决方案
在构建 C++ 代码时,我尝试使用 .replicate 和 .colwise 对过程进行矢量化。下面是一个示例代码:
int nRow = 1800;
int nCol = 2000;
// Setup two random maps for illustration purposes
MatrixXf mapL = MatrixXf::Random(nRow, nCol);
MatrixXf mapR = MatrixXf::Random(nRow, nCol);
// Initialize some parameters
VectorXf rowL;
MatrixXf rowR(1, nCol);
MatrixXf repeatR;
MatrixXf difference;
// Loop through every row
for (int r = 0; r < nRow; r++) {
// Extract a row from mapL and mapR
rowL = mapL.row(r);
rowR = mapR.row(r);
// Repeat rowR along the col direction
repeatR = rowR.replicate(nCol, 1);
// Compute the difference by .colwise
difference = repeatR.colwise()-=rowL;
/*
Some other codes after the difference is computed
*/
}
问题
上面的代码在我的机器(VS 2017,发布模式,x64)上需要大约 61 秒来遍历每一行。我在 MATLAB 中实现了类似的代码,只需要大约 6 秒。有什么方法可以提高 C++ 代码的效率吗?我对 C++ 很陌生,因此如果我遗漏了任何重要的概念,请告诉我。非常感谢!
编辑
经过更多的测试,我找到了一种更有效的方法来完成任务。分析我的原始代码显示.replicate 和.colwise 操作消耗了大量的计算能力(~90%)。使用嵌套循环需要
int nRow = 1800;
int nCol = 2000;
MatrixXf mapL = MatrixXf::Random(nRow, nCol);
MatrixXf mapR = MatrixXf::Random(nRow, nCol);
VectorXf rowL;
ArrayXf rowR(1, nCol);
ArrayXf difference;
for (int r = 0; r < nRow; r++) {
rowL = mapL.row(r);
rowR = mapR.row(r).array();
for (int c = 0; c < nCol; c++) {
difference = rowR - rowL(c);
}
}
【问题讨论】:
-
// Extract a row from mapL and mapR-- 我没有使用 Eigen,但有理由实际“提取”一行吗?是否可以仅引用现有行?row(r)究竟返回了什么?是参考吗?如果它确实返回了引用,那么您的代码似乎不必要地复制了该行,即VectorXf& rowL = mapL.row(r);似乎更快 ifrow()函数返回引用。 -
这是一个 O(n^2) 算法。如果在匹配之前对行进行排序,则可以在 O(n log n) 中执行此操作。
-
感谢 cmets。
row()函数实际上占用了非常小的计算量。分析原始代码显示.replicate和.colwise消耗了大约 90% 的计算负载。改成嵌套循环后,速度明显提升。新代码包含在 Edit 中。 -
请先运行您的代码(尺寸较小)并启用调试。至少在你的第二个 sn-p
ArrayXf rowR(1, nCol)应该在运行时失败(对于nCol>1)。另外,请发布minimal reproducible examples,并确保实际使用中间结果(否则编译器可能会决定将它们优化掉)。
标签: c++ performance vectorization eigen broadcast