Está en la página 1de 24

Presentación

Nombres:

Edward Manuel

Apellidos:

Arredondo Maleno

Matricula:

1-17-5078

Materia:

Algoritmo paralelo

Profesor:

Eduardo Arvelo

Fecha:

10/07/2021
Introducción

La uso de las Unidades de Procesamiento Gráfico (GPU) se ha popularizado y


masificado en los últimos tiempos, en principio por el auge de juegos en las
computadoras y la utilización en estos de cada vez más recursos gráficos que requieren
de una alta capacidad de procesamiento. Pero para NVIDIA el desarrollo de sus tarjetas
graficas ha ido más allá y ha facilitado acceso a los programadores a los recursos de
dichas tarjetas a través de una interfaz. Los programadores utilizan un modelo de
programación de la GPU en un lenguaje de alto nivel y una arquitectura de
programación paralela, estos dos elementos conforman lo que NVIDIA denomina
Compute Unified Device Architecture (CUDA). Un dispositivo CUDA en una GPU
típicamente contiene miles de núcleos. Los núcleos se agrupan en un flujo de
multiprocesadores (Streaming Multiprocessors - SM) y cada núcleo puede ejecutar
miles de hilos. Trabajando en una arquitectura conocida Single Instruction Multiple
Threads (SIMT), donde todos los núcleos ejecutan la misma instrucción al mismo
tiempo. Estas características de hardware y las implementaciones por software hacen
que una aplicación aproveche los recursos de cálculo de estas tarjetas. La programación
en CUDA deviene como un nuevo paradigma de programación paralela, pues aunque
mantiene los principios generales, incluye características especiales como tipos nuevos
de memoria y un modelo de ejecución de hilos diferente. El auge que ha ido alcanzando
esta tecnología viene dado por su accesibilidad y por la mejora en el rendimiento de las
aplicaciones desarrolladas con ella. “Las aplicaciones desarrolladas para ejecutarse en
las GPU de NVIDIA tienen mejor rendimiento por dólar y por watt que otras versiones
desarrolladas solo para la CPU” (J. Sanders and E. Kandrot 2011).
Contenidos

¿Qué es Cuda? Programación paralela de la GPU

CUDA Es una plataforma de computación paralela y un modelo de programación


desarrollado por NVIDIA para cálculos regulares en su propia GPU (unidad de
procesamiento de gráficos). CUDA permite a los desarrolladores implementar la parte
paralelizada calculada de la GPU, acelerando así la velocidad de calcular la aplicación
intensiva.

