Instalación de R en Ubuntu para HPC (High Performance Computing)

Work in progess… (sugerencias y comentarios serán bien recibidos).

La idea es preparar R para HPSC (High Performance Statistical Computing) de forma transparente para el usuario y posteriormente ver herramientas adicionales (paquetes y funciones) para procesamiento en paralelo (con CPUs y GPUs). Algunos paquetes se pueden configurar de forma transparente (Matrix, TensorFlow, …) pero otras herramientas requieren adaptar la programación.

El procedimiento habitual sería compilar las librerías de álgebra lineal (BLAS y LAPACK) y posteriormente R desde el código fuente tratando de conseguir la máxima optimización. Sin embargo compilar R de esta forma puede presentar problemas (ver R Installation and Administration) y depende de la instalación previa de BLAS/LAPACK. En la última sección se muestran algunas referencias para esta aproximación (se podrían instalar distintas versiones en distintos directorios).

Un enfoque más simple consiste en instalar R desde CRAN (siguiendo los pasos descritos en este post), que por defecto utilizará una implementación BLAS/LAPACK estable y compatible con distintas plataformas pero que no está optimizadas para el rendimiento, y posteriormente reemplazar las librerías estándar de referencia (típicamente enlazando con las proporcionadas por OpenBLAS, ATLAS o Intel MKL), como se describe en las secciones siguientes.

Otra alternativa sería instalar Microsoft R Open con MKL, aunque Microsoft retiró R Open el 1 de julio de 2022 y la última versión disponible es la 4.0.2 (Microsoft R Application Network y CRAN Time Machine se retirarán el 1 de julio de 2023, ya que SQL Server y Azure SQL pasaron a emplear la versión oficial de R).

Antes de nada, puede ser recomendable crear un directorio donde descargar archivos (si se instala RStudio, Intel MKL o se compila R desde el código fuente):

mkdir installr
cd installr

Fuentes:

Instalación de R

Como ya se comentó, el primer paso sería la instalación de R siguiendo los pasos descritos en este post.

Configurar R para HPC en Ubuntu/Debian

Después de instalar R el siguiente paso es reemplazar las librerías estándar BLAS/LAPACK por las proporcionadas por OpenBLAS, ATLAS o Intel MKL.

Posteriormente se podrá seleccionar que librerías de desea emplear, como se muestra más adelante.

Configurar R para emplear OpenBLAS o ATLAS

Instalar OpenBLAS o ATLAS

Instalar OpenBLAS:

sudo apt-get install libopenblas-base

Instalar ATLAS (Automatically Tuned Linear Algebra Software):

sudo apt-get install libatlas3-base liblapack3

Seleccionar el número de núcleos/hilos

Por defecto OpenBLAS (y OpenMP) utilizan todos los núcleos disponibles. Nos puede interesar establecer el número de núcleos/threads que empleará OpenBLAS a través de la variable de entorno OPENBLAS_NUM_THREADSo a través de OMP_NUM_THREADS (el orden de prioridades es OPENBLAS_NUM_THREADS > GOTO_NUM_THREADS > OMP_NUM_THREADS), e.g.:

echo "OMP_NUM_THREADS=8" | sudo tee -a /etc/environment

El código anterior no tendrá efecto hasta que se reinicie el sistema o se vuelva a iniciar sesión (temporalmente se podría ejecutar export OMP_NUM_THREADS=8). En R se puede emplear las funciones Sys.getenv('OMP_NUM_THREADS') y Sys.setenv(OMP_NUM_THREADS = 8).

El último paso sería enlazar estas librerías como se describe en esta sección.

Configurar R para emplear Intel Math Kernel Library (MKL)

Seguiremos los pasos descritos en Adding Intel MKL easily via a simple script, actualizados a la última versión y de forma interactiva.

Instalación de Intel MKL

