В поисках оптимального умножения матрицы на матрицу с использованием eigen3 (и, надеюсь, извлекая выгоду из поддержки SIMD) я написал следующий тест:
#include <iostream>
#include <Eigen/Dense>
#include <ctime>
using namespace Eigen;
using namespace std;
const int test_size= 13;
const int test_size_16b= test_size+1;
typedef Matrix<double, Dynamic, Dynamic, ColMajor, test_size_16b, test_size_16b> TestMatrix_dyn16b_t;
typedef Matrix<double, Dynamic, Dynamic> TestMatrix_dynalloc_t;
typedef Matrix<double, test_size, test_size> TestMatrix_t;
typedef Matrix<double, test_size_16b, test_size_16b> TestMatrix_fix16b_t;
template<typename TestMatrix_t> EIGEN_DONT_INLINE void test(const char * msg, int m_size= test_size, int n= 10000) {
double s= 0.0;
clock_t elapsed= 0;
TestMatrix_t m3;
for(int i= 0; i<n; i++) {
TestMatrix_t m1 = TestMatrix_t::Random(m_size, m_size);
TestMatrix_t m2= TestMatrix_t::Random(m_size, m_size);
clock_t begin = clock();
m3.noalias()= m1*m2;
clock_t end = clock();
elapsed+= end - begin;
// make sure m3 is not optimized away
s+= m3(1, 1);
}
double elapsed_secs = double(elapsed) / CLOCKS_PER_SEC;
cout << "Elapsed time " << msg << ": " << elapsed_secs << " size " << m3.cols() << ", " << m3.rows() << endl;
}
int main() {
#ifdef EIGEN_VECTORIZE
cout << "EIGEN_VECTORIZE on " << endl;
#endif
test<TestMatrix_t> ("normal ");
test<TestMatrix_dyn16b_t> ("dyn 16b ");
test<TestMatrix_dynalloc_t>("dyn alloc");
test<TestMatrix_fix16b_t> ("fix 16b ", test_size_16b);
}
скомпилировал с помощью g++ -msse3 -O2 -DEIGEN_DONT_PARALLELIZE test.cpp
и запустил на Athlon II X2 255. Результат меня несколько удивил:
EIGEN_VECTORIZE on
Elapsed time normal : 0.019193 size 13, 13
Elapsed time dyn 16b : 0.025226 size 13, 13
Elapsed time dyn alloc: 0.018648 size 13, 13
Elapsed time fix 16b : 0.018221 size 14, 14
Аналогичные результаты достигаются с другими нечетными числами для test_size
. Что меня смущает, так это:
- Прочитав часто задаваемые вопросы по векторизации собственных векторов, я подумал, что матрица 13x13 имеет размер не кратен 16 байтам, поэтому SIMD-оптимизация не принесет пользы. Я ожидал, что время вычислений будет намного хуже, но это не так.
- Прочитав о необязательных параметрах шаблона, я подумал, что динамические матрицы с фиксированной верхней границей известны во время компиляции будет вести себя так же, как динамически размещаемые матрицы, и поэтому будет иметь аналогичную скорость вычислений. Но это не так. На самом деле это то, что меня больше всего удивляет и что вызвало мой первоначальный поиск: я хотел знать, лучше ли использовать динамическую матрицу с фиксированной верхней границей, которая кратна 16 байтам, чем матрицу фиксированного размера, размер которой не кратен 16 байт.
- Наконец, интересно, но не так уж удивительно: матрица, фиксированный размер которой кратен 16, не медленнее, чем матрица, длина столбца и строки которой на единицу меньше. SIMD просто делает дополнительный столбец и строку бесплатно.
- Не мой первоначальный вопрос, но также интересный: когда тест скомпилирован без поддержки SSE2 и, следовательно, без векторизации, относительное время вычислений примерно пропорционально. Матрица фиксированной памяти с динамическим размером снова самая медленная.
Короче говоря, почему Matrix<double, Dynamic, Dynamic, ColMajor, test_size_16b, test_size_16b>
намного медленнее? Вы можете подтвердить мои наблюдения и, может быть, даже объяснить их?