Al borde de un ataque de Matlab… sálvame C++

Francisco Javier García Blas [email protected] Universidad Carlos III de Madrid Grupo ARCOS 2016 2

Matlab ’’ 3

Introducción

§ ¿Qué es Matlab?, MATrix LABoratory

§ Es un lenguaje de programación (inicialmente escrito en C) para realizar cálculos numéricos con vectores y matrices. Como caso particular puede también trabajar con números escalares, tanto reales como complejos.

§ Cuenta con paquetes de funciones especializadas. 4

Mito 1: Matlab es difícil 5

Google Trend

§ Gran comunidad de usuarios. § Usado en múltiples áreas de la ingeniería. 6

Mito 1: Matlab es difícil 7

Mito 2: Matlab es lento 8

Matlab es lento

§ Matlab utiliza Intel Matk Kernel Library (MKL)

§ Intel MKL incluye una nueva función de Descarga Automática (AO). § Multiprocesadores § Intel Xeon Phi de forma automática y transparente 9

Mito 2: Matlab es lento 10

Mito 3: Matlab es caro 11 12

Mito 3: Matlab es caro 13

¿Qué SI nos gusta de Matlab?

§ Prototipado rápido de aplicaciones § Soporte para desarrolladores § Representación sencilla de operaciones algebraicas § Interfaz gráfica (GUI) para depuración y desarrollo § Matlab Simulink § Programación paralela basada en maestro-esclavo (workers) § Parfor – Bucles paralelos § GPU 14

¿Qué NO nos gusta de Matlab?

§ Entorno cerrado de desarrollo (esclavos de Matlab). § Gestión de memoria. § El despliegue de aplicaciones es altamente dependiente. § Limitadas alternativas para la paralelización eficiente en memoria compartida. 15

Sálvame C++ 16

Alternativas a Matlab en C++

§ Eigen

§ Armadillo

§ ArrayFire 17

¿Qué es Armadillo?

§ Biblioteca de código libre para C++. § Fácil uso y con una sintaxis similar a Matlab. § Basada en templates y C++11. § Lambdas (transform, for_each, reduce) § Contenedores § Soporte para BLAS y LAPACK de forma adicional. § Representa tipos básicos para representación matemática: § Mat (2D) § Cube (3D) § Soporte para aceleración mediante tarjetas gráfica (GPU). § Proporciona vectorización automática SIMD (eg. SSE2). § CUIDADO: representación column-major. § Soporte para CMake

http://arma.sourceforge.net/ 18

¿Qué NO proporciona Armadillo?

§ Todo el conjunto de bibliotecas proporcionadas por Matlab. § Paralelización de operaciones sobre vectores.

#pragma omp parallel for for (int i = 0; i < inda.n_elem; ++i) { slicevf_GM.at(inda(i)) = ODF(ODF.n_rows - 1 ,i); } }

http://arma.sourceforge.net/ Asignatura/Tema 19

Operadores en Armadillo

§ Operadores sobrecargados para clases Mat, Col, Row y Cube § +: Suma de dos objetos.

§ -: Resta de un objeto de otro o negación de un objeto.

§ /: División de un objeto por otro objeto o un escalar.

§ * Multiplicación matricial de dos objetos; No aplicable a la clase Cube a menos que multiplique un cubo por un escalar.

§ %: Multiplicación por elementos de dos objetos.

§ ==: Evaluación de igualdad entre elementos de dos objetos; Genera una matriz de tipo umat con entradas que indican si en una posición dada los dos elementos de los dos objetos son iguales (1) o no iguales (0) 20

Ejemplo: multiplicación de matrices

#include #include

using namespace std; using namespace arma;

int main (int argc, char** argv) { mat A = randu(5000,5000); mat B = randu(5000,5000); mat C = A *B; return 0; } 21

Ejemplo: Solver

g++ -o solver solver.cpp -larmadillo

#include #include

int main() { arma::vec b; b << 2.0 << 5.0 << 2.0;

arma::mat A; A << 1.0 << 2.0 << arma::endr << 2.0 << 3.0 << arma::endr << 1.0 << 3.0 << arma::endr;

std::cout << ”Solución a mínimos cuadrados: “ << std::end; std::cout << solve(A,b) << std::end;

return 0; }

3.0000 -0.3636 22

Ejemplo: Programación funcional

// Idiff(Idiff>1) = 1; // Idiff(Idiff<0) = 0; Idiff.elem( find(Idiff > 1.0) ).ones(); Idiff.elem( find(Idiff < 0.0) ).zeros(); 23

Matlab Vs Armadillo

for i = 1:Niter fODFi = fODF; Ratio = mBessel_ratio(n_order,Reblurred_S); RL_factor = KernelT * ( Signal .* (Ratio)) ./ (KernelT * (Reblurred)+ eps); fODF = fODFi .* RL_factor; Reblurred = Kernel * fODF; Reblurred_S = (Signal .* Reblurred) ./ sigma2; sigma2_i = (1/N) * sum( (Signal.^2 + Reblurred.^2)/2 - (sigma2 .* Reblurred_S) .*

MATLAB Ratio, 1)./n_order; sigma2_i = min((1/10)^2, max(sigma2_i,(1/50)^2)); sigma2 = repmat(sigma2_i,[N, 1]); end

for (auto i = 0; i < Niter; ++i) { fODFi = fODF; Ratio = mBessel_ratio(n_order,Reblurred_S); RL_factor = KernelT * (Signal % Ratio) / ((KernelT * Reblurred) + std::numeric_limits::epsilon()); fODF = fODFi % RL_factor; Reblurred = Kernel * fODF; Reblurred_S = (Signal % Reblurred) / sigma2; sigma2_i = (1.0/N) * sum( (pow(Signal,2) + pow(Reblurred,2))/2 - (sigma2 %

Armadillo Reblurred_S) % Ratio , 0) / n_order; sigma2_i.transform( [](T val) { return std::min(std::pow(1.0/10.0,2), std::max(val, std::pow(1.0/50.0,2))); } ); sigma2 = repmat(sigma2_i, N, 1); } 24

¿Cómo puedo hacer para acelerar mi aplicación?

§ Permite paralelización “casi” mágica de mi aplicación: § Mediante enlazado de bibliotecas comúnmente usadas: § Intel MKL (CPU) § OpenBLAS (CPU) § Atlas (CPU) § Magma (GPU) § …

§ NVidia permite la descarga (offloading) del cálculo matricial § cuBLAS: necesitamos trabajar con API (complejo) § NVBLAS: descarga automática (fácil) 25

Configurando NVBLAS

NVBLAS_LOGFILE nvblas.log

NVBLAS_CPU_BLAS_LIB libmkl_rt.so #NVBLAS_CPU_BLAS_LIB libopenblas.so

NVBLAS_GPU_LIST 0 #NVBLAS_GPU_LIST ALL

NVBLAS_TILE_DIM 2048

#NVBLAS_GPU_DISABLED_SGEMM #NVBLAS_GPU_DISABLED_DGEMM #NVBLAS_GPU_DISABLED_CGEMM #NVBLAS_GPU_DISABLED_ZGEMM

NVBLAS_CPU_RATIO_CGEMM 0.07

%> LD_PRELOAD=LD_PRELOAD=/usr/local/-7.5/lib64/libnvblas.so ./miapplicacion 26

ArrayFire

§ Programación orientada a dispositivos § Basado en la clase array § Limitado a datos en 1D/2D/3D § Open source § Neutral § Nvidia § AMD (OpenCL) § CPU (CUDA) § Múltiples funcionalidades § Soporte para CMake

https://github.com/arrayfire/arrayfire 27

Ejemplos básicos

arrayA = array(seq(1,9), 3, 3); af_print(A);

af_print(A(0)); // primer elemento af_print(A(0,1)); // primera fila, segunda columna

af_print(A(end)); // último elemento af_print(A(-1)); // último elemento (también)

af_print(A(1,span)); // segunda fila af_print(A.row(end)); // última fila af_print(A.cols(1,end)); // todo menos la segunda fila

float b_host[] = {0,1,2,3,4,5,6,7,8,9}; array b(10, 1, b_host); af_print(b(seq(3))); af_print(b(seq(1,7))); af_print(b(seq(1,7,2))); af_print(b(seq(0,end,2))); 28

Ejemplo

#include std::cout << “Benchmark N-by-N” << std::endl; #include #include for (auto n = 128; n <= 2048; n += 128) { std::cout << n << “x” << n << “ ”; #include A = af::constant (1, n, n); double time = af::timeit(fn); static af::array A; double gflops = 2.0 * powf(n,3) / (time * 1e9); static void fn() if (gflops > peak) { peak = gflops; af::array B = af::matmul(A,A); B.eval(); std::cout << gflops << “GF” << std::endl; } } } catch (af::exception & e) { int main(int argc, char ** argv) std::cout << e.what() << std::endl;; { throw; double peak = 0; } try { int device = argc > 1 ? atoi(argv[1]) : 0; std::cout << “## Max“ << peak << “ GFLOPS“ << std::endl; af::setDevice(device); af::info(); return 0; } 29

Gfor-loop

§ La construcción gfor-loop puede utilizarse para iniciar simultáneamente todas las iteraciones de un bucle for de la GPU o dispositivo. § siempre y cuando las iteraciones sean independientes.

§ gfor-loop se realiza cada iteración al mismo tiempo (en paralelo).

§ Tamaño del intervalo limitado.

§ FFT a cada rodaja de un volumen:

for (auto i = 0; i < N; ++i) A(span,span,i) = fft2(A(span,span,i)); // Secuencial

gfor (seq i, N) A(span,span,i) = fft2(A(span,span,i)); // Paralelo 30

ArrayFire + Armadillo

§ Ambos comparten el mismo layout de memoria (column-major). § Es posible transferir datos de la clase Mat (Armadillo) a array (ArrayFire).

af::array mat_gpu = af::array(filas, columnas, mat_cpu.memptr());

mat_gpu.host(mat_cpu.memptr()); 31

Caso de uso: pHARDI

§ Identificación de fibras nerviosas para estudiar el grado de conectividad de las distintas áreas del cerebro. § Rendimiento: pseudo-tiempo real: http://www.bitbucket.com/fjblas/phardi § Quirófano (operatorio). § Procesamiento estadístico (clínico). 32 33

Evaluación (I)

§Intel Xeon E5-2630 v3 § 8 núcleos § 2.40 GHz, § 128 GB RAM §Ubuntu 14.04 x64 §CUDA versión 7.5. §Compiladores § GCC 5.1 § Flags –O3 y –DNDEBUG §Nvidia Tesla K40 §GTX 680 34

Evaluación (II)

35

Evaluación (y III)

36

Conclusiones

§ Es posible el despliegue de aplicaciones fuera del ámbito Matlab. § Flexibilidad de desarrollo. § Matlab como DSL. 37 Al borde de un ataque de Matlab… sálvame C++.

Francisco Javier García Blas Universidad Carlos III de Madrid Grupo ARCOS 2016