Revisar las versiones disponibles en el repositorio: Installing Intel Performance Libraries Using APT Repository. Instalaremos únicamente la última versión de 64bits de MKL (e.g. intel-mkl-64bit-2020.0-088), por lo que añadiremos solo ese repositorio. Antes hay que descargar y añadir la llave GnuPG.

wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB
sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB
sudo sh -c 'echo deb https://apt.repos.intel.com/mkl all main > /etc/apt/sources.list.d/intel-mkl.list'
sudo apt update
sudo apt install intel-mkl-64bit-2020.0-088

El enlace anterior puede no estar actualizado, se pueden listar los paquetes disponibles con:

apt-cache search intel-mkl-64bit

Integrate MKL

El siguiente código de Installing Intel Performance Libraries Using APT Repository, enlaza las librerías MKL mediante código (estableciendo una prioridad alta, 150), pero puede ser recomendable saltarse este paso (no los siguientes) y hacerlo manualmente como se describe en esta sección.

One the key advantages of a Debian or Ubuntu system is the overall integration providing a raft of useful features. One of these is the seamless and automatic selection of alternatives. By declaring a particular set of BLAS and LAPACK libraries the default, all application linked against this interface will use the default.
Better still, users can switch between these as well. So here we can make the MKL default for BLAS and LAPACK:

## update alternatives
sudo update-alternatives --install /usr/lib/x86_64-linux-gnu/libblas.so     libblas.so-x86_64-linux-gnu      /opt/intel/mkl/lib/intel64/libmkl_rt.so 150
sudo update-alternatives --install /usr/lib/x86_64-linux-gnu/libblas.so.3   libblas.so.3-x86_64-linux-gnu    /opt/intel/mkl/lib/intel64/libmkl_rt.so 150
sudo update-alternatives --install /usr/lib/x86_64-linux-gnu/liblapack.so   liblapack.so-x86_64-linux-gnu    /opt/intel/mkl/lib/intel64/libmkl_rt.so 150
sudo update-alternatives --install /usr/lib/x86_64-linux-gnu/liblapack.so.3 liblapack.so.3-x86_64-linux-gnu  /opt/intel/mkl/lib/intel64/libmkl_rt.so 150

Next, we have to tell the dyanmic linker about two directories use by the MKL, and have it update its cache:

echo "/opt/intel/lib/intel64" | sudo tee /etc/ld.so.conf.d/mkl.conf
echo "/opt/intel/mkl/lib/intel64" | sudo tee -a /etc/ld.so.conf.d/mkl.conf
sudo ldconfig

As discussed in issue ticket #2, mixing Intel OpenMP and GNU OpenMP run-times in one application can lead to issues. Since most of the open-source performance libraries use GNU OpenMP it is safer to make the MKL library also use GNU OpenMP as well by setting MKL_THREADING_LAYER=GNU in either /etc/environment or your local per-user settings. Here we use the file in /etc:

echo "MKL_THREADING_LAYER=GNU" | sudo tee -a /etc/environment

NOTA: El código anterior no tendrá efecto hasta que se reinicie el sistema o se vuelva a iniciar sesión (temporalmente se podría ejecutar export MKL_THREADING_LAYER=GNU).

Thanks to Evarist Fomenko from Intel’s Novosibirsk office for help with this point.

Use the MKL

Now the MKL is ‘known’ and the default. If we start R, its sessionInfo() shows the MKL:

Matrix products: default                            
BLAS/LAPACK: /opt/intel/compilers_and_libraries_2020.0.166/linux/mkl/lib/intel64_lin/libmkl_rt.so

Desinstalar o desenlazar MKL

Desenlazar (como ya se comentó, puede ser recomendable hacerlo manualmente como se describe aquí):

