tutorial que programming how historia c gpu gpgpu

que - nvidia cuda



Aplicación GPU básica, cálculos enteros. (3)

En pocas palabras, he hecho varios prototipos de software interactivo. Utilizo pygame ahora (envoltorio SDL de Python) y todo se hace en la CPU. Estoy empezando a portarlo a C ahora y, al mismo tiempo, busco las posibilidades existentes de usar alguna potencia de GPU para explotar la CPU de operaciones redundantes. Sin embargo, no puedo encontrar una buena "guía" sobre qué tecnología / herramientas exactas debo elegir en mi situación. Acabo de leer una gran cantidad de documentos, drena mis poderes mentales muy rápido. No estoy seguro de si es posible, así que estoy desconcertado.
Aquí hice un bosquejo muy aproximado de mi esqueleto de aplicación típico que desarrollé, pero dado que ahora usa GPU (nota, tengo casi ningún conocimiento práctico sobre la programación de GPU). Aún es importante que los tipos de datos y la funcionalidad deben preservarse exactamente. Aquí está:

Así que F (A, R, P) es una función personalizada, por ejemplo, sustitución de elementos, repetición, etc. La función es presumiblemente constante en la duración del programa, las formas del rectángulo generalmente no son iguales a la forma A, por lo que no es un cálculo en el lugar. Así que simplemente se generan con mis funciones. Ejemplos de F: repetir filas y columnas de A; sustituir valores con valores de tablas de sustitución; componer algunos azulejos en una sola matriz; cualquier función matemática en los valores A, etc. Como se dijo, todo esto puede hacerse fácilmente en la CPU, pero la aplicación debe ser realmente fluida. Por cierto, en Python puro, se volvió inutilizable después de agregar varias características visuales, que se basan en matrices numpy. Cython ayuda a hacer funciones personalizadas rápidas, pero el código fuente ya es una especie de ensalada.

Pregunta:

  • ¿Este esquema refleja alguna tecnología (estándar) / dev.tools?

  • CUDA es lo que estoy buscando? Si es así, algunos enlaces / ejemplos que coincidan con la estructura de mi aplicación, serían excelentes.

Me doy cuenta, esta es una gran pregunta, así que daré más detalles si ayuda.

Actualizar

Este es un ejemplo concreto de dos cálculos típicos para mi prototipo de editor de mapas de bits. Así que el editor trabaja con índices y los datos incluyen capas con las máscaras de bits correspondientes. Puedo determinar el tamaño de las capas y las máscaras tienen el mismo tamaño que las capas y, digamos, todas las capas tienen el mismo tamaño ( 1024 ^ 2 píxeles = 4 MB para valores de 32 bits). Y mi paleta es, por ejemplo, 1024 elementos (4 Kilobytes para formato de 32 bpp).
Considera que quiero hacer dos cosas ahora:

Paso 1 Quiero aplanar todas las capas en una sola. Supongamos que A1 es la capa predeterminada (fondo) y que las capas ''A2'' y ''A3'' tienen máscaras ''m2'' y ''m3''. En python escribiría:

from numpy import logical_not ... Result = (A1 * logical_not(m2) + A2 * m2) * logical_not(m3) + A3 * m3

Dado que los datos son independientes, creo que debe dar una proporción proporcional al número de bloques paralelos.

Paso 2 Ahora tengo una matriz y quiero ''colorearla'' con un poco de paleta, por lo que será mi tabla de búsqueda. Como veo ahora, hay un problema con la lectura simultánea del elemento de tabla de búsqueda.

Pero mi idea es que, probablemente, uno solo puede duplicar la paleta para todos los bloques, ¿entonces cada bloque puede leer su propia paleta? Me gusta esto:


Cuando su código es altamente paralelo (es decir, hay poca o ninguna dependencia de datos entre las etapas del procesamiento), entonces puede optar por CUDA (control más sincronizado sobre la sincronización) o OpenCL (API similar a OpenGL muy similar y portátil para interactuar con la GPU para procesamiento del kernel). La mayor parte del trabajo de aceleración que hacemos ocurre en OpenCL, que tiene una excelente interoperabilidad con OpenGL y DirectX, pero también tenemos la misma configuración que con CUDA. Una gran diferencia entre CUDA y OpenCL es que en CUDA puede compilar kernels una vez y demorarlos en la carga (y / o enlazarlos) en su aplicación, mientras que en OpenCL el compilador juega bien con la pila de controladores OpenCL para asegurar que el kernel se compile cuando la aplicación se inicia.

Una alternativa que a menudo se pasa por alto si está utilizando Microsoft Visual Studio es C ++ AMP, una api intuitiva y amigable para la sintaxis de C ++ para aquellos que no desean profundizar en los giros y vueltas lógicas de las API de OpenCL / CUDA. La gran ventaja aquí es que el código también funciona si no tiene una GPU en el sistema, pero no tiene tantas opciones para modificar el rendimiento. Aún así, en muchos casos, esta es una forma rápida y eficiente de escribir a prueba su código de concepto y volver a implementar los bits y partes en CUDA o OpenCL más adelante.

OpenMP y Thread Building Blocks son solo buenas alternativas cuando tienes problemas de sincronización y muchas dependencias de datos. Los subprocesos nativos que utilizan subprocesos de trabajo también son una solución viable, pero solo si tiene una buena idea de cómo se pueden configurar los puntos de sincronización entre los diferentes procesos de tal manera que los subprocesos no se maten de hambre cuando luchan por la prioridad. Esto es mucho más difícil de entender, y herramientas como Parallel Studio son una necesidad. Pero entonces, también está NVida NSight si está escribiendo código GPU.

