C ++ Eigen Sparse Matrix multiplica mucho más lento que python scipy.sparse

Edición: la gran diferencia en el rendimiento se debe a un error en la prueba, cuando se configura correctamente, Eigen es de 2 a 3 veces más rápido.

Noté que la multiplicación de matrices dispersas usando la biblioteca C++ Eigen es mucho más lenta que usar la biblioteca Python scipy.sparse . scipy.sparse en scipy.sparse en ~0.03 segundos lo que logro en Eigen en ~25 segundos. Tal vez estoy haciendo algo mal en Eigen?

Aquí el código de Python:

 from scipy import sparse from time import time import random as rn N_VALUES = 200000 N_ROWS = 400000 N_COLS = 400000 rows_a = rn.sample(range(N_COLS), N_VALUES) cols_a = rn.sample(range(N_ROWS), N_VALUES) values_a = [rn.uniform(0,1) for _ in xrange(N_VALUES)] rows_b = rn.sample(range(N_COLS), N_VALUES) cols_b = rn.sample(range(N_ROWS), N_VALUES) values_b = [rn.uniform(0,1) for _ in xrange(N_VALUES)] big_a = sparse.coo_matrix((values_a, (cols_a, rows_a)), shape=(N_ROWS, N_COLS)) big_b = sparse.coo_matrix((values_b, (cols_b, rows_b)), shape=(N_ROWS, N_COLS)) big_a = big_a.tocsr() big_b = big_a.tocsr() start = time() AB = big_a * big_b; end = time() print 'time taken : {}'.format(end - start) 

Código C ++:

 #include  #include  #include  #include  #include  #include  using namespace Eigen; std::vector gen_random_sample(long min, long max, long sample_size); double get_random_double(double min, double max); std::vector get_vector_of_rn_doubles(int length, double min, double max); int main() { long N_COLS = 400000; long N_ROWS = 400000; long N_VALUES = 200000; SparseMatrix big_A(N_ROWS, N_COLS); std::vector cols_a = gen_random_sample(0, N_COLS, N_VALUES); std::vector rows_a = gen_random_sample(0, N_COLS, N_VALUES); std::vector values_a = get_vector_of_rn_doubles(N_VALUES, 0, 1); for (int i = 0; i < N_VALUES; i++) big_A.insert(cols_a[i], cols_a[i]) = values_a[i]; // big_A.makeCompressed(); // slows things down SparseMatrix big_B(N_ROWS, N_COLS); std::vector cols_b = gen_random_sample(0, N_COLS, N_VALUES); std::vector rows_b = gen_random_sample(0, N_COLS, N_VALUES); std::vector values_b = get_vector_of_rn_doubles(N_VALUES, 0, 1); for (int i = 0; i < N_VALUES; i++) big_B.insert(cols_b[i], cols_b[i]) = values_b[i]; // big_B.makeCompressed(); SparseMatrix big_AB(N_ROWS, N_COLS); clock_t begin = clock(); big_AB = (big_A * big_B); //.pruned(); clock_t end = clock(); double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC; std::cout << "Time taken : " << elapsed_secs << std::endl; } std::vector gen_random_sample(long min, long max, long sample_size) { std::vector my_vector(sample_size); // THE BUG, is right std::vector my_vector for (long i = min; i != max; i++) { my_vector.push_back(i); } std::random_shuffle(my_vector.begin(), my_vector.end()); std::vector new_vec = std::vector(my_vector.begin(), my_vector.begin() + sample_size); return new_vec; } double get_random_double(double min, double max) { std::uniform_real_distribution unif(min, max); std::default_random_engine re; double a_random_double = unif(re); } std::vector get_vector_of_rn_doubles(int length, double min, double max) { std::vector my_vector(length); for (int i=0; i < length; i++) { my_vector[i] = get_random_double(min, max); } return my_vector; } 

g++ -std=c++11 -I/usr/include/eigen3 time_eigen.cpp -o my_exec -O2 -DNDEBUG con: g++ -std=c++11 -I/usr/include/eigen3 time_eigen.cpp -o my_exec -O2 -DNDEBUG .

¿Me estoy perdiendo una manera de hacer una multiplicación dispersa rápidamente usando Eigen?

Si comstack sin -DNDEBUG , verá que sus matrices están dañadas porque está insertando los mismos elementos varias veces y el método de inserción no lo permite.

Reemplácelos con coeffRef(i,j) += value o use una lista de tripletes como se recomienda en la documentación. Después de esta pequeña corrección, toma 0.012s para el código C ++ y 0.021s con Python en mi computadora. Tenga en cuenta que realmente no puede deducir cuál es el más rápido de estos dos números ya que las matrices de entrada no son exactamente iguales, pero al menos están en el mismo orden.

Compara los tiempos necesarios para volcar los resultados. Si Python está realizando una evaluación perezosa (es decir, calcula sobre la marcha cuando se accede al resultado (puede ser poco sensible)) obtendrá la diferencia de tiempo.