Notas para contenedor de docker:

Comando de docker para ejecución de la nota de forma local:

nota: cambiar <ruta a mi directorio> por la ruta de directorio que se desea mapear a /datos dentro del contenedor de docker.

docker run --rm -v <ruta a mi directorio>:/datos --name jupyterlab_openblas -p 8888:8888 -d palmoreck/jupyterlab_openblas:1.1.0

password para jupyterlab: qwerty

Detener el contenedor de docker:

docker stop jupyterlab_openblas

Al ejecutar el run anterior se descargará la imagen y posteriormente se instalará OpenBLAS y paquetes de Python adaptados a sus sistemas. Esto tardará $10-15$ minutos aproximadamente. Pueden revisar de vez en vez con el commando:

docker logs jupyterlab_openblas

hasta que salga un mensaje del tipo: Successfully built scipy...The Jupyter Notebook is running at... y poder acceder al puerto 8888 de sus máquinas

Nota: Lo anterior se realizó de esta forma (construir una imagen de docker con software simple y posteriormente instalar el software especializado) pues si hubiera construído la imagen de docker adaptada a mi máquina, es muy probable que se tendría que haber adaptado nuevamente a sus máquinas. Ver: docker images with architecture-optimisation

Documentación de la imagen de docker palmoreck/jupyterlab_openblas:1.1.0 en liga.


Implementaciones de la API standard de BLAS y LAPACK

En Handle different versions of BLAS and LAPACK se explica que BLAS: Basic Linear Algebra Subprograms y Linear Algebra Package: LAPACK además de ser implementaciones, también son API* standard para operaciones básicas del álgebra lineal. Muchas implementaciones de la API existen. Un ejemplo de implementaciones son las incluidas al instalar R o Python. Otras son las que se pueden instalar vía línea de comando:

sudo apt-get install -y libblas3 libblas-dev liblapack3 liblapack-dev

en un sistema operativo Ubuntu por ejemplo. Ver libblas3 libblas-dev liblapack3 liblapack-dev.

*Ver Application Programming Interface: API para una explicación de lo que es una API.

Sin embargo existen otras implementaciones de la API que están optimizadas para la arquitectura de nuestras máquinas, por ejemplo:

Revisaremos en esta nota algunas comparaciones en tiempo de ejecución de la API de BLAS y LAPACK que viene integrada en la instalación de numpy y scipy vs la que provee OpenBLAS.

OpenBLAS

En la documentación de OpenBLAS podemos revisar la liga supported-cpus-and-operating-systems para conocer procesadores y sistemas operativos soportados. En específico, la instalación que se realiza con la imagen de docker detallada al inicio de la nota utiliza Precompiled installation packages.

Las siguientes mediciones fueron calculadas con un procesador:


In [1]:
%%bash
lscpu


Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              8
On-line CPU(s) list: 0-7
Thread(s) per core:  2
Core(s) per socket:  4
Socket(s):           1
NUMA node(s):        1
Vendor ID:           GenuineIntel
CPU family:          6
Model:               60
Model name:          Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
Stepping:            3
CPU MHz:             3583.857
CPU max MHz:         4000.0000
CPU min MHz:         800.0000
BogoMIPS:            7195.53
Virtualization:      VT-x
L1d cache:           32K
L1i cache:           32K
L2 cache:            256K
L3 cache:            8192K
NUMA node0 CPU(s):   0-7
Flags:               fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts md_clear flush_l1d

Ejemplos de la implementación de BLAS y LAPACK vía pip3 install --user numpy scipy

Lo que continúa se ejecutó con la imagen de docker: palmoreck/jupyterlab_numerical:1.1.0 cuya documentación se encuentra: en liga y no trae instalado OpenBLAS.

Si instalamos numpy o scipy via pip3 install --user numpy scipy obtendremos una instalación que no está adecuada a mi máquina. Por ejemplo para numpy la configuración que trae tal implementación la podemos consultar con un np.show_config().

Multiplicación de matrices con numpy


In [1]:
import numpy as np
import scipy.sparse
from scipy.linalg import lu

In [ ]:
np.random.seed(2020)
m=10**4
r=10**4

A=np.random.rand(m,r)
fileA='A.txt'
np.savetxt(fileA,A)