sudo update-alternatives --remove libblas.so-x86_64-linux-gnu /opt/intel/mkl/lib/intel64/libmkl_rt.so 
sudo update-alternatives --remove libblas.so.3-x86_64-linux-gnu  /opt/intel/mkl/lib/intel64/libmkl_rt.so 
sudo update-alternatives --remove liblapack.so-x86_64-linux-gnu  /opt/intel/mkl/lib/intel64/libmkl_rt.so 
sudo update-alternatives --remove liblapack.so.3-x86_64-linux-gnu  /opt/intel/mkl/lib/intel64/libmkl_rt.so 

Desinstalar:

sudo apt-get autoremove intel-mkl-64bit-2020.0-088

Problemas con MKL

Al ejecutar paquetes que se compilan con LAPACK (npsp) aparecieron algunos problemas con los resultados… Pendiente volver a testear…

Seleccionar librerías BLAS y LAPACK

R por defecto emplea las librerías dinámicas por lo que solo sería necesario enlazar libblas.so.3 y liblapack.so.3 (libblas.so.3-x86_64-linux-gnu y liblapack.so.3-x86_64-linux-gnu en la última version de Ubuntu).

Enlazar BLAS:

sudo update-alternatives --config libblas.so.3-x86_64-linux-gnu
Existen 4 opciones para la alternativa libblas.so.3-x86_64-linux-gnu (que provee /usr/lib/x86_64-linux-gnu/libblas.so.3).

  Selección   Ruta                                             Prioridad  Estado
------------------------------------------------------------
* 0            /opt/intel/mkl/lib/intel64/libmkl_rt.so           150       modo automático
  1            /opt/intel/mkl/lib/intel64/libmkl_rt.so           150       modo manual
  2            /usr/lib/x86_64-linux-gnu/atlas/libblas.so.3      35        modo manual
  3            /usr/lib/x86_64-linux-gnu/blas/libblas.so.3       10        modo manual
  4            /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3   40        modo manual

Pulse <Intro> para mantener el valor por omisión [*] o pulse un número de selección: 4

Enlazar LAPACK (si se emplea ATLAS o MKL):

sudo update-alternatives --config liblapack.so.3-x86_64-linux-gnu 
Existen 4 opciones para la alternativa liblapack.so.3-x86_64-linux-gnu (que provee /usr/lib/x86_64-linux-gnu/liblapack.so.3).

  Selección   Ruta                                               Prioridad  Estado
------------------------------------------------------------
* 0            /opt/intel/mkl/lib/intel64/libmkl_rt.so             150       modo automático
  1            /opt/intel/mkl/lib/intel64/libmkl_rt.so             150       modo manual
  2            /usr/lib/x86_64-linux-gnu/atlas/liblapack.so.3      35        modo manual
  3            /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3     10        modo manual
  4            /usr/lib/x86_64-linux-gnu/openblas/liblapack.so.3   40        modo manual

Pulse <Intro> para mantener el valor por omisión [*] o pulse un número de selección: 4

Para confirmar la selección, podemos ejecutar sessionInfo() en R y nos mostrará algo de este estilo:

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.2.20.so
Matrix products: default                            
BLAS/LAPACK: /opt/intel/compilers_and_libraries_2020.0.166/linux/mkl/lib/intel64_lin/libmkl_rt.so

Si se emplean paquetes que se compilan con LAPACK (e.g. npsp) habría que reinstalarlos si se cambia el enlace a las librerías BLAS/LAPACK. Pendiente buscar referencia…

Se podrían seleccionar el número de núcleos/hilos como se describió aquí.

Instalación de RStudio Server

Descargar e instalar RStudio Server siguiendo los pasos en: Download RStudio Server for Debian & Ubuntu

sudo apt-get install gdebi-core
wget https://download2.rstudio.org/server/bionic/amd64/rstudio-server-1.4.1103-amd64.deb
sudo gdebi rstudio-server-1.4.1103-amd64.deb

Se recomienda crear un usuario:

sudo adduser rstudio

Para acceder (se solicitarán las credenciales):

http://<server-ip>:8787

Si aparecen problemas:

sudo rstudio-server verify-installation

Fuentes:

Problemas con RStudio Server

Desde RStudio solo se pudo ejecutar R-benchmark-25.R sin problemas con OpenBLAS (aunque al emplear la consola por SSH no hubo ningún problema con ninguna de las opciones).

ERROR: Al seleccionar MKL y testear con R-benchmark-25.R:

Error in solve(crossprod(a), crossprod(a, b)) : 
  the leading minor of order 1608 is not positive definite

Este tipo de errores pueden ser debidos a Open MP: https://stat.ethz.ch/pipermail/r-sig-debian/2011-July/001638.html pese a haber incluido MKL_THREADING_LAYER=GNU en /etc/environment para que MKL utilice la misma versión de R.

Este problema no apareció en la consola, pero pudo ser debido a que se inició una nueva conexión y se cargaron las variables de entorno? Pendiente volver a testear…

ERROR: Al seleccionar ATLAS y testear con R-benchmark-25.R, se queda colgado en:

Inverse of a 1600x1600 random matrix________________ (sec):

Introducción al procesamiento en paralelo en R

En esta sección se pretenden mostrar las principales herramientas para el procesamiento en paralelo disponibles en R y dar una idea de su funcionamiento. Para más detalles se recomienda ver CRAN Task View: High-Performance and Parallel Computing with R (además de HPC, High Performance Computing, también incluye herramientas para computación distribuida)1.

Emplearemos la siguiente terminología:

  • Núcleo: término empleado para referirse a un procesador lógico de un equipo (un equipo puede tener un único procesador con múltiples núcleos que pueden realizar operaciones en paralelo). También podría referirse aquí a un equipo (nodo) de una red (clúster de equipos; en la práctica cada uno puede tener múltiples núcleos). Un núcleo puede ejecutar procesos en serie.

  • Clúster: colección de núcleos en un equipo o red de equipos. Un clúster puede ejecutar varios procesos en paralelo.

Por defecto la versión oficial de R emplea un único núcleo, aunque se puede configurar de forma que realice cálculos en paralelo (e.g. librería LAPACK).

Los métodos simples de paralelización2 disponibles en R son:

  • Forking: Copia el proceso de R a un nuevo núcleo (se comparte el entorno de trabajo). Es el más simple y eficiente pero no está disponible en Windows.

  • Socket: Lanza una nueva versión de R en cada núcleo, como si se tratase de un cluster de equipos comunicados a traves de red (hay que crear un entorno de trabajo en cada núcleo). Disponible en todos los sistemas operativos.

Paquetes de R

Hay varios paquetes que se pueden usar para el procesamiento paralelo en R, entre ellos podríamos destacar:

  • parallel: forma parte de la instalación base de R y fusiona los paquetes multicore (forking) y snow (sockets; Simple Network of Workstations). Además incluye herramientas para la generación de números aleatorios en paralelo (cada proceso empleara una secuencia y los resultados serán reproducibles).

    Incluye versiones “paralelizadas” de la familia *apply(): mclapply() (forking), parLapply() (socket), …

  • foreach: permite realizar iteraciones y admite paralelización con el operador %dopar%, aunque requiere paquetes adicionales como doSNOW o doParallel (recomendado).

  • rslurm: permite la ejecución distribuida en clústeres Linux que implementen SLURM (Simple Linux Utility for Resource Management), un gestor de recursos de código abierto muy empleado.

Ejemplos

Si se emplea el paquete parallel en sistemas tipo Unix (Linux, Mac OS X, …), se podría evaluar en paralelo una función llamando directamente a mclapply(). Por defecto empleará todos los núcleos disponibles, pero se puede especificar un número menor mediante el argumento mc.cores.

library(parallel)
ncores <- detectCores()
ncores

func <- function(k) {
  i_boot <- sample(nrow(iris), replace = TRUE)
  lm(Petal.Width ~ Petal.Length, data = iris[i_boot, ])$coefficients
}