Apéndice:

Se está desarrollando una nueva plataforma llamada Quasar ( http://quasar.ugent.be/blog/ ) que le permite escribir sus problemas matemáticos en una sintaxis que es muy similar a Matlab, pero con soporte completo de c / c ++ / c # o la integración de java y compilaciones cruzadas (LLVM, CLANG) su código de "kernel" a cualquier configuración de hardware subyacente. Genera archivos ptx de CUDA, o se ejecuta en openCL, o incluso en su CPU utilizando TBB, o una mezcla de ellos. Usando unos pocos nombres, puedes decorar el algoritmo para que el compilador subyacente pueda inferir tipos (también puedes usar explícitamente la tipificación estricta), para que puedas dejar el material de tipo pesado completamente en manos del compilador. Para ser justos, en el momento de redactar este informe, el sistema aún se está borrando y los primeros programas compilados de OpenCL solo se están probando, pero el beneficio más importante es la creación rápida de prototipos con un rendimiento casi idéntico en comparación con un cuda optimizado.


Lo que quiere hacer es enviar los valores realmente rápido a la GPU utilizando el despacho de alta frecuencia y luego mostrar el resultado de una función que es básicamente búsquedas de texturas y algunos parámetros.

Yo diría que este problema solo valdrá la pena resolverlo en la GPU si se cumplen dos condiciones:

  1. El tamaño de A[] está optimizado para hacer que los tiempos de transferencia sean irrelevantes (Mire, http://blog.theincredibleholk.org/blog/2012/11/29/a-look-at-gpu-memory-transfer/ ).

  2. La tabla de búsqueda no es demasiado grande y / o los valores de búsqueda están organizados de manera que la memoria caché se pueda utilizar al máximo, en general, las búsquedas aleatorias en la GPU pueden ser lentas, lo ideal es que pueda cargar previamente los valores R[] en una búfer de memoria compartida para cada elemento del búfer A[] .

Si puede responder a ambas preguntas de manera positiva, solo entonces puede considerar usar la GPU para su problema, de lo contrario, esos 2 factores superarán la velocidad de cálculo que la GPU puede proporcionarle.

Otra cosa que puede tener en cuenta es la mejor manera de superponer los tiempos de transferencia y computación para ocultar tanto como sea posible las tasas de transferencia lentas de la CPU -> datos de la GPU.

Con respecto a su función F(A, R, P) , necesita asegurarse de que no necesita saber el valor de F(A, R, P)[0] para saber cuál es el valor de F(A, R, P)[1] es porque si lo hace, debe volver a escribir F(A, R, P) para solucionar este problema, utilizando alguna técnica de paralelización. Si tiene un número limitado de funciones F() , esto se puede resolver escribiendo una versión paralela de cada función F() para que la utilice la GPU, pero si F() está definido por el usuario, su problema se vuelve un poco más complicado.

Espero que esta información sea suficiente para tener una idea informada de si debe o no usar una GPU para resolver su problema.

EDITAR

Habiendo leído tu edición, diría que sí. La paleta podría caber en la memoria compartida (consulte el tamaño de la memoria compartida de la GPU es muy pequeño, ¿qué puedo hacer al respecto? ), Lo cual es muy rápido; si tiene más de una paleta, puede colocar 16 KB (tamaño de memoria compartida en la mayoría de las tarjetas). ) / 4KB por paleta = 4 paletas por bloque de hilos.

Una última advertencia, las operaciones de enteros no son las más rápidas en la GPU, considere usar puntos flotantes si es necesario después de haber implementado su algoritmo y está funcionando como una optimización barata.


No hay mucha diferencia entre OpenCL / CUDA, así que elige cuál funciona mejor para ti. Solo recuerda que CUDA te limitará a las GPU de NVidia.

Si entiendo correctamente su problema, el kernel (función ejecutada en GPU) debería ser simple. Debería seguir este pseudocódigo:

kernel main(shared A, shared outA, const struct R, const struct P, const int maxOut, const int sizeA) int index := getIndex() // get offset in input array if(sizeA >= index) return // GPU often works better when n of threads is 2^n int outIndex := index*maxOut // to get offset in output array outA[outIndex] := F(A[index], R, P) end

Las funciones F deben estar en línea y puede usar el interruptor o si es para una función diferente. Dado que no se conoce el tamaño de la salida de F, entonces tiene que usar más memoria. Cada instancia del kernel debe conocer las posiciones para las escrituras y lecturas correctas de la memoria, por lo que debe haber un tamaño máximo (si no hay ninguno, ¡todo esto es inútil y debe usar la CPU!). Si los tamaños diferentes son escasos, entonces usaría algo como calcular estos tamaños diferentes después de devolver la matriz a la RAM y calcular estos pocos con CPU, mientras se completa un A con algunos ceros o valores de indicación.

Los tamaños de los arrays son obviamente longitud (A) * maxOut = longitud (outA).

Olvidé mencionar que si la ejecución de F no es la misma en la mayoría de los casos (el mismo código fuente), GPU la serializará. Los multiprocesadores GPU tienen algunos núcleos conectados en la misma memoria caché de instrucciones, por lo que tendrá que serializar el código, que no es el mismo para todos los núcleos. ¡OpenMP o hilos son la mejor opción para este tipo de problema!