Aunque hay otras API de GPU propuestas, por ejemploOpenCL Y hay otras empresas
(por ejemplo,AMD La GPU competitiva, pero la combinación de GPU de CUDA y
NVIDIA domina en múltiples aplicaciones, incluido el aprendizaje profundo, y es la
computadora más rápida en parte del mundo.

Se puede decir que la tarjeta gráfica es tan antigua como la PC, es decir, si consideras
que el adaptador de exhibición de IBM Monochrome de 1981 como una tarjeta
gráfica. Para 1988, puede obtener una tarjeta de Wonder Wonders 2D VGA de 16 bits
de ATI (la compañía finalmente adquirida por AMD). Para 1996, puede comprar un
acelerador de gráficos en 3D de 3DFX Interactive para ejecutar la primera persona que
disparó el quick.

También en 1996, Ying Weida comenzó a intentar competir con un producto vulnerable
para competir con el mercado de acelerador 3D, pero con su desarrollo, obtuvo un pozo.
En 1999, Yingwei Dan lanzó con éxito GeForce 256, esta es la primera tarjeta gráfica
llamada GPU. En ese momento, la razón principal por tener GPU fue juegos. Hasta que
las personas más tarde utilizaron GPUs por matemáticas, ciencia e ingeniería.

El origen de CUDA

En 2003, un grupo de investigadores dirigido por IAN Buck (Ian Buck) reveló el
misterio de Brooke, que es el primer modelo de programación que se usa ampliamente
en la extensión de la estructura paralela de datos C. Buck luego se unió a NVIDIA y
líder de la versión de CUDA en 2006, que es la primera solución comercial para la
GPU.
OpenCl y Cuda

El competidor de CUDA OpenCL fue lanzado por Apple y Khronos Group en 2009
para proporcionar estándares para cálculos heterogéneos, que no se limita a Intel / AMD
CPU con NVIDIA GPU. Aunque Opencl suena muy atractivo debido a su versatilidad,
el rendimiento en NVIDIA GPU no es tan bueno como CUDA, y muchos marcos de
aprendizaje de profundidad lo apoyan, o solo después de la liberación del apoyo de
CUDA.

Mejora de rendimiento de CUDA

A lo largo de los años, CUDA ha mejorado continuamente y amplió su alcance,


mientras que la GPU de NVIDIA mejorada es más o menos. Desde la versión 9.2 de
CUDA, puede usar múltiples gpus del servidor P100, puede aumentar el rendimiento de
la CPU hasta 50 veces. Para alguna carga, V100 (no mostrada en esta figura) es 3 veces
más rápido. La última generación de GPU K80 del servidor ha aumentado de 5 a 12
veces en comparación con la CPU.

Nvidia
El aumento de la velocidad de la GPU se ha convertido en la escasez de cálculos de alto
rendimiento. La ecuación con el tiempo, se mejora el rendimiento de un solo hilo de la
CPU (se recomienda la ley de Moore duplicar cada 18 meses), y debido al fabricante de
chips que encuentran restricciones físicas (incluido el proceso de fabricación, la
resolución de la máscara de chip y la restricción de La resolución de la máscara de chip
y el límite de la tasa de restricción), por lo tanto, se reduce al 10% anual. Restringir la
frecuencia del reloj en tiempo de ejecución.
Nvidia
Dominio de la aplicación CUDA

 Nvidia
Como se muestra en la cifra anterior, las GPU de CUDA y NVIDIA se han adoptado en
muchas áreas que requieren un rendimiento informático de alto flotador. Las listas más
completas incluyen:

1. Calcular finanzas
2. CLIMA, TIEMPO Y SIMULACIÓN DEL OCÉANO

3. Ciencia y análisis de datos.

4. Estudio profundo y aprendizaje automático.

5. Defensa nacional e inteligencia.

6. Fabricación / AEC (arquitectura, ingeniería y construcción): CAD y CAE


(incluida la física de flujo computacional, la mecánica estructural computacional, el
diseño y la visualización, y la automatización de diseño electrónico)

7. Medios de comunicación y entretenimiento (incluida la animación, modelado y


representación; corrección de color y gestión de texturas; síntesis; finalización y
efecto; edición; codificación y distribución digital; gráficos en vivo; Herramienta en
vivo, revisión y gráficos en estéreo)

8. Imagenes medicas

9. Petróleo y gas

10. Investigación: educación superior y supercarties (incluida la química


computacional y la biología, el análisis numérico, la física y la visualización
científica)

11. Salvaguardia

12. Herramientas y gestión

CUDA EN APRENDIZAJE PROFUNDO

Estudio de profundidadDemanda excesiva de velocidad computacional. P.ej, Para


capacitar a los modelos de Google Translate en 2016. Los equipos de Google Brain y
Google Translate han realizado una semana durante cientos de veces utilizando
GPU.TensorFlowcorrer; Para este fin, compraron 2,000 GPU de nivel servidor de
NVIDIA. Si no hay GPU, entonces esta capacitación tardará un mes para no una
semana. Para implementar estos modelos de conversión de TensorFlow, Google utiliza
un nuevo chip de procesamiento personalizado TPU (unidad de procesamiento Tensor).

Además de TensorFlow, muchos otros marcos DL también se basan en CUDA para


proporcionar soporte de GPU, incluyendo CAFFE2, CNTK, DAPRICKS, H2O.AI,
KERAS, MXNET, PYTORCH, THEANO y TORDCH. En la mayoría de los casos,
usan.cuDNNCálculo de red neuronal de profundidad. La biblioteca es tan importante
para la capacitación de marcos de aprendizaje profundo, de modo que todos los marcos
del CUDNN utilizando una versión determinada son sustancialmente los mismos para
casos de uso equivalentes. Cuando CUDA y CUDNN se actualizan de una versión a
otra, todos los marcos de aprendizaje actualizados se mejorarán. El rendimiento de cada
marco tiende a tener diferentes lugares donde pueden extenderse hasta la extensión de
múltiples GPU y múltiples nodos.

Diseño de programa CUDA

Kit de cuda

Incluye bibliotecas, puestas en servicio y herramientas de optimización, compiladores,


documentos y bibliotecas de tiempo de ejecución para implementar aplicaciones. Tiene
componentes que apoyan el aprendizaje de profundidad, el álgebra lineal, el
procesamiento de la señal y los algoritmos paralelos. Por lo general, la biblioteca de
CUDA admite todas las series de GPU NVIDIA, pero en la última generación de
productos, como V100, para la carga de trabajo de entrenamiento de aprendizaje
profundo, que puede ser 3 veces más rápido que P100. Mientras se implemente el
algoritmo requerido en la biblioteca apropiada, el uso de una o más bibliotecas es la
forma más fácil de utilizar GPUS.

Biblioteca de aprendizaje de profundidad de CUDA

En el campo del aprendizaje profundo, hay tres bibliotecas principales de aceleración de


GPU: CUDNN, lo mencioné a los componentes de la GPU de la mayoría de los marcos
de aprendizaje de profundidad de código abierto; TensorRTEs el optimizador de
razonamiento de aprendizaje de profundidad de alto rendimiento de Nvidia y tiempo de
ejecución; conDeepStream Una biblioteca de refuerzo de video. TENSORT le ayuda a
optimizar el modelo de red neuronal, realice una calibración de alta precisión en una
precisión más baja, e implemente modelos capacitados a la capacitación a la nube, el
centro de datos, el sistema integrado o la plataforma de productos de automóviles.
Cuda Lineal Algebra y Biblioteca de Matemáticas

El álgebra lineal es la base para los cálculos de tensor y el aprendizaje de


profundidad. Desde 1989, los científicos e ingenieros han estado utilizando el BLAS
(Programa de álgebra lineal básico), que es una colección de algoritmos de matriz
implementada en Fortran. cuBLASEs la versión de aceleración de la GPU de la BLAS,
que es el mayor rendimiento de la GPU. Cublas asume que la matriz es
intensiva; cuSPARSETratamiento de matrices escasas.

Biblioteca de procesamiento de señales de CUDA

Transformación rápida de Fourier (FFT) es uno de los algoritmos básicos utilizados


para el procesamiento de la señal. Convierte señales tales como formas de onda de
audio en espectro. cuFFTEs la GPU acelerada FFT.

El video codificado / comprimido y decodificado / descomprimido / decodificado


utilizando los estándares tales como H.264 y otras normas para la transmisión y la
visualización. Nvidia Video Codec SDKEste proceso se acelera a través de la GPU.
Algoritmo paralelo de CUDA

Estas tres bibliotecas para algoritmos paralelos tienen diferentes


usos. NCCL (Biblioteca de comunicaciones colectivas de NVIDIA) se utiliza para
extender la solicitud entre múltiples GPU y nodos; nvGRAPHUtilizado para el análisis
paralelial; ThrustEs una biblioteca de plantillas de C ++ basada en la biblioteca de
plantillas estándar de C ++. El empuje proporciona paralelismo de datos, como escaneo,
clasificación y líneas simplificadas.

Rendimiento de CUDA y CPU


En algunos casos, se puede usar una función CUDA incorporada en lugar de funciones
de CPU equivalentes. Por ejemplo, solo enlace aNVBLASLa biblioteca ahora puede
reemplazar el criterio de multiplicación de matriz GEMM BLAS a la versión GPU:

Nvidia

Fundación de programación de CUDA

Si no puede encontrar la rutina de la biblioteca de CUDA para acelerar el programa,


debe intentarloProgramación de CUDA de bajo nivel 。 Ahora, esto es mucho más fácil
cuando lo intenté por primera vez a finales de 2000. Además de otras razones, la
sintaxis es más sencilla, y la herramienta de desarrollo es mejor. Mi única pregunta es
que en MacOS, el último compilador de CUDA y el último compilador C ++ (de
Xcode) rara vez se sincronizan. La herramienta de línea de comando más antigua debe
descargarse de Apple, luego usarxcode-selectCambie a ellos para que el código CUDA
compile y vincule.

Por ejemplo, considere agregar dos matrices usando las siguientes rutinas sencillas C / C
++:

void add(int n, float *x, float *y)


{  
       for (int i = 0; i < n; i++)      
             y[i] = x[i] + y[i];
}

Aprobar__global__Las palabras clave se agregan a la Declaración, que puede


convertirla en el kernel que se ejecutará en la GPU, luego llame al kernel usando los tres
soportes:

add<<<1, 1>>>(N, x, y);

También debes cambiar tu


derecho.cudaMallocManagedconcudaFree malloc / newconfree / deleteLlame para
asignar espacio en la GPU. Finalmente, debe esperar a que se complete el cálculo de la
GPU, y luego use los resultados en la CPU, puede
usarcudaDeviceSynchronizerealizarcudaDeviceSynchronize 。

El soporte triple anterior usa un bloque de hilo y un hilo. Las gpus de NVIDIA actual
pueden manejar muchos bloques y hilos. Por ejemplo, basado enPascal GPU
arquitecturaTESLA P100 GPU tiene 56 multiprocesadores de flujo (SM), cada
procesador admite hasta 2048 hilos activos.

El código del kernel deberá conocer su bloque y índice de hilo para encontrar su
compensación en la matriz de transferencia. Núcleo paralelo usualmente
useCuadrículaCiclo, por ejemplo:

__global__
void add(int n, float *x, float *y)
{
   int index = blockIdx.x * blockDim.x + threadIdx.x;
   int stride = blockDim.x * gridDim.x;
   for (int i = index; i < n; i += stride)
     y[i] = x[i] + y[i];
}

Si observa el ejemplo en el kit de herramientas de CUDA, encontrará que hay más


problemas que deben considerarse además del conocimiento básico introducido
anteriormente. Por ejemplo, algunas llamadas de la función CUDA deben ser
envasadascheckCudaErrors()Llamar. De manera similar, en muchos casos, el código
más rápido utilizarácuBLASLa biblioteca, y la asignación de la memoria del host y el
equipo y la reproducción de la matriz de espalda.

Los beneficios de usar GPU

La Unidad de procesamiento de gráficos (GPU) proporciona un rendimiento de


instrucción y un ancho de banda de memoria mucho más altos que la CPU con un precio
y una potencia similares. Muchas aplicaciones aprovechan estas capacidades superiores
para ejecutarse más rápido en la GPU que en la CPU (consulte Aplicaciones de GPU ).
Otros dispositivos informáticos, como los FPGA, también son muy eficientes desde el
punto de vista energético, pero ofrecen mucha menos flexibilidad de programación que
las GPU. Esta diferencia de capacidades entre la GPU y la CPU existe porque están
diseñadas con diferentes objetivos en mente. Si bien la CPU está diseñada para
sobresalir en la ejecución de una secuencia de operaciones, llamada subproceso , lo más
rápido posible y puede ejecutar algunas decenas de estos subprocesos en paralelo, la
GPU está diseñada para sobresalir en la ejecución de miles de ellos en paralelo. La GPU
está especializada para cálculos altamente paralelos y, por lo tanto, está diseñada de
manera que se dediquen más transistores al procesamiento de datos en lugar de al
almacenamiento en caché de datos y al control de flujo. La Figura 1 esquemática
muestra un ejemplo de distribución de recursos de chip para una CPU frente a una GPU.

Dedicar más transistores al procesamiento de datos, por ejemplo, cálculos de punto


flotante, es beneficioso para cálculos altamente paralelos; la GPU puede ocultar las
latencias de acceso a la memoria con computación, en lugar de depender de grandes
cachés de datos y un control de flujo complejo para evitar largas latencias de acceso a la
memoria, las cuales son caras en términos de transistores. En general, una aplicación
tiene una combinación de partes paralelas y partes secuenciales, por lo que los sistemas
se diseñan con una combinación de GPU y CPU para maximizar el rendimiento general.
Las aplicaciones con un alto grado de paralelismo pueden aprovechar esta naturaleza
masivamente paralela de la GPU para lograr un mayor rendimiento que en la CPU.

Un modelo de programación paralela escalable


La llegada de las CPU multinúcleo y las GPU de muchos núcleos significa que los
chips de procesador convencionales ahora son sistemas paralelos. El desafío es
desarrollar software de aplicación que escale de manera transparente su paralelismo
para aprovechar el número creciente de núcleos de procesador, de la misma manera que
las aplicaciones de gráficos 3D escalan de manera transparente su paralelismo a GPU de
muchos núcleos con números de núcleos muy variables. El modelo de programación
paralela CUDA está diseñado para superar este desafío mientras mantiene una curva de
aprendizaje baja para los programadores familiarizados con lenguajes de programación
estándar como C.

Modelo de programación

Este capítulo presenta los conceptos principales detrás del modelo de programación
paralela en CUDA y describe cómo se exponen en C ++. En Interfaz de programación
se ofrece una descripción detallada de CUDA C ++ . El código completo para el
ejemplo de suma de vectores utilizado en este capítulo y el siguiente se puede encontrar
en el vectorAddMuestra CUDA .

Granos CUDA C ++ amplía C ++ al permitir que el programador defina funciones de C


++, llamadas núcleos , que, cuando se llaman, se ejecutan N veces en paralelo mediante

Jerarquía de hilos

Por conveniencia, threadIdxes un vector de 3 componentes, de modo que los hilos se


pueden identificar utilizando un índice de hilo unidimensional, bidimensional o
tridimensional, formando un bloque de hilos unidimensional, bidimensional o
tridimensional, llamado bloque de hilo . Esto proporciona una forma natural de invocar
el cálculo de los elementos de un dominio como un vector, una matriz o un volumen. El
índice de un hilo y su ID de hilo se relacionan entre sí de una manera sencilla: para un
bloque unidimensional, son lo mismo; para un bloque bidimensional de tamaño (D x , D
y ) , el ID de hilo de un hilo de índice (x, y) es (x + y D x ) ; para un bloque
tridimensional de tamaño (D x , D y , D z ) , el ID de hilo de un hilo de índice (x, y, z)
es (x + y D x+ z D x D y ) .

Jerarquía de la memoria
Los subprocesos CUDA pueden acceder a datos de múltiples espacios de memoria
durante su ejecución, como se ilustra en la Figura 5 . Cada hilo tiene memoria local
privada. Cada bloque de subprocesos tiene memoria compartida visible para todos los
subprocesos del bloque y con la misma vida útil que el bloque. Todos los subprocesos
tienen acceso a la misma memoria global.

También hay dos espacios de memoria adicionales de solo lectura accesibles para todos
los subprocesos: los espacios de memoria constante y de textura. Los espacios de
memoria global, constante y de textura están optimizados para diferentes usos de
memoria (consulte Accesos a la memoria del dispositivo ). La memoria de textura
también ofrece diferentes modos de direccionamiento, así como filtrado de datos, para
algunos formatos de datos específicos (consulte Textura y memoria de superficie ). Los
espacios de memoria global, constante y de textura son persistentes en los lanzamientos
del kernel por la misma aplicación.

Programación heterogénea

Como se ilustra en la Figura 6 , el modelo de programación CUDA supone que los


subprocesos CUDA se ejecutan en un dispositivo físicamente separado que opera como
un coprocesador del host que ejecuta el programa C ++. Este es el caso, por ejemplo,
cuando los núcleos se ejecutan en una GPU y el resto del programa C ++ se ejecuta en
una CPU. El modelo de programación CUDA también asume que tanto el host como el
dispositivo mantienen sus propios espacios de memoria separados en DRAM,
denominados memoria del host y memoria del dispositivo, respectivamente. Por lo
tanto, un programa administra los espacios de memoria global, constante y de textura
visibles para los núcleos a través de llamadas al tiempo de ejecución de CUDA (descrito
en Interfaz de programación). Esto incluye la asignación y desasignación de memoria
del dispositivo, así como la transferencia de datos entre el host y la memoria del
dispositivo. La memoria unificada proporciona memoria administrada para unir los
espacios de memoria del dispositivo y del host. Se puede acceder a la memoria
administrada desde todas las CPU y GPU del sistema como una imagen de memoria
única y coherente con un espacio de direcciones común. Esta capacidad permite la
suscripción excesiva de la memoria del dispositivo y puede simplificar en gran medida
la tarea de transferir aplicaciones al eliminar la necesidad de duplicar explícitamente los
datos en el host y el dispositivo. Consulte Programación de memoria unificada para
obtener una introducción a la memoria unificada.

Capacidad de cómputo

La capacidad de cálculo de un dispositivo está representada por un número de versión,


también llamado a veces su "versión SM". Este número de versión identifica las
funciones compatibles con el hardware de la GPU y las aplicaciones lo utilizan en
tiempo de ejecución para determinar qué funciones de hardware y / o instrucciones
están disponibles en la GPU actual.

Recopilación justo a tiempo

Cualquier código PTX cargado por una aplicación en tiempo de ejecución se compila
además en código binario por el controlador del dispositivo. Esto se llama compilación
justo a tiempo. La compilación justo a tiempo aumenta el tiempo de carga de la
aplicación, pero permite que la aplicación se beneficie de las nuevas mejoras del
compilador que vienen con cada nuevo controlador de dispositivo. También es la única
forma de que las aplicaciones se ejecuten en dispositivos que no existían en el momento
en que se compiló la aplicación, como se detalla en Compatibilidad de aplicaciones .

Compatibilidad binaria

El código binario es específico de la arquitectura. Se genera un objeto cubin usando la


opción del compilador-código que especifica la arquitectura de destino: por ejemplo,
compilar con -code = sm_35produce código binario para dispositivos con capacidad de
cálculo 3.5. La compatibilidad binaria está garantizada de una revisión menor a la
siguiente, pero no de una revisión menor a la anterior ni a través de revisiones
importantes. En otras palabras, un objeto cubin generado para la capacidad de cálculo
Xy solo se ejecutará en dispositivos de capacidad de cálculo Xz donde z≥y .

Compatibilidad de aplicaciones

Para ejecutar código en dispositivos con capacidad de cálculo específica, una


aplicación debe cargar código binario o PTX que sea compatible con esta capacidad de
cálculo como se describe en Compatibilidad binaria y compatibilidad PTX . En
particular, para poder ejecutar código en arquitecturas futuras con mayor capacidad de
cálculo (para las cuales aún no se puede generar código binario), una aplicación debe
cargar código PTX que se compilará justo a tiempo para estos dispositivos (consulte
Just- compilación a tiempo).

Implementación de hardware

La arquitectura de la GPU NVIDIA se basa en una matriz escalable de


multiprocesadores de transmisión ( SM ) multiproceso . Cuando un programa CUDA en
la CPU del host invoca una cuadrícula del kernel, los bloques de la cuadrícula se
enumeran y distribuyen a multiprocesadores con capacidad de ejecución disponible. Los
subprocesos de un bloque de subprocesos se ejecutan simultáneamente en un
multiprocesador y varios bloques de subprocesos se pueden ejecutar simultáneamente
en un multiprocesador. Cuando los bloques de subprocesos terminan, se lanzan nuevos
bloques en los multiprocesadores desocupados

Arquitectura SIMT

El multiprocesador crea, administra, programa y ejecuta subprocesos en grupos de 32


subprocesos paralelos llamados warps . Los subprocesos individuales que componen
una urdimbre comienzan juntos en la misma dirección de programa, pero tienen su
propio contador de direcciones de instrucción y estado de registro y, por lo tanto,
pueden ramificarse y ejecutarse de forma independiente. El término urdimbre se origina
en el tejido, la primera tecnología de hilo paralelo.

Transferencia de datos entre el host y el dispositivo

Las aplicaciones deben esforzarse por minimizar la transferencia de datos entre el host
y el dispositivo. Una forma de lograr esto es mover más código del host al dispositivo,
incluso si eso significa ejecutar kernels que no exponen suficiente paralelismo para
ejecutarse en el dispositivo con total eficiencia. Las estructuras de datos intermedias
pueden crearse en la memoria del dispositivo, operarse por el dispositivo y destruirse sin
que el host las asigne ni las copie en la memoria del host. Además, debido a la
sobrecarga asociada con cada transferencia, agrupar muchas transferencias pequeñas en
una sola transferencia grande siempre funciona mejor que hacer cada transferencia por
separado.

Accesos a la memoria del dispositivo

Una instrucción que accede a la memoria direccionable (es decir, memoria global,
local, compartida, constante o de textura) puede necesitar ser reeditada varias veces
dependiendo de la distribución de las direcciones de memoria en los subprocesos dentro
de la deformación. La forma en que la distribución afecta el rendimiento de la
instrucción de esta manera es específica para cada tipo de memoria y se describe en las
siguientes secciones. Por ejemplo, para la memoria global, como regla general, cuanto
más dispersas están las direcciones, más reducido es el rendimiento.

Instrucciones de control de flujo

Cualquier instrucción de control de flujo (Si, cambiar, hacer, para, mientras) puede
afectar significativamente el rendimiento efectivo de las instrucciones al hacer que los
subprocesos del mismo warp diverjan (es decir, sigan diferentes rutas de ejecución). Si
esto sucede, las diferentes rutas de ejecución deben serializarse, aumentando el número
total de instrucciones ejecutadas para este warp. Para obtener el mejor rendimiento en
los casos en los que el flujo de control depende del ID del hilo, la condición de control
debe escribirse de manera que se minimice el número de deformaciones divergentes.
Esto es posible porque la distribución de las deformaciones a lo largo del bloque es
determinista como se menciona en Arquitectura SIMT . Un ejemplo trivial es cuando la
condición de control solo depende de (threadIdx / warpSize) dónde warpSizees el
tamaño de la urdimbre. En este caso, ninguna urdimbre diverge ya que la condición de
control está perfectamente alineada con las urdimbres.

Alcance de las primitivas CUDA

Tanto en el host como en el dispositivo, el tiempo de ejecución de CUDA ofrece una


API para lanzar kernels, para esperar a que se complete el trabajo lanzado y para
rastrear dependencias entre lanzamientos a través de transmisiones y eventos. En el
sistema anfitrión, el estado de los lanzamientos y las primitivas CUDA que hacen
referencia a los flujos y eventos son compartidos por todos los subprocesos dentro de un
proceso; sin embargo, los procesos se ejecutan de forma independiente y es posible que
no compartan objetos CUDA. Existe una jerarquía similar en el dispositivo: los núcleos
lanzados y los objetos CUDA son visibles para todos los subprocesos en un bloque de
subprocesos, pero son independientes entre los bloques de subprocesos. Esto significa,
por ejemplo, que un flujo puede ser creado por un hilo y utilizado por cualquier otro
hilo en el mismo bloque de hilo, pero no puede ser compartido con hilos en ningún otro
bloque de hilo.

Ventajas

CUDA presenta ciertas ventajas sobre otros tipos de computación sobre GPU utilizando
APIs gráficas.

 Lecturas dispersas: se puede consultar cualquier posición de memoria.


 Memoria compartida: CUDA pone a disposición del programador un área de
memoria de 16KB (o 48KB en la serie Fermi) que se compartirá entre hilos. Dado
su tamaño y rapidez puede ser utilizada como caché.
 Lecturas más rápidas de y hacia la GPU.
 Soporte para enteros y operadores a nivel de bit.

Limitaciones

 No se puede utilizar recursividad, punteros a funciones, variables estáticas


dentro de funciones o funciones con número de parámetros variable
 No está soportado el renderizado de texturas
 En precisión simple no soporta números desnormalizados o NaNs
 Puede existir un Cuello de botella entre la CPU y la GPU por los anchos de
banda de los buses y sus latencias.
 Los threads o Hilo de ejecución, por razones de eficiencia, deben lanzarse en
grupos de al menos 32, con miles de hilos en total.

El modelo de cuda
CUDA intenta aprovechar el gran paralelismo, y el alto ancho de banda de la memoria
en las GPU en aplicaciones con un gran coste aritmético frente a realizar numerosos
accesos a memoria principal, lo que podría actuar de cuello de botella.

El modelo de programación de CUDA está diseñado para que se creen aplicaciones que
de forma transparente escalen su paralelismo para poder incrementar el número de
núcleos computacionales. Este diseño contiene tres puntos claves, que son la jerarquía
de grupos de hilos, las memorias compartidas y las barreras de sincronización.

La estructura que se utiliza en este modelo está definido por un grid, dentro del cual hay
bloques de hilos que están formados por como máximo 512 hilos distintos.

Cada hilo está identificado con un identificador único, que se accede con la variable
threadIdx. Esta variable es muy útil para repartir el trabajo entre distintos hilos.
threadIdx tiene 3 componentes (x, y, z), coincidiendo con las dimensiones de bloques de
hilos. Así, cada elemento de una matriz, por ejemplo, lo podría tratar su homólogo en un
bloque de hilos de dos dimensiones.

Al igual que los hilos, los bloques se identifican mediante blockIdx (en este caso con
dos componentes x e y). Otro parámetro útil es blockDim, para acceder al tamaño de
bloque.

Kernel

Un kernel en “C for CUDA”, es una función la cual al ejecutarse lo hará en N distintos


hilos en lugar de en secuencial. Se define incluyendo __global__ en la declaración. Por
ejemplo:

//Definición del kernel


__global__ void f(int a, int b, int c)
{
}
Si nuestra función f queremos que calcule la diferencia entre dos vectores A y B y lo
almacene en un tercero C:

__global__ void f(int* A, int* B, int* C)


{
int i = threadIdx.x;
C[i] = A[i] - B[i];
}

Esta función se ejecutaría una vez en cada hilo, reduciendo el tiempo total de ejecución
en gran medida, y dividiendo su complejidad, O(n), por una constante directamente
relacionada con el número de procesadores disponibles.

El mismo ejemplo con matrices sería:

__global__ void f(int** A, int** B, int** C)


{
int i = threadIdx.x; //Columna del bloque que ocupa este determinado hilo
int j= threadIdx.y; //Fila
C[i][j] = A[i][j] - B[i][j];
}

Invocaciones de kernel

En una llamada a un kernel, se le ha de pasar el tamaño de grid y de bloque, por


ejemplo, en el main del ejemplo anterior podríamos añadir:

dim3 bloque(N,N); //Definimos un bloque de hilos de N*N


dim3 grid(M,M) //Grid de tamaño M*M

f<<<grid, bloque>>>(A, B, C);

En el momento que se invoque esta función, los bloques de un grid se enumerarán y


distribuirán por los distintos multiprocesadores libres.
Sincronización

Como los distintos hilos colaboran entre ellos y pueden compartir datos, se requieren
unas directivas de sincronización. En un kernel, se puede explicitar una barrera
incluyendo una llamada a __syncthreads(), en la que todos los hilos se esperarán a que
los demás lleguen a ese mismo punto.

Conclusión
CUDA es una tecnología que permite obtener grandes rendimientos para problemas con
un alto paralelismo. Hay que tener claro su funcionamiento para saber si es adecuado y
obtener el mayor rendimiento posible. Programar en CUDA es fácil, pero no lo es
obtener rendimiento.

También podría gustarte