【发布时间】:2022-11-08 02:58:31
【问题描述】:
将Eigen::Matrix 与一组随机列索引进行矩阵乘法的最快方法是什么?
Eigen::MatrixXd mat = Eigen::MatrixXd::Random(100, 1000);
// vector of random indices (linspaced here for brevity)
Eigen::VectorXi idx = VectorXi::LinSpaced(8,1000,9);
我正在使用 RcppEigen 和 R,它仍然在 Eigen 的 3.x 版本上(不支持带有索引数组的 ()),无论如何,我的理解是 () 运算符仍然执行深层复制。
现在我正在做一个深拷贝并生成一个新矩阵,其中只包含idx 中的列的数据:
template <typename T>
inline Eigen::Matrix<T, -1, -1> subset_cols(const Eigen::Matrix<T, -1, -1>& x, const std::vector<size_t>& cols) {
Eigen::Matrix<T, -1, -1> y(x.rows(), cols.size());
for (size_t i = 0; i < cols.size(); ++i)
y.col(i) = x.col(cols[i]);
return y;
}
然后做矩阵乘法:
Eigen::MatrixXd sub_mat = subset_cols(mat, idx);
Eigen::MatrixXd a = sub_mat * sub_mat.transpose();
a 是我想要的。
一定有办法避免深拷贝,而是使用Eigen::Map?
22 年 5 月 9 日编辑:作为对@Markus 的回复,他提出了一种使用原始数据访问和Eigen::Map 的方法。所提出的解决方案比深拷贝的矩阵乘法要慢一些。这里的基准测试是使用 Rcpp 代码和 R 完成的:
//[[Rcpp::depends(RcppClock)]]
#include <RcppClock.h>
//[[Rcpp::export]]
void bench(Eigen::MatrixXd mat, Eigen::VectorXi idx){
Rcpp::Clock clock;
size_t reps = 100;
while(reps-- > 0){
clock.tick("copy");
Eigen::MatrixXd sub_mat = subset_cols(mat, idx);
Eigen::MatrixXd a = sub_mat * sub_mat.transpose();
clock.tock("copy");
clock.tick("map");
double *b_raw = new double[mat.rows() * mat.rows()];
Eigen::Map<Eigen::MatrixXd> b(b_raw, mat.rows(), mat.rows());
subset_AAt(b_raw, mat, idx);
clock.tock("map");
}
clock.stop("clock");
}
这是一个 100,000 列的 100 行矩阵的 3 次运行。我们正在对 (1) 10 列的子集、(2) 1000 列的子集和 (3) 10000 列的子集进行矩阵乘法。
回复:
bench(
matrix(runif(100000 * 100), 100, 100000),
sample(100000, 10) - 1)
# Unit: microseconds
# ticker mean sd min max neval
# copy 31.65 4.376 30.15 69.46 100
# map 113.46 21.355 68.54 166.29 100
bench(
matrix(runif(100000 * 100), 100, 100000),
sample(100000, 1000) - 1)
# Unit: milliseconds
# ticker mean sd min max neval
# copy 2.361 0.5789 1.972 4.86 100
# map 9.495 2.4201 7.962 19.90 100
bench(
matrix(runif(100000 * 100), 100, 100000),
sample(100000, 10000) - 1)
# Unit: milliseconds
# ticker mean sd min max neval
# copy 23.04 2.774 20.95 42.4 100
# map 378.14 19.424 351.56 492.0 100
我在几台机器上进行了基准测试,结果相似。以上结果来自一个好的 HPC 节点。
编辑:2022 年 5 月 10 日这是一个代码 sn-p,它对列子集执行矩阵乘法的速度与不直接使用 Eigen BLAS 的任何代码一样快:
template <typename T>
Eigen::Matrix<T, -1, -1> subset_AAt(const Eigen::Matrix<T, -1, -1>& A, const Eigen::VectorXi& cols) {
const size_t n = A.rows();
Eigen::Matrix<T, -1, -1> AAt(n, n);
for (size_t k = 0; k < cols.size(); ++k) {
const T* A_data = A.data() + cols(k) * n;
for (size_t i = 0; i < n; ++i) {
T tmp_i = A_data[i];
for (size_t j = 0; j <= i; ++j) {
AAt(i * n + j) += tmp_i * A_data[j];
}
}
}
return AAt;
}
【问题讨论】:
-
@Sedenion 感谢您在对替代方法进行基准测试方面所做的努力。你的想法是有道理的,但似乎任何收获都可能非常微不足道。是的,在我个人使用中,我正在使用启用的 AVX 和英特尔 MKL,但普通用户的性能是我首先关心的问题。
标签: c++ linear-algebra eigen rcppeigen