根据值的数量,使用三种不同方法的矩阵相乘会得到不同的结果



我想乘以两个矩阵AB,并想比较三种不同的方法。其中之一是简单地在B的列上迭代并将其乘以矩阵A,第二个是使用来自armadillo的函数each_col()并应用lambda,第三个是简单地相乘A * B。结果代码如下所示:

#include <complex>
#include <iostream>
#include <chrono>
#include <armadillo>
constexpr int num_values = 2048;
constexpr int num_rows = 128;
constexpr int num_cols = num_values / num_rows;
constexpr int bench_rounds = 100;
void test_multiply_loop(const arma::mat &in_mat,
const arma::mat &init_mat,
arma::mat &out_mat) {
for(size_t i = 0; i < in_mat.n_cols; ++i) {
out_mat.col(i) = init_mat * in_mat.col(i);
}
}
void test_multiply_matrix(const arma::mat &in_mat,
const arma::mat &init_mat,
arma::mat &out_mat) {
out_mat = init_mat * in_mat;
}
void test_multiply_lambda(const arma::mat &in_mat,
const arma::mat &init_mat,
arma::mat &out_mat) {
out_mat = in_mat;
out_mat.each_col([init_mat](arma::colvec &a) {
a = init_mat * a;
});
}

int main()
{
std::cout << "Hello World" << "n";
//Create matrix
arma::colvec test_vec = arma::linspace(1, num_values, num_values);
arma::mat init_mat = arma::reshape(test_vec, num_rows, num_cols);
arma::mat out_mat_loop = arma::zeros(num_rows, num_cols),
out_mat_lambda = arma::zeros(num_rows, num_cols),
out_mat_matrix = arma::zeros(num_rows, num_cols);
arma::mat test_mat = arma::eye(num_rows, num_rows);
for(size_t i = 0; i < num_rows; ++i)
for(size_t j = 0; j < num_rows; ++j)
test_mat(i, j) *= (i + 1);
auto t1 = std::chrono::high_resolution_clock::now();
for(size_t i = 0; i < bench_rounds; ++i)
test_multiply_loop(init_mat, test_mat, out_mat_loop);
auto t2 = std::chrono::high_resolution_clock::now();
auto t3 = std::chrono::high_resolution_clock::now();
for(size_t i = 0; i < bench_rounds; ++i)
test_multiply_lambda(init_mat, test_mat, out_mat_lambda);
auto t4 = std::chrono::high_resolution_clock::now();
auto t5 = std::chrono::high_resolution_clock::now();
for(size_t i = 0; i < bench_rounds; ++i)
test_multiply_matrix(init_mat, test_mat, out_mat_matrix);
auto t6 = std::chrono::high_resolution_clock::now();
std::cout << "Multiplication by loop:tt" << std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count() << 'n';
std::cout << "Multiplication by lambda:t" << std::chrono::duration_cast<std::chrono::microseconds>( t4 - t3 ).count() << 'n';
std::cout << "Multiplication by internal:t" << std::chrono::duration_cast<std::chrono::microseconds>( t6 - t5 ).count() << 'n';
std::cout << "Loop and matrix are equal:t" << arma::approx_equal(out_mat_loop, out_mat_matrix, "reldiff", 0.1) << 'n';
std::cout << "Loop and lambda are equal:t" << arma::approx_equal(out_mat_loop, out_mat_lambda, "reldiff", 0.1) << 'n';
std::cout << "Matrix and lambda are equal:t" << arma::approx_equal(out_mat_matrix, out_mat_lambda, "reldiff", 0.1) << 'n';
return 0;
}

现在,对于num_rows = 128,我的输出是

Multiplication by loop:         124525
Multiplication by lambda:       46690
Multiplication by internal:     1270
Loop and matrix are equal:      0
Loop and lambda are equal:      0
Matrix and lambda are equal:    0

但对于num_rows = 64,我的输出是

Multiplication by loop:         32305
Multiplication by lambda:       6517
Multiplication by internal:     56344
Loop and matrix are equal:      1
Loop and lambda are equal:      1
Matrix and lambda are equal:    1

为什么增加列的数量时输出会如此不同?为什么函数的时间变化如此之大?

这三个函数确实在做同样的事情,结果应该是相同的,除了精度差异,这应该无关紧要,因为您将结果与arma::approx_equal进行比较。

在我的机器中,您提到的两种尺寸和我尝试过的其他更高值的输出都是正确的。我无法重现这个问题。

作为参考,我尝试了armadillo 9.870.2,我链接了openblas和lapack。

你是如何安装armadillo的

Armadillo的大部分功能都使用blas和lapack。对于矩阵乘法,它使用了一些blas实现。blas有几种实现,如openblas、mkl甚至cublas(用于在gpu中运行(等

Armadillo可以在没有blas实现的情况下工作,它可以使用自己的(较慢的(实现来进行矩阵乘法。我还没有尝试过在不与blas链接的情况下使用它自己的实现。

另一个可能相关的点是,根据blas实现,矩阵乘法可能使用多个线程,但通常只针对大矩阵,因为对小矩阵使用多个螺纹会影响性能。也就是说,根据矩阵大小,用于执行乘法的代码路径可能不同(但如果两个代码路径不能产生相同的答案,那当然是一个错误(。

最新更新