Resons for potentially bad performance of numpy?

Viewed 121

I have been running into performance issues with numpy. I tested different ways to compute a dot product, but similar performance issues have been seen with other numpy functions.

import timeit
import numpy as np
import tensorflow as tf

def np_dot(U, X):
    tic = timeit.default_timer()
    X_bar = np.dot(U.T, X)
    toc = timeit.default_timer()
    print("np.dot took {:.4f} s".format(toc-tic))
    return X_bar

def np_at(U, X):
    tic = timeit.default_timer()
    X_bar = U.T @ X
    toc = timeit.default_timer()
    print("np @ took {:.4f} s".format(toc-tic))
    return X_bar

def np_matmul(U, X):
    tic = timeit.default_timer()
    X_bar = np.matmul(U.T, X)
    toc = timeit.default_timer()
    print("np.matmul took {:.4f} s".format(toc-tic))
    return X_bar

def np_einsum(U, X):
    tic = timeit.default_timer()
    X_bar = np.einsum('ij,jk', U.T, X)
    toc = timeit.default_timer()
    print("np.einsum took {:.4f} s".format(toc-tic))
    return X_bar

def tf_matmul(U, X):
    tic = timeit.default_timer()
    X_bar = tf.matmul(U.T, X)
    toc = timeit.default_timer()
    print("tf.matmul took {:.4f} s".format(toc-tic))
    return X_bar


if __name__ == "__main__":
    print("is_gpu_available?", tf.test.is_gpu_available(), tf.test.is_built_with_cuda())
    print("numpy version:", np.__version__)
    print("tensorflow version:", tf.__version__)
    
    M = N = 1000
    X = np.random.rand(M, N)
    U = np.random.rand(M, N)
    X_bar = np_dot(U, X)
    X_bar0 = np_at(U, X)
    X_bar1 = np_matmul(U, X)
    X_bar2 = np_einsum(U, X)
    X_bar3 = tf_matmul(U, X)
    print(np.allclose(X_bar, X_bar0))
    print(np.allclose(X_bar, X_bar1))
    print(np.allclose(X_bar, X_bar2))
    print(np.allclose(X_bar, X_bar3))

The output on my laptop (with Windows 10 and python 3.8) is:

is_gpu_available? False False
numpy version: 1.19.2
tensorflow version: 2.2.0
np.dot took 12.5368 s
np @ took 14.3303 s
np.matmul took 14.0634 s
np.einsum took 0.5937 s
tf.matmul took 0.0263 s
True
True
True
True

meaning the standard @ is 545 times slower than than it could be! On a friends laptop the result look more like expected (with Ubuntu and python 3.6.9):

is_gpu_available? False False
numpy version: 1.19.5
tensorflow version: 1.14.0
np.dot took 0.0192 s
np @ took 0.0196 s
np.matmul took 0.0208 s
np.einsum took 0.3893 s
tf.matmul took 0.0280 s
True
True
True
True

Interestingly, numpys einsum does not seem to be affected by whatever is going wrong on my laptop and neither is tensorflow.

What is going on here? What are the potential reasons for such a huge performance difference?

edit:

Always make sure numpy is using some BLAS library. Read more about it here: Boosting numpy: Why BLAS Matters.

In my case numpy.show_config() returns the following:

blas_mkl_info:
  NOT AVAILABLE
blis_info:
  NOT AVAILABLE
openblas_info:
  NOT AVAILABLE
atlas_3_10_blas_threads_info:
  NOT AVAILABLE
atlas_3_10_blas_info:
  NOT AVAILABLE
atlas_blas_threads_info:
  NOT AVAILABLE
atlas_blas_info:
  NOT AVAILABLE
accelerate_info:
  NOT AVAILABLE
blas_info:
  NOT AVAILABLE
blas_src_info:
  NOT AVAILABLE
blas_opt_info:
  NOT AVAILABLE
lapack_mkl_info:
  NOT AVAILABLE
openblas_lapack_info:
  NOT AVAILABLE
openblas_clapack_info:
  NOT AVAILABLE
flame_info:
  NOT AVAILABLE
atlas_3_10_threads_info:
  NOT AVAILABLE
atlas_3_10_info:
  NOT AVAILABLE
atlas_threads_info:
  NOT AVAILABLE
atlas_info:
  NOT AVAILABLE
lapack_info:
  NOT AVAILABLE
lapack_src_info:
  NOT AVAILABLE
lapack_opt_info:
  NOT AVAILABLE
numpy_linalg_lapack_lite:
    language = c
    define_macros = [('HAVE_BLAS_ILP64', None), ('BLAS_SYMBOL_SUFFIX', '64_')]
0 Answers
Related