Умножение матриц с использованием трех разных методов дает разные результаты в зависимости от количества значений - PullRequest
0 голосов
/ 04 мая 2020

Я хотел бы умножить две матрицы A и B и хотел сравнить три разных метода. Один из них просто перебирает столбцы B и умножает их на матрицу A, второй использует функцию each_col() из броненосца и применяет лямбду, а третий просто умножает 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:\t\t" << 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

Почему выходные данные так сильно отличаются при увеличении количества столбцов? И почему время изменения функций так сильно меняется?

1 Ответ

1 голос
/ 04 мая 2020

Три функции действительно выполняют одно и то же, и результат должен быть одинаковым, за исключением различий в точности, которые не должны иметь значения, поскольку вы сравниваете результаты с arma::approx_equal.

В моей машине вывод был поправьте оба упомянутых вами размера и другие более высокие значения, которые я пробовал. Я не смог воспроизвести проблему.

Для справки, я попробовал с броненосцем 9.870.2 и связался с openblas и lapack.

Как вы установили броненосец?

Armadillo использует blas и lapack для большей части своей функциональности. Для умножения матриц используется некоторая блестящая реализация. Существует несколько реализаций для blas, таких как openblas, mkl даже cublas (для запуска в gpu) и т. Д. c ..

Armadillo может работать без реализации blas, где он будет использовать свою собственную (медленнее ) реализация для умножения матриц. Я не пробовал использовать его собственную реализацию без связи с blas.

Еще один момент, который может быть связан с тем, что в зависимости от реализации blas для умножения матриц может использоваться несколько потоков, но обычно только для больших матриц, поскольку использование нескольких потоков для небольших матриц может снизить производительность. То есть путь кода, используемый для выполнения умножения, может отличаться в зависимости от размера матрицы (но, конечно, это будет ошибкой, если оба пути кода не приведут к одному и тому же ответу).

...