In [ ]:
np.random.seed(2021)
r=10**4
n=10**4

B=np.random.rand(r,n)
fileB='B.txt'
np.savetxt(fileB,B)

In [2]:
fileA='A.txt'
fileB='B.txt'

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [3]:
A = np.loadtxt(fileA)
B = np.loadtxt(fileB)

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [4]:
%timeit -n 1 -r 2 A@B


20.5 s ± 9.91 ms per loop (mean ± std. dev. of 2 runs, 1 loop each)

In [5]:
np.show_config()


blas_mkl_info:
  NOT AVAILABLE
blis_info:
  NOT AVAILABLE
openblas_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/local/lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
blas_opt_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/local/lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
lapack_mkl_info:
  NOT AVAILABLE
openblas_lapack_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/local/lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
lapack_opt_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/local/lib']
    language = c
    define_macros = [('HAVE_CBLAS', None)]

In [6]:
%%file mult_matrix_matrix_numpy_no_openblas.py
import numpy as np
m=10**4
r=10**4
n=10**4

fileA='A.txt'
fileB='B.txt'
A = np.loadtxt(fileA)
B = np.loadtxt(fileB)
C = A@B


Writing mult_matrix_matrix_numpy_no_openblas.py

In [7]:
%%bash
sudo perf stat -S -a --per-core -e cycles,instructions,cache-references,cache-misses -r 2 python3 mult_matrix_matrix_numpy_no_openblas.py


 Performance counter stats for 'system wide' (2 runs):

S0-C0           2        94197037438      cycles                                                      
S0-C0           2       120879480875      instructions              #    1.28  insn per cycle         
S0-C0           2          892617347      cache-references                                            
S0-C0           2          258382917      cache-misses              #   28.947 % of all cache refs    
S0-C1           2        93840578242      cycles                                                      
S0-C1           2       121937318809      instructions              #    1.30  insn per cycle         
S0-C1           2          802838566      cache-references                                            
S0-C1           2          203576303      cache-misses              #   25.357 % of all cache refs    
S0-C2           2        95083397010      cycles                                                      
S0-C2           2       121535834363      instructions              #    1.28  insn per cycle         
S0-C2           2          894352079      cache-references                                            
S0-C2           2          271679533      cache-misses              #   30.377 % of all cache refs    
S0-C3           2       573435295924      cycles                                                      
S0-C3           2      1338220158294      instructions              #    2.33  insn per cycle         
S0-C3           2         1891519282      cache-references                                            
S0-C3           2          388577928      cache-misses              #   20.543 % of all cache refs    

     134.723100399 seconds time elapsed                                          ( +-  1.10% )

Factorización LU con scipy

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [4]:
%timeit -n 2 -r 2 lu(A)


5.81 s ± 1.16 ms per loop (mean ± std. dev. of 2 runs, 2 loops each)

Multiplicación matriz sparse o rala con vector usando scipy

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [ ]:
m=10**4
n=10**4
matrix = scipy.sparse.rand(m,n, density=0.6, format="csr", random_state=2020)

In [4]:
np.random.seed(2020)
x = np.random.rand(n)

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [5]:
%timeit -n 5 -r 10 matrix.dot(x)


54.7 ms ± 431 µs per loop (mean ± std. dev. of 10 runs, 5 loops each)

Ejemplos de la implementación de BLAS y LAPACK vía OpenBLAS

Lo que continúa se ejecutó con la imagen de docker: palmoreck/jupyterlab_openblas:1.1.0 (detallada al inicio de la nota) cuya documentación se encuentra: en liga y sí trae instalado OpenBLAS.

Multiplicación de matrices con numpy


In [1]:
import numpy as np
import scipy.sparse
from scipy.linalg import lu

In [ ]:
np.random.seed(2020)
m=10**4
r=10**4

A=np.random.rand(m,r)
fileA='A.txt'
np.savetxt(fileA,A)

In [ ]:
np.random.seed(2021)
r=10**4
n=10**4

B=np.random.rand(r,n)
fileB='B.txt'
np.savetxt(fileB,B)

In [2]:
fileA='A.txt'
fileB='B.txt'

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [3]:
A = np.loadtxt(fileA)
B = np.loadtxt(fileB)

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [4]:
%timeit -n 1 -r 2 A@B