RNGkind("L'Ecuyer-CMRG") # Establecemos Pierre L'Ecuyer's RngStreams...
set.seed(1)

system.time(res.boot <- mclapply(1:100, func)) # En Windows llama a lapply() (mc.cores = 1)
# res.boot <- mclapply(1:100, func, mc.cores = ncores - 1) # En Windows genera un error si mc.cores > 1

En Windows habría que crear previamente un cluster, llamar a una de las funciones par*apply() y finalizar el cluster:

cl <- makeCluster(ncores - 1, type = "PSOCK")
clusterSetRNGStream(cl, 1) # Establecemos Pierre L'Ecuyer's RngStreams con semilla 1...

system.time(res.boot <- parSapply(cl, 1:100, func))

# stopCluster(cl)

str(res.boot)

Esto también se puede realizar en Linux (type = "FORK"), aunque podríamos estar trabajando ya en un cluster de equipos…

También podríamos emplear balance de carga si el tiempo de computación es variable (e.g. parLapplyLB() o clusterApplyLB()) pero no sería recomendable si se emplean números pseudo-aleatorios (los resultados no serían reproducibles).

Además, empleando las herramientas del paquete snow se puede representar el uso del cluster (experimental en Windows):

# library(snow)
ctime <- snow::snow.time(snow::parSapply(cl, 1:100, func))
ctime
plot(ctime)

Hay que tener en cuenta la sobrecarga adicional debida a la comunicación entre nodos al paralelizar (especialmente con el enfoque de socket).

La función boot::boot() incluye parámetros para el procesamiento en paralelo: parallel = c("no", "multicore", "snow"), ncpus, cl. Si parallel = "snow" se crea un clúster en la máquina local durante la ejecución, salvo que se establezca con el parámetro cl.

Procesamiento en paralelo en R con GPUs

Esta sección está incompleta, en un primer intento no conseguí configurar R (era necesario que el administrador del sistema3 modificase la instalación), no tuve tiempo a volver a intentarlo. Incluyo las notas por si resultan de utilidad (avisadme si conseguís finalizar la configuración…).

La idea era emplear en R la librería NVBLAS disponible en equipos con GPUs de NVIDIA (no es necesario que sea un servidor con GPUs avanzadas) y configurar los paquetes de R que admiten procesamiento con GPUs.

Para verificar que está instalado el driver de NVIDIA, ejecutar:

nvidia-smi

y se obtendrá algo similar a esto:

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.30       Driver Version: 430.30       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GRID P40-12Q        On   | 00000000:02:01.0 Off |                    0 |
| N/A   N/A    P8    N/A /  N/A |    784MiB / 11454MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

Adicionalmente se mostrará un cuadro con el uso de GPU.

Configurar R para emplear NVBLAS

The NVBLAS Library is a GPU-accelerated Libary that implements BLAS on top of the cuBLAS Library (using only the CUBLASXT API). Because NVBLAS does not support all standard BLAS routines, it might be necessary to associate it with an existing full BLAS Library.

Ubuntu/Debian installation of cuBLAS:

sudo apt-get install cublas

ERROR: No se ha podido localizar el paquete cublas

Install a full BLAS Library: OpenBLAS? MKL?

Add to you path?

export LD_LIBRARY_PATH=PATH_TO_CUBLAS/lib64:PATH_TO_SYSTEM_BLAS

Configuration:

NVBLAS must be configured through an ASCII text file, parsed at the time of the loading of the library. The location and name of the configuration file must be defined by the environment variable NVBLAS_CONFIG_FILE (if it is not defined, it is assumed to be nvblas.conf in the current directory). The configuration file should have restricted write permissions.

The example below shows a typical NVBLAS configuration file:

# This is the configuration file to use NVBLAS Library
# Setup the environment variable NVBLAS_CONFIG_FILE to specify your own config file.
# By default, if NVBLAS_CONFIG_FILE is not defined, 
# NVBLAS Library will try to open the file "nvblas.conf" in its current directory
# Example : NVBLAS_CONFIG_FILE  /home/cuda_user/my_nvblas.conf
# The config file should have restricted write permissions accesses

# Specify which output log file (default is stderr)
NVBLAS_LOGFILE  nvblas.log

# Enable trace log of every intercepted BLAS calls
NVBLAS_TRACE_LOG_ENABLED

#Put here the CPU BLAS fallback Library of your choice
#It is strongly advised to use full path to describe the location of the CPU Library
NVBLAS_CPU_BLAS_LIB  /usr/lib/libopenblas.so
#NVBLAS_CPU_BLAS_LIB  <mkl_path_installtion>/libmkl_rt.so

# List of GPU devices Id to participate to the computation 
# Use ALL if you want all your GPUs to contribute
# Use ALL0, if you want all your GPUs of the same type as device 0 to contribute
# However, NVBLAS consider that all GPU have the same performance and PCI bandwidth
# By default if no GPU are listed, only device 0 will be used

#NVBLAS_GPU_LIST 0 2 4
#NVBLAS_GPU_LIST ALL
NVBLAS_GPU_LIST ALL0

# Tile Dimension
NVBLAS_TILE_DIM 2048

# Autopin Memory
NVBLAS_AUTOPIN_MEM_ENABLED

#List of BLAS routines that are prevented from running on GPU (use for debugging purpose
# The current list of BLAS routines supported by NVBLAS are
# GEMM, SYRK, HERK, TRSM, TRMM, SYMM, HEMM, SYR2K, HER2K

#NVBLAS_GPU_DISABLED_SGEMM 
#NVBLAS_GPU_DISABLED_DGEMM 
#NVBLAS_GPU_DISABLED_CGEMM 
#NVBLAS_GPU_DISABLED_ZGEMM 

# Computation can be optionally hybridized between CPU and GPU
# By default, GPU-supported BLAS routines are ran fully on GPU
# The option NVBLAS_CPU_RATIO_<BLAS_ROUTINE> give the ratio [0,1] 
# of the amount of computation that should be done on CPU
# CAUTION : this option should be used wisely because it can actually
# significantly reduced the overall performance if too much work is given to CPU

#NVBLAS_CPU_RATIO_CGEMM 0.07
echo "NVBLAS_LOGFILE nvblas.log
NVBLAS_CPU_BLAS_LIB /usr/lib64/libopenblas.so
NVBLAS_GPU_LIST ALL" > /etc/nvblas.conf
nano /etc/nvblas.conf

Usage:

Unfortunately, it seems that R does not offer a way to specify multiple BLAS Libraries.

On Linux, an alternative way to use NVBLAS Library is to use the LD_PRELOAD environment variable; this technique has the advantage of avoiding the relinkage step. However, the user should avoid defining that environment variable globally because it will cause the NVBLAS library to be loaded by every shell command executed on the system, thus leading to a lack of responsiveness of the system.

export LD_PRELOAD=libnvblas.so

You can now run R on the command line with GPU-accelerated BLAS like this:

LD_PRELOAD=/usr/local/cuda-9.0/lib64/libnvblas.so NVBLAS_CONFIG_FILE=/etc/nvblas.conf R

Accelerated R with CUDA on Linux

Fuentes:


  1. También puede ser de interés la presentación R y HPC (uso de R en el CESGA) de Aurelio Rodríguez en las VI Xornada de Usuarios de R en Galicia.↩︎

  2. Realmente las herramientas estándar son OpenMP para el procesamiento en paralelo con memoria compartida en un único equipo y MPI para la computación distribuida en múltiples nodos.↩︎

  3. En nuestro caso se emplearon máquinas virtuales (que nos proporciona el CITIC, https://citic.udc.es) en equipos con NVIDIA Tesla P40 24GB configuradas para trabajar con NVIDIA Grid.↩︎

comments powered by Disqus