2013-07-03 1 views
1

Мне нужно использовать матричную структуру данных в моей программе, в то время как C++ имеет 2d массивы, это очень низкий уровень, в то время как некоторые библиотеки, такие как Eigen, обеспечивают более высокий уровень структуры матричных данных. Но, на мой взгляд, независимо от того, насколько хорошо библиотека работает в некоторых высококвалифицированных операциях, таких как svd, быстрая скорость для основных операций, включая чтение (доступ), запись, суммирование, точку, должна быть предварительным требованием к таким библиотекам , Поскольку в реальных приложениях такие базовые операции могут быть гораздо более частыми, чем те, которые являются высококвалифицированными, если библиотека работает на таких операциях, она может оказаться бременем или даже узким местом системы.Что я должен ожидать, когда я использую структуру данных с плотной матрицей Eigen?

Итак, я пишу некоторые очень простые программы, используя как 2d-массив, так и плотную матрицу Eigen3 (MatrixXd), и сравниваю их характеристики с четырьмя основными операциями, получается, что большую часть времени 2d-массив выигрывает Eigen3, что весьма неутешительно , Я перечислю некоторые из моих результатов теста ниже (код в конце приложения):

10000X10000 матрица, компилировать команду: г ++ -o test.o test.cpp -O0 -msse2

Эйген :

[! COST] init: 6.8 сек.

[! COST] читать: 14.85 сек.

[! COST] написать письмо: 23.02 сек.

[! COST] сумма: 3.28 сек.

[! COST] dot: 3,12 sec.

CPP:

INIT [COST]: 1,81 сек.

[! COST] читать: 2.4 сек.

[! COST] запись: 3.4 сек.

[! COST] сумма: 0.63 сек.

[! COST] dot: 0.52 sec.

10000X10000 матрица, компилировать команду: г ++ -o test.o test.cpp -O3 -msse2

Эйген:

INIT [COST]: 2,44 сек.

[! COST] читать: 2.16 сек.

[! COST] запись: 2.18 сек.

[! COST] сумма: 0.26 секунд.

[! COST] dot: 0.26 sec.

CPP:

INIT [COST]: 1,71 сек.

[! COST] читать: 2.06 сек.

[! COST] написать письмо: 2.24 сек.

[! COST] сумма: 0.15 sec.

[! COST] dot: 0.06 sec.

Однако я до сих пор есть некоторые сомнения по этому поводу, может быть, я не должен ожидать более высокий уровень абстракции матричной структуры должны работать так же быстро, как его сырой вариант, если да, то следует ожидать использования библиотеки, такие как Эйген? Обратите внимание, что в моей программе есть несколько высококвалифицированных операций как SVD, тогда как есть более основные операции, такие как доступ к матрице и запись матрицы.

Приложение, test.cpp:

#include <iostream> 
#include <Eigen/Dense> 
#include <ctime> 
using Eigen::MatrixXf; 

inline int cpp_testor_read(float **m, const int M, const int N) 
{ 
    float randomTmp = 0; 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
     { 
      randomTmp += m[i][j]; 
      randomTmp -= m[j][i]; 
     } 
    return randomTmp; 
} 

inline int eigen_testor_read(MatrixXf m, const int M, const int N) 
{ 
    float randomTmp = 0; 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
     { 
      randomTmp += m(i, j); 
      randomTmp -= m(j, i); 
     } 
    return randomTmp; 
} 

inline int cpp_testor_write(float **m, const int M, const int N) 
{ 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
     { 
      m[i][j] += m[j][i]; 
      m[j][i] -= m[i][j]; 
     } 
    return m[rand()%10000][rand()%10000]; 
} 

inline int eigen_testor_write(MatrixXf m, const int M, const int N) 
{ 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
     { 
      m(i, j) += m(j, i); 
      m(j, i) -= m(i, j); 
     } 
    return m(rand()%10000, rand()%10000); 
} 

inline int cpp_testor_sum(float **m, const int M, const int N, float val) 
{ 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
     { 
      m[i][i] += m[i][j]; 
     } 
    return m[rand()%1000][rand()%1000]; 
} 

inline int eigen_testor_sum(MatrixXf m, const int M, const int N, float val) 
{ 
    m += m; 
    return m(0, 0); 
} 

inline int cpp_testor_dot(float **m, const int M, const int N, float val) 
{ 
    float randomTmp = 0; 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
     { 
      m[i][j] *= val; 
     } 
    return m[rand()%1000][rand()%1000]; 
} 

inline int eigen_testor_dot(MatrixXf m, const int M, const int N, float val) 
{ 
    m *= val; 
    return m(0, 0); 
} 

float** cpp_generator_mtarix(const int M, const int N) 
{ 
    float **m = new float*[M]; 
    for (int i = 0; i < M; i ++) 
     m[i] = new float[N]; 
    return m; 
} 

MatrixXf& eigen_generator_matrix(const int M, const int N) 
{ 

    static MatrixXf m(M,N); 
    return m; 
} 

int main() 
{ 
    const int M = 10000; 
    const int N = M; 
    int antiopt = 0; 
    srand(time(NULL)); 
    float val1 = rand()%10000 + 1; 
    float val2 = rand()%10000 + 1; 
    std::cout<< M << " " << N << std::endl; 

    std::cout<<"Eigen:" << std::endl; 
    size_t t = clock(); 
    //MatrixXf m = eigen_generator_matrix(M, N); 
    MatrixXf m(M,N); 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
      m(i,j) = rand()%1000 + 1; 
    t = clock() - t; 
    std::cout<< "[!COST] init: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += eigen_testor_read(m,M,N); 
    t = clock() - t; 
    std::cout<< "[!COST] read: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += eigen_testor_write(m,M,N); 
    t = clock() - t; 
    std::cout<< "[!COST] write: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += eigen_testor_sum(m,M,N, val1); 
    t = clock() - t; 
    std::cout<< "[!COST] sum: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += eigen_testor_dot(m,M,N, val2); 
    t = clock() - t; 
    std::cout<< "[!COST] dot: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    std::cout<<"CPP:" << std::endl; 
    t = clock(); 
    //float **mm = cpp_generator_mtarix(M, N); 
    float **mm = new float*[M]; 
    for (int i = 0; i < M; i ++) 
     mm[i] = new float[N]; 
    for (int i = 0; i < M; i ++) 
     for (int j = 0; j < N; j ++) 
      mm[i][j] = rand()%1000 + 1; 
    t = clock() - t; 
    std::cout<< "[!COST] init: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += cpp_testor_read(mm,M,N); 
    t = clock() - t; 
    std::cout<< "[!COST] read: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += cpp_testor_write(mm,M,N); 
    t = clock() - t; 
    std::cout<< "[!COST] write: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += cpp_testor_sum(mm,M,N, val1); 
    t = clock() - t; 
    std::cout<< "[!COST] sum: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    t = clock(); 
    antiopt += cpp_testor_dot(mm,M,N, val2); 
    t = clock() - t; 
    std::cout<< "[!COST] dot: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl; 

    std::cout<<antiopt<<std::endl; 
} 

ответ

3

Для собственных тестовых функций, которые Вы передаете матрицу по значению, а это значит, что он должен быть скопирован. Время для этих (больших) копий включено в эталон.

Вы должны вместо этого передать матрицы по ссылке, чтобы избежать накладных расходов на копирование и получить ту же семантику, что и версия массива. С этим изменением я получаю результаты, как в следующем, которые звучат достаточно быстро мне:

10000 10000 
Eigen: 
[!COST] init: 3.5 sec. 
[!COST] read: 2.98 sec. 
[!COST] write: 3.03 sec. 
[!COST] sum: 0.06 sec. 
[!COST] dot: 0.07 sec. 
CPP: 
[!COST] init: 1.46 sec. 
[!COST] read: 3.41 sec. 
[!COST] write: 3.57 sec. 
[!COST] sum: 0.14 sec. 
[!COST] dot: 0.05 sec. 

(Также обратите внимание, что бенчмаркинг с -O0 бессмысленно:. Вы явно указать компилятору, чтобы не сделать это быстро)

+0

Да , это [как и мои результаты] (http://i.stack.imgur.com/h8fdt.png) Я только что получил, когда я изменил все (хотя и встроенные) вызовы функций, чтобы принять «MatrixXf &» вместо «MatrixXf» – bobobobo

+0

Да! Спасибо, я не могу поверить, что я проигнорировал это ... но кажется, что при последовательном доступе массив 2d все еще несколько раз быстрее, чем собственный, я добавляю как m [i] [j], так и m [j] [i] в мой тестовый код, но если только использовать m [i] [j] в массиве 2d и m (j, i) в собственном, 2d-массив выполняется в 3 раза быстрее. Не знаю, почему – chentingpc

+1

@chentingpc Из-за того, как память кеша компьютеров, итерация по столбцам или строкам сначала будет иметь разную производительность в зависимости от того, будет ли ваша матрица [основной или основной строки] (http://en.wikipedia.org/wiki/ Строка-major_order). –