15.4 s ± 15.4 ms per loop (mean ± std. dev. of 2 runs, 1 loop each)

In [3]:
np.show_config()


blas_mkl_info:
  NOT AVAILABLE
blis_info:
  NOT AVAILABLE
openblas_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/lib/x86_64-linux-gnu']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
blas_opt_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/lib/x86_64-linux-gnu']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
lapack_mkl_info:
  NOT AVAILABLE
openblas_lapack_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/lib/x86_64-linux-gnu']
    language = c
    define_macros = [('HAVE_CBLAS', None)]
lapack_opt_info:
    libraries = ['openblas', 'openblas']
    library_dirs = ['/usr/lib/x86_64-linux-gnu']
    language = c
    define_macros = [('HAVE_CBLAS', None)]

In [4]:
%%file mult_matrix_matrix_numpy_openblas.py
import numpy as np
m=10**4
r=10**4
n=10**4

fileA='A.txt'
fileB='B.txt'
A = np.loadtxt(fileA)
B = np.loadtxt(fileB)
C = A@B


Writing mult_matrix_matrix_numpy_openblas.py

In [1]:
%%bash
sudo perf stat -S -a --per-core -e cycles,instructions,cache-references,cache-misses -r 2 python3 mult_matrix_matrix_numpy_openblas.py


 Performance counter stats for 'system wide' (2 runs):

S0-C0           2       557798152924      cycles                                                      
S0-C0           2      1316323671570      instructions              #    2.36  insn per cycle         
S0-C0           2         1750182833      cache-references                                            
S0-C0           2          315759345      cache-misses              #   18.042 % of all cache refs    
S0-C1           2        85729339699      cycles                                                      
S0-C1           2       121421167895      instructions              #    1.42  insn per cycle         
S0-C1           2          604647185      cache-references                                            
S0-C1           2          141773011      cache-misses              #   23.447 % of all cache refs    
S0-C2           2        93151016869      cycles                                                      
S0-C2           2       122733051324      instructions              #    1.32  insn per cycle         
S0-C2           2          634325981      cache-references                                            
S0-C2           2          151379231      cache-misses              #   23.865 % of all cache refs    
S0-C3           2        87907291840      cycles                                                      
S0-C3           2       123020783578      instructions              #    1.40  insn per cycle         
S0-C3           2          640293423      cache-references                                            
S0-C3           2          154680878      cache-misses              #   24.158 % of all cache refs    

     130.089082424 seconds time elapsed                                          ( +-  0.04% )

Factorización LU con scipy

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [4]:
%timeit -n 2 -r 2 lu(A)


5.47 s ± 3.38 ms per loop (mean ± std. dev. of 2 runs, 2 loops each)

Multiplicación matriz sparse o rala con vector usando scipy

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [2]:
m=10**4
n=10**4
matrix = scipy.sparse.rand(m,n, density=0.6, format="csr", random_state=2020)

In [3]:
np.random.seed(2020)
x = np.random.rand(n)

Mientras se ejecuta la siguiente celda se sugiere en la terminal ejecutar en la línea de comando htop


In [4]:
%timeit -n 5 -r 10 matrix.dot(x)


47.1 ms ± 433 µs per loop (mean ± std. dev. of 10 runs, 5 loops each)

Resultados

  • Para la multiplicación de matrices con numpy se observa una reducción de tiempo siendo la implementación vía OpenBLAS más rápida que la que viene integrada en la instalación de numpy.

  • Para la factorización LU o la multiplicación de una matriz sparse o rala con un vector no se observan diferencias vía implementación OpenBLAS que la integrada con scipy.

Referencias:

Para referencias sobre el uso de BLAS y LAPACK con C ver:

Hay implementaciones en paralelo de BLAS para sistemas de memoria distribuida. Ver por ejemplo:

También NVIDIA tiene su propia implementación de BLAS para uso con GPU's: CUBLAS y su implementación de LAPACK: CUSOLVER. Para más sobre CUBLAS y CUSOLVER ver: C/extensiones_a_C/CUDA/CUBLAS y C/extensiones_a_C/CUDA/CUSOLVER/

Otras referencias para uso de GPU's con implementaciones de BLAS y LAPACK se encuentran:

Para otra implementación de BLAS y LAPACK ver: