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.
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.
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
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()
.
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
In [5]:
np.show_config()
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
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
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)
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)
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.
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
In [3]:
np.show_config()
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
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
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)
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)
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:
MAGMA, MAGMA en NVIDIA, ver por ejemplo: Matrix computations on the GPU
Para otra implementación de BLAS y LAPACK ver: