- En este repositorio se encuentran los códigos propuestos en el taller Taller de programación paralela en GPUs con oneAPI de la X Semana de la Informática
- Para poner a punto el taller se recomienda seguir los pasos de la sección Setup del lab
- Los códigos que vamos a trabajar están disponibles en la sección "Ejemplos", resumidamente trabajan algunos de los aspectos mostrados en la parte teórica:
- helloWorld: ilustra la selección de dispositivos
- Memoria USM: uso de USM
- Suma de vectores: suma de vectores
- Tratamiento de imágenes: transformación de imágen a
- Todo el material está disponible en el repositorio github
- Puede descargarse fácilmente clonando el repositorio ejecutando en un terminal el comando
git clone https://github.com/garsanca/semanaInformatica2024.git
- Puede descargarse fácilmente clonando el repositorio ejecutando en un terminal el comando
- Además las transparencias del taller están disponible en el directorio "transparencias"
- En los laboratorio está instalado el entorno de oneAPI para que pueda ser utilizado por los alumnos
- Únicamente hay que tener en cuenta 3 cuestiones:
- Está instalado en el sistema operativo GNU-Linux
- El entorno (compiladores, herramientas y librerías) se activan cargando el script setvars.sh:
source /opt/intel/oneapi/setvars.sh
. Es importante hacerlo cada vez que se abra una consola o terminal
user@host:~/ $ source /opt/intel/oneapi/setvars.sh
:: initializing oneAPI environment ...
bash: BASH_VERSION = 5.1.16(1)-release
args: Using "$@" for setvars.sh arguments:
:: advisor -- latest
:: ccl -- latest
:: clck -- latest
:: compiler -- latest
:: dal -- latest
:: debugger -- latest
:: dev-utilities -- latest
:: dnnl -- latest
:: dpcpp-ct -- latest
:: dpl -- latest
:: inspector -- latest
:: intelpython -- latest
:: ipp -- latest
:: ippcp -- latest
:: ipp -- latest
:: itac -- latest
:: mkl -- latest
:: mpi -- latest
:: tbb -- latest
:: vpl -- latest
:: vtune -- latest
:: oneAPI environment initialized ::
user@host:~/ $ sycl-ls
[opencl:acc:0] Intel(R) FPGA Emulation Platform for OpenCL(TM), Intel(R) FPGA Emulation Device 1.2 [2023.15.3.0.20_160000]
[opencl:cpu:1] Intel(R) OpenCL, 12th Gen Intel(R) Core(TM) i7-12700 3.0 [2023.15.3.0.20_160000]
[opencl:gpu:2] Intel(R) OpenCL HD Graphics, Intel(R) UHD Graphics 770 [0x4680] 3.0 [22.28.23726.1]
[ext_oneapi_level_zero:gpu:0] Intel(R) Level-Zero, Intel(R) UHD Graphics 770 [0x4680] 1.3 [1.3.23726]
[ext_oneapi_cuda:gpu:0] NVIDIA CUDA BACKEND, Quadro P400 0.0 [CUDA 12.2]
- Compilación en Laboratorio 3 en Gráficas Intel integradas:
icpx -fsycl -o exec.intel main.cpp
- Compilación en Laboratorio 3 en Gráficas NVIDIA:
icpx -fsycl -fsycl-targets=nvptx64-nvidia-cuda -o exec.cuda main.cpp
- El Intel® Developer Cloud es un espacio de desarrollo gratuito para que la comunidad de desarrolladores puedan programar aplicaciones.
- Se puede testear y evaluar una variedad de máquinas virtuales
- Sistemas bare metal
- Dispositivos en el Edge
- Plataformas para entrenamiento de IA
- Entornos para desarrollo
- Contenedores
- JupyterLabs
- Conexión directa por SSH
- La documentación y actualizaciones disponible en https://tinyurl.com/ReadmeIDC o en el Readme.md
- Para disponer de cuenta en Intel® Developer Cloud se puede acceder seguir el enlace http://cloud.intel.com
- Siguiendo los pasos del proceso de registro:
- Selección de usuario Stardard
- Creación de cuenta
- Introducción de datos personales
-
Verificación de correo mediante envío de email con un código
-
Aceptación de términos y condiciones de uso del Intel® Developer Cloud
- Para acceder a la cuenta en Intel® Developer Cloud
- Clicar en Already a Member? Sign In e introducir las credenciales creadas anteriormente
- O bien se puede hacer con el "atajo" https://console.cloud.intel.com/
- JupyterLabs: en el menú Training and Workshops
- Clicar en LaunchJupyterLab
- SSH: se puede conectar al IDC vía ssh previamente añadiendo las Account Keys
- Modificar el fichero .ssh/config
Host myidc #←YOU CAN CALL IT ANYTHING
Hostname idcbetabatch.eglb.intel.com
User uXXXXXX #← Request "scheduled access" at https://scheduler.cloud.intel.com/#/systems" to get your user identifier.
#ProxyCommand /usr/bin/nc -x YourProxy:XXXX %h %p # Uncomment if necessary
ServerAliveInterval 60
ServerAliveCountMax 10
StrictHostKeyChecking no # Frequent changes in the setup are taking place now, this will help reduce the known hosts errors.
UserKnownHostsFile=/dev/null
- En nodo frontend o head es un Ubuntu 22.04LTS
- Uso de nodo en modo interactivo: srun --pty bash
- Nodo con Intel Data Center GPU Max 1100
user@localhost:~$ ssh myidc
uXXXX@idc-beta-batch-head-node:~$ srun --pty bash
uXXXX@idc-beta-batch-pvc-node-03:~$ source /opt/intel/oneapi/setvars.sh
:: initializing oneAPI environment ...
bash: BASH_VERSION = 5.1.16(1)-release
args: Using "$@" for setvars.sh arguments:
uXXXX@idc-beta-batch-pvc-node-03:~$ sycl-ls
Warning: ONEAPI_DEVICE_SELECTOR environment variable is set to opencl:cpu;opencl:fpga;level_zero:3.
To see the correct device id, please unset ONEAPI_DEVICE_SELECTOR.
[opencl:cpu:0] Intel(R) OpenCL, Intel(R) Xeon(R) Platinum 8480L 3.0 [2023.16.7.0.21_160000]
[opencl:acc:1] Intel(R) FPGA Emulation Platform for OpenCL(TM), Intel(R) FPGA Emulation Device 1.2 [2023.16.7.0.21_160000]
[opencl:cpu:2] Intel(R) OpenCL, Intel(R) Xeon(R) Platinum 8480L 3.0 [2023.16.7.0.21_160000]
[ext_oneapi_level_zero:gpu:0] Intel(R) Level-Zero, Intel(R) Data Center GPU Max 1100 1.3 [1.3.26516]
- sinfo: obtener información de los nodos disponibles
- squeue: trabajos encolados
- sbatch -p {PARTITION-NAME} {SCRIPT-NAME}: encolar trabajo en las colas
- scancel {JOB-ID}: cancelar trabajo
- srun --pty bash: lanzamiento de trabajo de forma interactiva
- Tomemos como ejemplo un código en SYCL que devuelve el device seleccionado en este caso una GPU
#include <sycl/sycl.hpp>
using namespace sycl;
int main() {
//# Create a device queue with device selector
queue q(gpu_selector_v);
//# Print the device name
std::cout << "Device: " << q.get_device().get_info<info::device::name>() << "\n";
return 0;
}
- Podemos compilarlo con el compilador icpx
uXXXX@idc-beta-batch-pvc-node-03:~$ icpx -o ex exampleSYCL.cpp -fsycl
- Lanzar job mediante slurm
#!/bin/bash
#SBATCH --job-name=gpu_run
#SBATCH --partition=pvc-shared
#SBATCH --error=job.%J.err
#SBATCH --output=job.%J.out
#SBATCH --mail-type=ALL
#SBATCH --mail-user=your@email.com
icpx -o ex exampleSYCL.cpp -fsycl
./ex
- Lanzamos el job y visualizamos salida
uXXXX@idc-beta-batch-head-node:~$ sbatch job.sh
uXXXX@idc-beta-batch-head-node:~$ more job.42151.out
Device: Intel(R) Data Center GPU Max 1100
- Conexión por ssh al nodo head-node: ssh myidc
- Lanzar sesión interactiva: srun --pty bash
- Activar el entorno conda para poder lanzar Jupyter-Lab: conda activate pytorch_xpu
- Lanzar Jupyterlab
- Importante buscar la dirección de escucha del Jupyterlab, algo parecido a http://10.10.10.X:8888/lab?token=9d83e1d8a0eb3ffed84fa3428aae01e592cab170a4119130
user@localhost:~$ ssh myidc
uXXXX@idc-beta-batch-head-node:~$ srun --pty bash
uXXXX@idc-beta-batch-pvc-node-03:~$ source /opt/intel/oneapi/setvars.sh
uXXXX@idc-beta-batch-pvc-node-03:~$ conda activate pytorch-gpu
(pytorch-gpu) uXXXX@idc-beta-batch-pvc-node-04:~$ jupyter-lab --ip $(hostname -i)
....
[I 2023-11-11 15:01:56.875 ServerApp] http://10.10.10.8:8888/lab?token=f25e4d3977b7b059d5ff4b376085562dbbaa3f9dffed4fde
....
- Desde otro terminal local hacer un tunel ssh a la dirección que aparece en la consola lanzada: 10.10.10.X
user@localhost:~$ ssh myidc -L 8888:10.10.10.8:8888
- Conectarse con un navegador a la dirección local: https://localhost:8888 e introducir el token que aparece en el ejemplo en este ejemplo f25e4d3977b7b059d5ff4b376085562dbbaa3f9dffed4fde
- Core compute
- Basada en procesador Xeon 4th gen
- VMs con 8, 16, 32 cores
- Bare Metal 112 cores, 256GB y 2TB disco
- Basada en procesador Xeon 4th gen
- Intel Max GPU
- 4xGPUs 1100 + 2xsockets Xeon 4th gen
- Gaudi Deep Learning Server
- 8x Gaudi HL + Xeon Platinum 3gen
- Lanzar instancia en la consola->Hardware Catalog
- Obtener la forma desde el apartado de reservas, por ejemplo mediante conexión ssh
- En este ejemplo vamos a ver como se selecciona un dispositivo
- Como se elige el dispositivo: sycl::device
- Lanzamiento de kernel a la command-queue asíncrona
- La siguiente tabla muestra la nomenclatura para seleccionar y asociar la cola a los dispositivos
type | Device |
default_selector_v | Selects any device or host device if no device can be found |
gpu_selector_v | Select a GPU |
accelerator_selector_v | Select an accelerator |
cpu_selector_v | Select a CPU device |
my_device_selector | Custom selector |
- El siguiente código disponible en el directorio helloWorld muestra un ejemplo de selección, donde
- La línea
d = sycl::device(sycl::gpu_selector_v)
seleccione el dispositivo - La clase
d.get_info
devuelve informacion asociada al dispositivo - el
single_task
escribe en la salida la cadena "Hello, World!", que está asociada a la ejecución en el dispositivo seleccionado
- La línea
int main(int argc, char **argv) {
auto d = sycl::device(sycl::default_selector_v);
std::cout << "Using " << d.get_info<sycl::info::device::name>() << "\n";
sycl::queue Q(d);
Q.submit([&](handler &cgh) {
// Create a output stream
sycl::stream sout(1024, 256, cgh);
// Submit a unique task, using a lambda
cgh.single_task([=]() {
sout << "Hello, World!" << sycl::endl;
}); // End of the kernel function
}); // End of the queue commands. The kernel is now submited
// wait for all queue submissions to complete
Q.wait();
return 0;
}
- Para compilar los código existe un fichero Makefile que invocando make en consola genera el ejecutable exec
- Se puede elegir el dispositivo mediante la variable de entorno ONEAPI_DEVICE_SELECTOR con los dispositivos disponibles con el comando
sycl-ls
- Se puede elegir el dispositivo mediante la variable de entorno ONEAPI_DEVICE_SELECTOR con los dispositivos disponibles con el comando
user@host:~/ $ make
icpx -c -o main.o main.cpp -I. -fsycl
icpx -o exec main.o -I. -fsycl
user@host:~/ $ ls
exec main.cpp main.o Makefile
user@host:~/ $ sycl-ls
Warning: ONEAPI_DEVICE_SELECTOR environment variable is set to opencl:*;level_zero:1.
To see the correct device id, please unset ONEAPI_DEVICE_SELECTOR.
[opencl:acc:0] Intel(R) FPGA Emulation Platform for OpenCL(TM), Intel(R) FPGA Emulation Device 1.2 [2023.16.7.0.21_160000]
[opencl:cpu:1] Intel(R) OpenCL, Intel(R) Xeon(R) Platinum 8480+ 3.0 [2023.16.7.0.21_160000]
[opencl:gpu:2] Intel(R) OpenCL Graphics, Intel(R) Data Center GPU Max 1100 3.0 [23.22.26516.29]
[opencl:gpu:3] Intel(R) OpenCL Graphics, Intel(R) Data Center GPU Max 1100 3.0 [23.22.26516.29]
[opencl:gpu:4] Intel(R) OpenCL Graphics, Intel(R) Data Center GPU Max 1100 3.0 [23.22.26516.29]
[opencl:gpu:5] Intel(R) OpenCL Graphics, Intel(R) Data Center GPU Max 1100 3.0 [23.22.26516.29]
[ext_oneapi_level_zero:gpu:0] Intel(R) Level-Zero, Intel(R) Data Center GPU Max 1100 1.3 [1.3.26516]
user@host:~/ $ ONEAPI_DEVICE_SELECTOR=opencl:1 ./exec
Using Intel(R) Xeon(R) Platinum 8480+
Hello, World!
user@host:~/ $ ONEAPI_DEVICE_SELECTOR=opencl:2 ./exec
Using Intel(R) Data Center GPU Max 1100
Hello, World!
- Se recomienda experimentar con el cambio de selector para seleccionar CPU/GPU...
- El siguiente ejemplo ilustra el código de suma de vectores
$C_{i} = A_{i}+B_{i}$ - El alumno deberá de completar el código que aparece con el texto TODO
int *a; //TODO: create vectors with USM
int *b; //TODO: create vectors with USM
int *c; //TODO: create vectors with USM
// Parallel for
for(int i=0; i<N; i++){
a[i] = i; // Init a
b[i] = i*i; // Init b
}
// Create a kernel to perform c=a+b
Q.submit([&](handler &h) {
/* To Do!!! */
}
for(int i=0; i<N; i++)
std::cout << "c[" << i << "] = " << c[i] << std::endl;
free(a, Q);
free(b, Q);
free(c, Q);
- Rellenar la reserva de memoria de a, b y c empleando el esquema de USM
- Además se debe de codificar el kernel de suma de vectores dentro del Q.submit
La transformación de una imagen del espacio de color RGB a sepia implica ajustar los componentes de color de la imagen para darle un aspecto cálido y envejecido. El proceso generalmente se realiza mediante una combinación de ajustes en los valores de los canales de color rojo, verde y azul. Aquí hay un enfoque básico para lograr esta transformación:
El fichero fuente sepia.cpp contiene la codificación de la transformación de una imagen procedente de vídeo a sepia. Para compilar el proyecto se ha de (1) crear una carpeta "build", (2) acceder a dicha carpeta, (3) invocar la herramienta cmake y (4) compilar con make. Los pasos se detallan a continuación:
user@host:~/sepia$ mkdir build
user@host:~/sepia$ cd build/
user@host:~/sepia/build$ cmake ..
-- The CXX compiler identification is GNU 11.4.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found OpenCV: /usr (found version "4.5.4")
-- Configuring done
-- Generating done
-- Build files have been written to: sepia/build
user@host:~/sepia$ make
[ 50%] Building CXX object CMakeFiles/sepia.dir/sepia.cpp.o
[100%] Linking CXX executable sepia
[100%] Built target sepia
user@host:~/sepia/build$ ./sepia
Usage: ./sepia <video_filename|0 for webcam>
user@host:~/sepia/build$ ./sepia 0
El proyecto utiliza OpenCV para facilitar el manejo de vídeo e imágenes. El ejecutable muestra en una ventana vídeo original (o el procedente de la webcam si se invoca con el argumento 0, sino se puede pasar como parámetro un vídeo como el que se puede descargar de la URL) y la imágen procesada en tiempo real generando su equivalente en "filtrado sepia". El ejecutable tiene varias opciones que se controlan con el teclado:
- s: save/o guardado de una imagen en el archivo
- d: ejecución en el dispositivo
- h: ejecución en el host
- q: quit/cerrar de la aplicación
El filtrado sepia se procesa en las rutinas applySepia y applySepiaKernel que se detallan a continuación:
// Function to apply sepia effect using a kernel
void applySepiaKernel(unsigned char* input, unsigned char* output, size_t sizeX, size_t sizeY) {
// Sepia transformation matrix
float sepia[3][3] = {
{0.393f, 0.769f, 0.189f},
{0.349f, 0.686f, 0.168f},
{0.272f, 0.534f, 0.131f}
};
for (int y = 0; y < sizeY; ++y) {
for (int x = 0; x < sizeX; ++x) {
float r_in = input[(y * sizeX + x) * 3 + 2]; // Red channel
float g_in = input[(y * sizeX + x) * 3 + 1]; // Green channel
float b_in = input[(y * sizeX + x) * 3 + 0]; // Blue channel
float r_out = sepia[0][0]*r_in + sepia[0][1]*g_in + sepia[0][2]*b_in;
float g_out = sepia[1][0]*r_in + sepia[1][1]*g_in + sepia[1][2]*b_in;
float b_out = sepia[2][0]*r_in + sepia[2][1]*g_in + sepia[2][2]*b_in;
// Clamp the values to be within the range [0, 255]
r_out = std::min(std::max(r_out, 0.0f), 255.0f);
g_out = std::min(std::max(g_out, 0.0f), 255.0f);
b_out = std::min(std::max(b_out, 0.0f), 255.0f);
// Set the output pixel
output[(y * sizeX + x) * 3 + 0] = b_out;
output[(y * sizeX + x) * 3 + 1] = g_out;
output[(y * sizeX + x) * 3 + 2] = r_out;
}
}
}
// Function to apply sepia effect to an image
void applySepia(Mat& input, Mat& output) {
output = Mat::zeros(input.size(), input.type());
// Prepare input and output pointers
size_t sizeX = input.cols;
size_t sizeY = input.rows;
unsigned char* inputPtr = reinterpret_cast<unsigned char*>(input.data);
unsigned char* outputPtr = reinterpret_cast<unsigned char*>(output.data);
// Apply sepia effect using applySepiaKernel
applySepiaKernel(inputPtr, outputPtr, sizeX, sizeY);
}
El alumno deberá rellenar las funciones applySepiaSYCL y applySepiaKernelSYCL, donde se ha guiado el desarrollo a realizar con los comentarios "To fill". Además se debe de proceder a la reserva de memoria USM en el main.