Comparación del número de procesadores cuda. Adónde lleva cuda: aplicación práctica de la tecnología gpgpu: el mejor hardware. Historia del desarrollo de CUDA

tecnología CUDA

Vladimir Frolov,[correo electrónico protegido]

Anotación

El artículo habla sobre la tecnología CUDA, que permite a un programador utilizar tarjetas de video como potentes unidades informáticas.

Las herramientas proporcionadas por Nvidia permiten escribir programas de unidades de procesamiento de gráficos (GPU) en un subconjunto del lenguaje C++. Esto libera al programador de la necesidad de utilizar sombreadores y comprender el funcionamiento del canal de gráficos. El artículo proporciona ejemplos de programación utilizando CUDA y varias técnicas de optimización.

1. Introducción

El desarrollo de la tecnología informática ha progresado rápidamente durante las últimas décadas. Tan rápido que los desarrolladores de procesadores ya casi han llegado al llamado "callejón sin salida del silicio". Un aumento desenfrenado en la frecuencia del reloj se ha vuelto imposible debido a una serie de razones tecnológicas serias.

Esta es, en parte, la razón por la que todos los fabricantes de sistemas informáticos modernos están avanzando hacia un aumento en el número de procesadores y núcleos, en lugar de aumentar la frecuencia de un procesador. El número de núcleos de unidad central de procesamiento (CPU) en los sistemas avanzados ya es de 8.

Otra razón es la velocidad relativamente baja de la RAM. No importa qué tan rápido funcione el procesador, los cuellos de botella, como muestra la práctica, no son operaciones aritméticas en absoluto, sino accesos fallidos a la memoria: errores de caché.

Sin embargo, si nos fijamos en los procesadores gráficos GPU (Unidad de procesamiento de gráficos), tomaron el camino del paralelismo mucho antes. En las tarjetas de video actuales, por ejemplo en la GF8800GTX, el número de procesadores puede llegar a 128. El rendimiento de estos sistemas, con una programación hábil, puede ser bastante significativo (Fig. 1).

Arroz. 1. Número de operaciones de punto flotante para CPU y GPU

Cuando aparecieron las primeras tarjetas de video en el mercado, eran dispositivos altamente especializados bastante simples (en comparación con el procesador central) diseñados para aliviar al procesador de la carga de visualizar datos bidimensionales. Con el desarrollo de la industria del juego y la aparición de juegos tridimensionales como Doom (Fig. 2) y Wolfenstein 3D (Fig. 3), surgió la necesidad de visualización 3D.

Desde el momento en que 3Dfx creó las primeras tarjetas de video Voodoo (1996) hasta 2001, solo se implementó en la GPU un conjunto fijo de operaciones sobre los datos de entrada.

Los programadores no tenían otra opción en el algoritmo de renderizado y, para aumentar la flexibilidad, aparecieron sombreadores: pequeños programas ejecutados por la tarjeta de video para cada vértice o para cada píxel. Sus tareas incluían transformaciones sobre los vértices y sombreado: cálculo de la iluminación en un punto, por ejemplo, utilizando el modelo de Phong.

Aunque los sombreadores han avanzado mucho hoy en día, debe entenderse que fueron diseñados para tareas altamente especializadas como la transformación y rasterización 3D. Si bien las GPU evolucionan hacia sistemas multiprocesador de uso general, los lenguajes de sombreado siguen siendo altamente especializados.

Se pueden comparar con FORTRAN en el sentido de que, al igual que FORTRAN, fueron los primeros, pero diseñados para resolver solo un tipo de problema. Los sombreadores son de poca utilidad para resolver otros problemas que no sean transformaciones 3D y rasterización, del mismo modo que FORTRAN no es conveniente para resolver problemas no relacionados con cálculos numéricos.

Hoy en día existe una tendencia a utilizar tarjetas de vídeo de forma poco convencional para resolver problemas en los campos de la mecánica cuántica, la inteligencia artificial, los cálculos físicos, la criptografía, la visualización físicamente correcta, la reconstrucción a partir de fotografías, el reconocimiento, etc. Es inconveniente resolver estas tareas en el marco de las API de gráficos (DirectX, OpenGL), ya que estas API se crearon para aplicaciones completamente diferentes.

El desarrollo de la programación de propósito general en GPU (Programación general en GPU, GPGPU) condujo lógicamente al surgimiento de tecnologías destinadas a una gama más amplia de tareas que la rasterización. Como resultado, Nvidia creó Compute Unified Device Architecture (o CUDA para abreviar) y la empresa competidora ATI creó la tecnología STREAM.

Cabe señalar que al momento de escribir este artículo, la tecnología STREAM estaba muy por detrás de CUDA en desarrollo y, por lo tanto, no la consideraremos aquí. Nos centraremos en CUDA, una tecnología GPGPU que le permite escribir programas en un subconjunto del lenguaje C++.

2. Diferencia fundamental entre CPU y GPU

Echemos un vistazo rápido a algunas de las diferencias significativas entre las áreas y características de las aplicaciones de CPU y tarjetas de video.

2.1. Posibilidades

La CPU está diseñada inicialmente para resolver problemas generales y funciona con memoria direccionable aleatoriamente. Los programas de la CPU pueden acceder directamente a cualquier celda de memoria lineal y homogénea.

Este no es el caso de las GPU. Como aprenderá al leer este artículo, CUDA tiene hasta 6 tipos de memoria. Puede leer desde cualquier celda a la que sea físicamente accesible, pero no puede escribir en todas las celdas. La razón es que la GPU es, en cualquier caso, un dispositivo específico diseñado para fines específicos. Esta limitación se introdujo para aumentar la velocidad de ciertos algoritmos y reducir el costo del equipo.

2.2. Rendimiento de la memoria

Un problema constante con la mayoría de los sistemas informáticos es que la memoria es más lenta que el procesador. Los fabricantes de CPU resuelven este problema introduciendo cachés. Las áreas de memoria utilizadas con más frecuencia se colocan en la memoria o memoria caché y funcionan a la frecuencia del procesador. Esto le permite ahorrar tiempo al acceder a los datos utilizados con más frecuencia y cargar el procesador con los cálculos reales.

Tenga en cuenta que los cachés son esencialmente transparentes para el programador. Tanto al leer como al escribir, los datos no van directamente a la RAM, sino que pasan por las cachés. Esto permite, en particular, leer rápidamente un valor inmediatamente después de escribirlo.

Las GPU (aquí nos referimos a las tarjetas de video GF de la octava serie) también tienen cachés, y también son importantes, pero este mecanismo no es tan poderoso como en una CPU. En primer lugar, no todos los tipos de memoria se almacenan en caché y, en segundo lugar, las cachés son de sólo lectura.

En las GPU, los accesos lentos a la memoria se ocultan mediante computación paralela. Mientras algunas tareas esperan datos, otras están trabajando, listas para los cálculos. Este es uno de los principios fundamentales de CUDA, que puede mejorar enormemente el rendimiento del sistema en su conjunto.

3. Núcleo CUDA

3.1. modelo de transmisión

La arquitectura informática CUDA se basa en el conceptoun comando para muchos datos(Instrucción única, datos múltiples, SIMD) y concepto multiprocesador.

El concepto SIMD significa que una instrucción puede procesar múltiples datos simultáneamente. Por ejemplo, el comando addps en los procesadores Pentium 3 y Pentium más nuevos le permite agregar 4 números de punto flotante de precisión simple a la vez.

Un multiprocesador es un procesador SIMD de múltiples núcleos que permite ejecutar solo una instrucción en todos los núcleos en un momento dado. Cada núcleo multiprocesador es escalar, es decir no admite operaciones vectoriales puras.

Antes de continuar, introduzcamos un par de definiciones. Tenga en cuenta que en este artículo nos referiremos a dispositivo y host de una manera completamente diferente a la que están acostumbrados la mayoría de los programadores. Usaremos dichos términos para evitar discrepancias con la documentación CUDA.

En nuestro artículo, por dispositivo nos referiremos a un adaptador de vídeo que admita el controlador CUDA u otro dispositivo especializado diseñado para ejecutar programas que utilicen CUDA (como NVIDIA Tesla). En este artículo, consideraremos la GPU sólo como un dispositivo lógico, evitando detalles de implementación específicos.

Llamaremos host a un programa en la RAM normal de la computadora que usa la CPU y realiza funciones de control para trabajar con el dispositivo.

De hecho, la parte de su programa que se ejecuta en la CPU es anfitrión, y tu tarjeta de video - dispositivo. Lógicamente, el dispositivo se puede representar como un conjunto de multiprocesadores (Fig. 4) más un controlador CUDA.

Arroz. 4. Dispositivo

Supongamos que queremos ejecutar un determinado procedimiento en nuestro dispositivo en N subprocesos (es decir, queremos paralelizar su trabajo). Según la documentación de CUDA, llamemos a este procedimiento kernel.

Una característica de la arquitectura CUDA es su organización en cuadrícula de bloques, lo cual es inusual para aplicaciones multiproceso (Fig. 5). En este caso, el controlador CUDA distribuye de forma independiente los recursos del dispositivo entre subprocesos.

Arroz. 5. Organización de hilos

En la figura. 5. El kernel se designa como Kernel. Todos los subprocesos que ejecutan este kernel se combinan en bloques (Block) y los bloques, a su vez, se combinan en una cuadrícula (Grid).

Como se puede ver en la Figura 5, se utilizan índices bidimensionales para identificar hilos. Los desarrolladores de CUDA han brindado la posibilidad de trabajar con índices tridimensionales, bidimensionales o simples (unidimensionales), según sea más conveniente para el programador.

En general, los índices son vectores tridimensionales. Para cada hilo, se conocerá lo siguiente: el índice del hilo dentro del bloque threadIdx y el índice del bloque dentro de la grilla blockIdx. Al inicio, todos los subprocesos se diferenciarán sólo en estos índices. De hecho, es a través de estos índices que el programador ejerce el control, determinando qué parte de sus datos se procesa en cada hilo.

La respuesta a la pregunta de por qué los desarrolladores eligieron esta organización en particular no es trivial. Una razón es que se garantiza la ejecución de un bloque. en uno multiprocesador del dispositivo, pero un multiprocesador puede ejecutar varios bloques diferentes. Las razones restantes quedarán claras más adelante en el artículo.

Un bloque de tareas (hilos) se ejecuta en un multiprocesador en partes o grupos, llamados warps. El tamaño de deformación actual en las tarjetas de video con soporte CUDA es de 32 subprocesos. Las tareas dentro del grupo warp se ejecutan en estilo SIMD, es decir. Todos los subprocesos dentro de warp solo pueden ejecutar una instrucción a la vez.

Aquí hay que hacer una salvedad. En las arquitecturas modernas al momento de escribir este artículo, el número de procesadores dentro de un multiprocesador es 8, no 32. De ello se deduce que no todo el warp se ejecuta simultáneamente, sino que se divide en 4 partes, que se ejecutan secuencialmente (ya que los procesadores son escalares).

Pero, en primer lugar, los desarrolladores de CUDA no regulan estrictamente el tamaño de la deformación. En sus trabajos mencionan el parámetro del tamaño de la urdimbre, y no el número 32. En segundo lugar, desde un punto de vista lógico, la urdimbre es la unión mínima de hilos, de la cual podemos decir que todos los hilos dentro de ella se ejecutan simultáneamente, y al mismo tiempo. Al mismo tiempo no hay suposiciones sobre el resto, el sistema no se realizará.

3.1.1. Derivación

Inmediatamente surge la pregunta: si en el mismo momento todos los hilos dentro de una urdimbre están ejecutando la misma instrucción, ¿qué pasa con las ramas? Después de todo, si hay una rama en el código del programa, las instrucciones serán diferentes. Aquí se utiliza una solución estándar para la programación SIMD (Figura 6).

Arroz. 6. Organización de sucursales en SIMD

Digamos que tenemos el siguiente código:

si (cont.)B;

En el caso de SISD (Instrucción única, datos únicos), ejecutamos la declaración A, verificamos la condición y luego ejecutamos las declaraciones B y D (si la condición es verdadera).

Ahora tengamos 10 subprocesos ejecutándose en estilo SIMD. En los 10 subprocesos ejecutamos la declaración A, luego verificamos la condición cond y resulta que en 9 de cada 10 subprocesos es verdadera y en un subproceso es falsa.

Está claro que no podemos lanzar 9 hilos para ejecutar el operador B, y el restante para ejecutar el operador C, porque sólo se puede ejecutar una instrucción en todos los hilos al mismo tiempo. En este caso, debe hacer esto: primero, "matamos" el subproceso separado para que no estropee los datos de nadie y ejecutamos los 9 subprocesos restantes. Luego "matamos" 9 subprocesos que ejecutaron el operador B y pasamos por un subproceso con el operador C. Después de esto, los subprocesos se fusionan nuevamente y ejecutan el operador D todos al mismo tiempo.

El triste resultado es que no sólo se desperdician recursos del procesador en la molienda de bits vacíos en los subprocesos divididos, sino que lo que es mucho peor es que terminaremos teniendo que ejecutar AMBAS ramas.

Sin embargo, no todo es tan malo como podría parecer a primera vista. Una gran ventaja de la tecnología es que estos trucos los realiza dinámicamente el controlador CUDA y son completamente transparentes para el programador. Al mismo tiempo, cuando se trata de comandos SSE de CPU modernas (específicamente en el caso de intentar ejecutar 4 copias del algoritmo simultáneamente), el propio programador debe cuidar los detalles: combinar datos en quads, no olvidarse de la alineación. , y generalmente escribe en un nivel bajo, de hecho, como en ensamblador.

De todo lo anterior se desprende una conclusión muy importante. Las ramas no causan degradación del rendimiento en sí mismas. Las únicas ramas dañinas son aquellas en las que los hilos divergen dentro del mismo grupo de hilos warp. Además, si los hilos divergen dentro de un bloque, pero en diferentes grupos de warp, o dentro de diferentes bloques, esto no tiene absolutamente ningún efecto.

3.1.2. Comunicación entre hilos

Al momento de escribir este artículo, cualquier interacción entre subprocesos (sincronización e intercambio de datos) solo era posible dentro de un bloque. Es decir, es imposible organizar la interacción entre subprocesos de diferentes bloques utilizando únicamente capacidades documentadas.

En cuanto a las funciones no documentadas, se desaconseja su uso. La razón de esto es que dependen de las características de hardware específicas de un sistema en particular.

La sincronización de todas las tareas dentro de un bloque se realiza llamando a la función __synchtreads. El intercambio de datos es posible a través de la memoria compartida, ya que es común a todas las tareas dentro del bloque.

3.2. Memoria

CUDA distingue seis tipos de memoria (Fig. 7). Estos son registros, memoria local, global, compartida, constante y de textura.

Esta abundancia se debe a las características específicas de la tarjeta de video y su propósito principal, así como al deseo de los desarrolladores de hacer que el sistema sea lo más barato posible, sacrificando en varios casos la versatilidad o la velocidad.

Arroz. 7. Tipos de memoria en CUDA

3.2.0. Registros

Siempre que es posible, el compilador intenta colocar todas las variables de funciones locales en registros. Se accede a dichas variables a máxima velocidad. En la arquitectura actual, hay 8192 registros de 32 bits disponibles por multiprocesador. Para determinar cuántos registros hay disponibles para un subproceso, debe dividir este número (8192) por el tamaño del bloque (el número de subprocesos que contiene).

Con la división habitual de 64 subprocesos por bloque, el resultado es sólo 128 registros (hay algunos criterios objetivos, pero 64 son adecuados en promedio para muchas tareas). En realidad, nvcc nunca asignará 128 registros. Normalmente no da más de 40 y las variables restantes irán a la memoria local. Esto sucede porque se pueden ejecutar varios bloques en un multiprocesador. El compilador intenta maximizar el número de bloques que funcionan simultáneamente. Para una mayor eficiencia, debes intentar ocupar menos de 32 registros. Entonces, teóricamente, se pueden ejecutar 4 bloques (8 warps, si hay 64 subprocesos en un bloque) en un multiprocesador. Sin embargo, aquí también se debe tener en cuenta la cantidad de memoria compartida ocupada por los subprocesos, ya que si un bloque ocupa toda la memoria compartida, dos de esos bloques no se pueden ejecutar en un multiprocesador simultáneamente.

3.2.1. memoria local

En los casos en que los datos del procedimiento local sean demasiado grandes o el compilador no pueda calcular algún paso de acceso constante para ellos, puede colocarlos en la memoria local. Esto se puede facilitar, por ejemplo, moldeando punteros para tipos de diferentes tamaños.

Físicamente, la memoria local es análoga a la memoria global y opera a la misma velocidad. Al momento de escribir este artículo, no existen mecanismos para evitar explícitamente que el compilador use memoria local para variables específicas. Dado que es bastante difícil controlar la memoria local, es mejor no utilizarla en absoluto (consulte la sección 4 “Recomendaciones de optimización”).

3.2.2. Memoria global

En la documentación CUDA como uno de los principales logros.La tecnología ofrece la posibilidad de direccionamiento arbitrario de la memoria global. Es decir, puede leer desde cualquier celda de memoria y también escribir en una celda arbitraria (este no suele ser el caso en una GPU).

Sin embargo, en este caso hay que pagar por la versatilidad con la velocidad. La memoria global no se almacena en caché. Funciona muy lentamente, en cualquier caso se debe minimizar el número de accesos a la memoria global.

La memoria global se necesita principalmente para almacenar los resultados de un programa antes de enviarlos al host (en DRAM normal). La razón de esto es que la memoria global es el único tipo de memoria donde puedes escribir cualquier cosa.

Las variables declaradas con el calificador __global__ se asignan a la memoria global. La memoria global también se puede asignar dinámicamente llamando a cudaMalloc(void* mem, int size) en el host. Esta función no se puede llamar desde el dispositivo. De ello se deduce que la asignación de memoria debe ser manejada por el programa host que se ejecuta en la CPU. Los datos del host se pueden enviar al dispositivo llamando a la función cudaMemcpy:

cudaMemcpy(void* gpu_mem, void* cpu_mem, int size, cudaMemcpyHostToDevice);

Puedes hacer el procedimiento inverso exactamente de la misma forma:

cudaMemcpy(void* cpu_mem, void* gpu_mem, int size, cudaMemcpyDeviceToHost);

Esta llamada también se realiza desde el anfitrión.

Cuando se trabaja con memoria global, es importante seguir la regla de fusión. La idea principal es que los subprocesos deben acceder a celdas de memoria consecutivas, de 4, 8 o 16 bytes. En este caso, el primer hilo debe acceder a una dirección alineada con un límite de 4, 8 o 16 bytes, respectivamente. Las direcciones devueltas por cudaMalloc están alineadas en al menos un límite de 256 bytes.

3.2.3. Memoria compartida

La memoria compartida no es almacenable en caché pero es una memoria rápida. Se recomienda utilizarlo como caché administrado. Sólo hay 16 KB de memoria compartida disponibles por multiprocesador. Al dividir este número por la cantidad de tareas en el bloque, obtenemos la cantidad máxima de memoria compartida disponible por subproceso (si planea usarla de forma independiente en todos los subprocesos).

Una característica distintiva de la memoria compartida es que se aborda por igual para todas las tareas dentro de un bloque (Fig. 7). De ello se deduce que se puede utilizar para intercambiar datos entre subprocesos de un solo bloque.

Se garantiza que durante la ejecución del bloque en un multiprocesador, se preservará el contenido de la memoria compartida. Sin embargo, después de cambiar un bloque en un multiprocesador, no se garantiza que se conserve el contenido del bloque anterior. Por lo tanto, no debes intentar sincronizar tareas entre bloques, dejando datos en la memoria compartida y esperando su seguridad.

Las variables declaradas con el calificador __shared__ se asignan en la memoria compartida.

Compartido__ flotante mem_shared;

Cabe recalcar una vez más que sólo existe una memoria compartida para un bloque. Por lo tanto, si necesita usarlo simplemente como caché administrado, debe acceder a diferentes elementos de la matriz, por ejemplo, así:

flotante x = mem_shared;

Donde threadIdx.x es el índice x del hilo dentro del bloque.

3.2.4. Memoria constante

La memoria constante se almacena en caché, como se puede ver en la Fig. 4. El caché existe en una única copia para un multiprocesador, lo que significa que es común para todas las tareas dentro del bloque. En el host, puedes escribir algo en la memoria constante llamando a la función cudaMemcpyToSymbol. Desde el dispositivo, la memoria constante es de sólo lectura.

La memoria constante es muy cómoda de usar. Puede colocar datos de cualquier tipo en él y leerlos mediante una tarea simple.

#definir N 100

Constant__ int gpu_buffer[N];

función_host anulada()

int cpu_buffer[N];

cudaMemcpyToSymbol(gpu_buffer, cpu_buffer, sizeof(int)*N);

// __global__ significa que device_kernel es un kernel que se puede ejecutar en la GPU

Global__ vacío dispositivo_kernel()

int a = gpu_buffer;

int b = gpu_buffer + gpu_buffer;

// gpu_buffer = a; ¡ERROR! la memoria constante es de solo lectura

Dado que la memoria constante utiliza un caché, el acceso a ella es generalmente bastante rápido. El único pero muy grande inconveniente de la memoria constante es que su tamaño es de sólo 64 KB (para todo el dispositivo). Esto implica que tiene sentido almacenar sólo una pequeña cantidad de datos utilizados con frecuencia en la memoria contextual.

3.2.5. Memoria de textura

La memoria de textura está almacenada en caché (Fig. 4). Sólo hay un caché para cada multiprocesador, lo que significa que este caché es común para todas las tareas dentro del bloque.

El nombre de memoria de textura (y, lamentablemente, funcionalidad) se hereda de los conceptos de “textura” y “texturizado”. El texturizado es el proceso de aplicar una textura (solo una imagen) a un polígono durante el proceso de rasterización. La memoria de textura está optimizada para el muestreo de datos 2D y tiene las siguientes capacidades:

    recuperación rápida de valores de tamaño fijo (byte, palabra, palabra doble o cuádruple) de una matriz unidimensional o bidimensional;

    direccionamiento normalizado con números flotantes en el intervalo. Luego puede seleccionarlos usando direccionamiento normalizado. El valor resultante será una palabra de tipo float4 asignada al intervalo;

    CudaMalloc((void**) &gpu_memory, N*sizeof (uint4 )); // asignar memoria en la GPU

    // configurando los parámetros de textura textura

    Texture.addressMode = cudaAddressModeWrap; // modo Envoltura

    Texture.addressMode = cudaAddressModeWrap;

    Texture.filterMode = cudaFilterModePoint; //valor más cercano

    Textura.normalizada = falsa; // no usar direccionamiento normalizado

    CudaBindTexture(0, textura, gpu_memory, N ) // de ahora en adelante esta memoria será considerada memoria de textura

    CudaMemcpy(gpu_memory, cpu_buffer, N*sizeof(uint 4), cudaMemcpyHostToDevice ); // copiar datos aGPU

    // __global__ significa que device_kernel es el kernel que se va a paralelizar

    Global__ vacío dispositivo_kernel()

    uint4 a = tex1Dfetch(textura,0); // ¡Solo puedes seleccionar datos de esta manera!

    uint4 b = tex1Dfetch(textura,1);

    int c = ax * by;

    ...

    3.3. Ejemplo sencillo

    Como ejemplo sencillo, considere el programa cppIntegration del SDK de CUDA. Demuestra técnicas para trabajar con CUDA, así como el uso de nvcc (un subconjunto especial del compilador C++ de Nvidia) en combinación con MS Visual Studio, lo que simplifica enormemente el desarrollo de programas en CUDA.

    4.1. Divide tu tarea correctamente

    No todas las tareas son adecuadas para arquitecturas SIMD. Si su tarea no es adecuada para esto, puede que no valga la pena utilizar una GPU. Pero si estás decidido a utilizar una GPU, deberías intentar dividir el algoritmo en partes que puedan ejecutarse eficientemente en estilo SIMD. Si es necesario, cambie el algoritmo para resolver su problema, cree uno nuevo, uno que se ajuste bien a SIMD. Un ejemplo de un área adecuada para usar la GPU es la implementación de la suma piramidal de elementos de matriz.

    4.2. Seleccionar el tipo de memoria

    Coloque sus datos en textura o memoria constante si todas las tareas en el mismo bloque acceden a la misma ubicación de memoria o áreas muy espaciadas. Los datos 2D se pueden procesar de manera eficiente utilizando las funciones text2Dfetch y text2D. La memoria de textura está optimizada específicamente para el muestreo 2D.

    Utilice la memoria global en combinación con la memoria compartida si todas las tareas acceden aleatoriamente a ubicaciones de memoria diferentes y muy separadas (con direcciones o coordenadas muy diferentes si se trata de datos 2D/3D).

    memoria global => memoria compartida

    Hilos de sincronización();

    Procesar datos en memoria compartida.

    Hilos de sincronización();

    memoria global<= разделяемая память

    4.3. Habilitar contadores de memoria

    El indicador del compilador --ptxas-options=-v le permite saber exactamente cuánta y qué tipo de memoria (registros, compartida, local, constante) está utilizando. Si el compilador usa memoria local, definitivamente lo sabes. Analizar datos sobre la cantidad y los tipos de memoria utilizada puede ser de gran ayuda para optimizar su programa.

    4.4. Intenta minimizar el uso de registros y memoria compartida.

    Cuantos más registros o memoria compartida utilice el núcleo, menos subprocesos (o más bien deformaciones) se podrán ejecutar simultáneamente en un multiprocesador, porque Los recursos multiprocesador son limitados. Por lo tanto, un ligero aumento en la ocupación de los registros o de la memoria compartida puede en algunos casos provocar una caída del rendimiento a la mitad, precisamente porque ahora se ejecuta exactamente la mitad de warps simultáneamente en un multiprocesador.

    4.5. Memoria compartida en lugar de local.

    Si, por alguna razón, el compilador de Nvidia asignó datos a la memoria local (generalmente esto se nota por una caída muy grande en el rendimiento en lugares donde no hay nada que consuma muchos recursos), averigüe exactamente qué datos terminaron en la memoria local y colóquelos en la compartida. memoria ).

    A menudo, el compilador coloca una variable en la memoria local si no se utiliza con frecuencia. Por ejemplo, se trata de una especie de acumulador en el que se acumula un valor calculando algo en un bucle. Si el bucle es grande en términos de volumen de código (¡pero no en tiempo de ejecución!), entonces el compilador puede colocar su acumulador en la memoria local, porque se utiliza relativamente raramente y los registros son pocos. La pérdida de rendimiento en este caso puede ser notable.

    Si rara vez utiliza una variable, es mejor colocarla explícitamente en la memoria global.

    Aunque puede parecer conveniente que el compilador coloque automáticamente dichas variables en la memoria local, en realidad no lo es. Será difícil encontrar el cuello de botella durante modificaciones posteriores del programa si la variable comienza a usarse con más frecuencia. El compilador puede o no transferir dicha variable a la memoria de registro. Si el modificador __global__ se especifica explícitamente, es más probable que el programador le preste atención.

    4.6. Desenrollar bucles

    El desenrollado de bucles es una técnica de ejecución estándar en muchos sistemas. Su esencia es realizar más acciones en cada iteración, reduciendo así el número total de iteraciones y, por tanto, el número de ramas condicionales que tendrá que realizar el procesador.

    A continuación se explica cómo desenrollar el bucle para encontrar la suma de una matriz (por ejemplo, un número entero):

    int un[N]; suma interna;

    para (int i=0;i

    Por supuesto, los bucles también se pueden desenrollar manualmente (como se muestra arriba), pero este es un trabajo improductivo. Es mucho mejor usar plantillas de C++ en combinación con funciones en línea.

    plantilla

    clase ArraySumm

    Dispositivo__ static T exec(const T* arr) ( return arr + ArraySumm (arr+1); )

    plantilla

    clase ArraySumm<0,T>

    Dispositivo__ estático T exec(const T* arr) (retorno 0;)

    para (int i=0;i

    suma+= MatrizSumm<4,int>::exec(a);

    Cabe señalar una característica interesante del compilador nvcc. El compilador siempre incorporará funciones de tipo __device__ de forma predeterminada (hay una directiva especial __noinline__ para anular esto).

    Por lo tanto, puede estar seguro de que un ejemplo como el anterior se desarrollará en una secuencia simple de declaraciones y no será de ninguna manera inferior en eficiencia al código escrito a mano. Sin embargo, en el caso general (no nvcc) no puede estar seguro de esto, ya que inline es solo una indicación para el compilador de que puede ignorarlo. Por lo tanto, no se garantiza que sus funciones estén integradas.

    4.7. Alineación de datos y muestreo de 16 bytes.

    Alinee estructuras de datos en límites de 16 bytes. En este caso, el compilador podrá utilizar instrucciones especiales para ellos que cargan datos de 16 bytes a la vez.

    Si la estructura tiene 8 bytes o menos, puede alinearla en 8 bytes. Pero en este caso, puede seleccionar dos variables a la vez combinando dos variables de 8 bytes en una estructura mediante una unión o conversión de puntero. La conversión debe usarse con precaución porque el compilador puede colocar los datos en la memoria local en lugar de en registros.

    4.8. Conflictos del banco de memoria compartida

    La memoria compartida está organizada en forma de 16 (¡en total!) bancos de memoria con un paso de 4 bytes. Durante la ejecución de un grupo de subprocesos warp en un multiprocesador, se divide en dos mitades (si tamaño warp = 32) de 16 subprocesos, que acceden a la memoria compartida a su vez.

    Las tareas en diferentes mitades de la disformidad no entran en conflicto en la memoria compartida. Debido al hecho de que las tareas de la mitad del grupo warp accederán a los mismos bancos de memoria, se producirán colisiones y, como resultado, una disminución del rendimiento. Las tareas dentro de la mitad de la deformación pueden acceder a diferentes partes de la memoria compartida con un determinado paso.

    Los pasos óptimos son 4, 12, 28, ..., 2^n-4 bytes (Fig. 8).

    Arroz. 8. Pasos óptimos.

    Los pasos no óptimos son 1, 8, 16, 32, ..., 2^n bytes (Fig. 9).

    Arroz. 9. Pasos subóptimos

    4.9. Minimizar los movimientos de datos del host<=>Dispositivo

    Intente transferir los resultados intermedios al host para procesarlos utilizando la CPU lo menos posible. Implemente, si no todo el algoritmo, al menos su parte principal en la GPU, dejando solo las tareas de control a la CPU.

    5. Biblioteca matemática portátil CPU/GPU

    El autor de este artículo ha escrito una biblioteca portátil MGML_MATH para trabajar con objetos espaciales simples, cuyo código está operativo tanto en el dispositivo como en el host.

    La biblioteca MGML_MATH se puede utilizar como marco para escribir sistemas portátiles (o híbridos) de CPU/GPU para calcular problemas físicos, gráficos u otros problemas espaciales. Su principal ventaja es que se puede utilizar el mismo código tanto en la CPU como en la GPU y, al mismo tiempo, la velocidad es el principal requisito de la biblioteca.

    6 . Literatura

      Chris Kaspersky. Técnicas de optimización de programas. Uso eficiente de la memoria. - San Petersburgo: BHV-Petersburg, 2003. - 464 p.: ill.

      Guía de programación CUDA 1.1 ( http://developer.download.nvidia.com/compute/cuda/1_1/NVIDIA_CUDA_Programming_Guide_1.1.pdf )

      Guía de programación CUDA 1.1. página 14-15

      Guía de programación CUDA 1.1. página 48

    Según la teoría de la evolución de Darwin, el primer simio (si
    para ser precisos - homo antecessor, predecesor humano) más tarde se convirtió en
    en nosotros. Centros de computación de varias toneladas con mil o más tubos de radio,
    que ocupaban habitaciones enteras fueron sustituidos por portátiles de medio kilo, que, por cierto,
    no será inferior en rendimiento al primero. Las máquinas de escribir antediluvianas se han convertido
    al imprimir cualquier cosa y sobre cualquier cosa (incluso en el cuerpo humano)
    dispositivos multifuncionales. Los gigantes de los procesadores de repente decidieron tapiar
    núcleo gráfico en "piedra". Y las tarjetas de video comenzaron no solo a mostrar una imagen con
    FPS y calidad de gráficos aceptables, pero también realizar todo tipo de cálculos. Sí
    todavía cómo producir! Se discutirá la tecnología de computación multiproceso que utiliza GPU.

    ¿Por qué GPU?

    Me pregunto por qué decidieron transferir toda la potencia informática a los gráficos.
    ¿adaptador? Como puede ver, los procesadores todavía están de moda y es poco probable que renuncien a su calidez.
    lugar. Pero la GPU guarda un par de ases bajo la manga, junto a un comodín, y algunas mangas
    suficiente. Un procesador central moderno está diseñado para lograr el máximo
    rendimiento al procesar datos enteros y de punto flotante
    coma, sin preocuparse especialmente por el procesamiento paralelo de la información. Al mismo tiempo
    tiempo, la arquitectura de la tarjeta de video le permite "paralelizar" rápidamente y sin problemas
    proceso de datos. Por un lado, se calculan los polígonos (gracias al transportador 3D),
    por otro lado, el procesamiento de texturas de píxeles. Está claro que existe una “armonía”
    ruptura de la carga en el núcleo de la tarjeta. Además, la memoria y el rendimiento del procesador de vídeo.
    más óptima que la combinación “RAM-caché-procesador”. El momento en que una unidad de datos
    en la tarjeta de video comienza a ser procesado por un procesador de flujo GPU, otro
    unidad se carga en paralelo a otra y, en principio, es fácil de lograr
    Carga de GPU comparable al ancho de banda del bus,
    sin embargo, para que esto suceda, los transportadores deben cargarse uniformemente, sin
    cualquier transición y rama condicional. El procesador central, en virtud de su
    La versatilidad requiere un caché completo para sus necesidades de procesamiento.
    información.

    Los expertos han pensado en el trabajo de las GPU en la computación paralela y
    matemáticas y se le ocurrió la teoría de que muchos cálculos científicos son en muchos aspectos similares a
    Procesamiento de gráficos 3D. Muchos expertos creen que el factor fundamental en
    desarrollo GPGPU (Computación de propósito general en GPU – universal
    cálculos usando una tarjeta de video
    ) fue el surgimiento del proyecto Brook GPU en 2003.

    Los creadores del proyecto de la Universidad de Stanford tuvieron que resolver un difícil
    Problema: hardware y software para forzar la producción del adaptador de gráficos.
    cálculos diversos. Y lo lograron. Usando el lenguaje C genérico,
    Los científicos estadounidenses hicieron que la GPU funcionara como un procesador, ajustado para
    procesamiento paralelo. Después de Brook, aparecieron varios proyectos sobre cálculos VGA,
    como la biblioteca Accelerator, la biblioteca Brahma, el sistema
    Metaprogramación GPU++ y otros.

    ¡CUDA!

    La premonición de las perspectivas de desarrollo obligó AMD Y Nvidia
    aferrarse a la GPU Brook como un pitbull. Si omitimos la política de marketing, entonces
    Si implementas todo correctamente, podrás afianzarte no sólo en el sector gráfico.
    mercado, pero también en informática (mira las tarjetas informáticas especiales y
    servidores tesla con cientos de multiprocesadores), desplazando a las CPU habituales.

    Naturalmente, los “señores del FPS” se separaron ante el obstáculo, cada uno a su manera.
    camino, pero el principio básico se mantuvo sin cambios: hacer cálculos
    usando GPU. Y ahora echaremos un vistazo más de cerca a la tecnología "verde": CUDA
    (Arquitectura de dispositivo unificado de computación).

    El trabajo de nuestra "heroína" es proporcionar una API, dos a la vez.
    El primero es de alto nivel, CUDA Runtime, que representa las funciones que
    se dividen en niveles más simples y se pasan a la API inferior: controlador CUDA. Entonces
    que la frase “alto nivel” es exagerada para aplicarla al proceso. Toda la sal es
    exactamente en el controlador, y las bibliotecas creadas amablemente le ayudarán a obtenerlo
    desarrolladores Nvidia: CUBLAS (herramientas para cálculos matemáticos) y
    FFT (cálculo mediante el algoritmo de Fourier). Bueno, pasemos a lo práctico.
    partes del material.

    Terminología CUDA

    Nvidia opera con definiciones muy únicas para la API CUDA. Ellos
    difieren de las definiciones utilizadas para trabajar con un procesador central.

    Hilo– un conjunto de datos que deben ser procesados ​​(no
    requiere grandes recursos de procesamiento).

    Urdimbre– un grupo de 32 hilos. Los datos se procesan únicamente
    se deforma, por lo tanto una deformación es la cantidad mínima de datos.

    Bloquear– un conjunto de flujos (de 64 a 512) o un conjunto
    deformaciones (de 2 a 16).

    Red es una colección de bloques. Esta división de datos
    utilizado únicamente para mejorar el rendimiento. Entonces, si el número
    multiprocesadores es grande, entonces los bloques se ejecutarán en paralelo. si con
    no hubo suerte con la tarjeta (los desarrolladores recomiendan usar
    adaptador no inferior a GeForce 8800 GTS 320 MB), luego se procesarán los bloques de datos
    secuencialmente.

    NVIDIA también introduce conceptos como núcleo, anfitrión
    Y dispositivo.

    ¡Estamos trabajando!

    Para trabajar completamente con CUDA necesita:

    1. Conocer la estructura de los núcleos de sombreado de GPU, desde la esencia de la programación.
    Consiste en distribuir uniformemente la carga entre ellos.
    2. Ser capaz de programar en entorno C, teniendo en cuenta algunos aspectos.

    Desarrolladores Nvidia reveló el "interior" de la tarjeta de video varias veces
    diferente a lo que estamos acostumbrados a ver. Así que, quieras o no, tendrás que estudiarlo todo.
    sutilezas de la arquitectura. Echemos un vistazo a la estructura de la legendaria "piedra" del G80. GeForce 8800
    GTX
    .

    El núcleo del sombreador consta de ocho grupos TPC (Texture Processor Cluster)
    procesadores de textura (por lo tanto, GeForce GTX 280– 15 núcleos, 8800 GTS
    hay seis de ellos 8600 – cuatro, etc.). Éstos, a su vez, constan de dos
    multiprocesadores de transmisión (en adelante, SM). SM (todos ellos
    16) consta de front-end (resuelve los problemas de lectura y decodificación de instrucciones) y
    canalizaciones de back-end (salida final de instrucciones), así como ocho SP escalares (sombreador
    procesador) y dos SFU (unidades de superfunción). Para cada latido (unidad
    tiempo) el front-end selecciona la deformación y la procesa. Para que toda la deformación fluya
    (permítanme recordarles que hay 32) procesados, se requieren 32/8 = 4 ciclos al final del transportador.

    Cada multiprocesador tiene lo que se llama memoria compartida.
    Su tamaño es de 16 kilobytes y proporciona total libertad al programador.
    comportamiento. Distribuye como desees :). La memoria compartida proporciona comunicación entre subprocesos.
    un bloque y no está diseñado para funcionar con sombreadores de píxeles.

    Los SM también pueden acceder a GDDR. Para ello, se les entregaron 8 kilobytes a cada uno.
    memoria caché que almacena todas las cosas más importantes para el trabajo (por ejemplo, informática
    constantes).

    El multiprocesador tiene 8192 registros. El número de bloques activos no puede ser
    más de ocho, y el número de deformaciones no es más de 768/32 = 24. De esto queda claro que G80
    Puede procesar un máximo de 32*16*24 = 12288 subprocesos por unidad de tiempo. No puedes evitarlo
    tener en cuenta estas cifras a la hora de optimizar el programa en el futuro (en una escala
    – el tamaño del bloque, por el otro – el número de subprocesos). El equilibrio de parámetros puede desempeñar un papel
    papel importante en el futuro, por lo que Nvidia recomienda usar bloques
    con 128 o 256 hilos. Un bloque de 512 hilos es ineficiente porque tiene
    mayores retrasos. Teniendo en cuenta todas las sutilezas de la estructura de la tarjeta de video GPU más
    buenas habilidades de programación, puedes crear muy productivo
    Herramienta para computación paralela. Por cierto, sobre programación...

    Programación

    Para la “creatividad” con CUDA necesitas Tarjeta de video GeForce no inferior
    episodio ocho
    . CON

    sitio web oficial, debe descargar tres paquetes de software: controlador de
    Soporte CUDA (cada sistema operativo tiene el suyo), el paquete CUDA SDK en sí (el segundo
    versión beta) y bibliotecas adicionales (kit de herramientas CUDA). Soportes tecnológicos
    sistemas operativos Windows (XP y Vista), Linux y Mac OS X. Para estudiar I
    eligió Vista Ultimate Edition x64 (de cara al futuro, diré que el sistema se comportó
    simplemente excelente). Al momento de escribir estas líneas, era relevante para el trabajo
    Controlador ForceWare 177.35. Utilizado como un conjunto de herramientas.
    Paquete de software Borland C++ 6 Builder (aunque cualquier entorno que funcione con
    lengua C).

    A una persona que domina el idioma le resultará fácil acostumbrarse a un nuevo entorno. Todo lo que se requiere es
    Recuerde los parámetros básicos. Palabra clave _global_ (colocada antes de la función)
    indica que la función pertenece al kernel. La llamarán por la central.
    procesador, y todo el trabajo se realizará en la GPU. La convocatoria _global_ requiere más
    detalles específicos, a saber, tamaño de malla, tamaño de bloque y qué núcleo será
    aplicado. Por ejemplo, la línea _global_ void saxpy_parallel<<>>, donde X –
    el tamaño de la cuadrícula e Y es el tamaño del bloque, especifica estos parámetros.

    El símbolo _dispositivo_ significa que la función será llamada por el núcleo de gráficos, también conocido como
    Seguirá todas las instrucciones. Esta función se encuentra ubicada en la memoria del multiprocesador,
    por lo tanto, es imposible obtener su dirección. El prefijo _host_ significa que la llamada
    y el procesamiento se llevará a cabo únicamente con la participación de la CPU. Hay que tener en cuenta que _global_ y
    Los _dispositivos_ no pueden llamarse entre sí ni pueden llamarse a sí mismos.

    Además, el lenguaje CUDA tiene una serie de funciones para trabajar con memoria de video: cudafree
    (liberando memoria entre GDDR y RAM), cudamemcpy y cudamemcpy2D (copiando
    memoria entre GDDR y RAM) y cudamalloc (asignación de memoria).

    Todos los códigos del programa son compilados por la API CUDA. primero se toma
    código destinado exclusivamente al procesador central y sujeto a
    compilación estándar y otro código destinado al adaptador de gráficos,
    reescrito en el lenguaje intermedio PTX (muy parecido al ensamblador) para
    identificando posibles errores. Después de todos estos “bailes” la final
    traducción (traducción) de comandos a un lenguaje comprensible para GPU/CPU.

    Kit de estudio

    Casi todos los aspectos de la programación se describen en la documentación que va
    junto con el controlador y dos aplicaciones, así como en el sitio web de los desarrolladores. Tamaño
    el artículo no alcanza para describirlos (el lector interesado deberá adjuntar
    un poco de esfuerzo y estudia el material tú mismo).

    El navegador CUDA SDK fue desarrollado especialmente para principiantes. Cualquiera puede
    Sienta el poder de la computación paralela de primera mano (la mejor prueba para
    estabilidad: los ejemplos funcionan sin artefactos ni fallas). La aplicación tiene
    un gran número de miniprogramas indicativos (61 “pruebas”). Para cada experiencia hay
    Documentación detallada del código del programa más archivos PDF. Es inmediatamente obvio que la gente
    los presentes con sus creaciones en el navegador están haciendo un trabajo serio.
    También puede comparar la velocidad del procesador y la tarjeta de video durante el procesamiento.
    datos. Por ejemplo, escanear matrices multidimensionales con una tarjeta de video. GeForce 8800
    GT
    Produce 512 MB con un bloque de 256 subprocesos en 0,17109 milisegundos.
    La tecnología no reconoce tándems SLI, por lo que si tienes un dúo o un trío,
    deshabilite la función de "emparejamiento" antes de trabajar; de lo contrario, CUDA verá solo uno
    dispositivo Doble núcleo AMD Athlon 64 X2(frecuencia central 3000 MHz) misma experiencia
    pasa en 2,761528 milisegundos. Resulta que G92 es más de 16 veces
    más rápido que una roca AMD! Como puede ver, esto está lejos de ser un sistema extremo en
    junto con un sistema operativo no amado por las masas muestra buenos
    resultados.

    Además del navegador, existen una serie de programas útiles para la sociedad. Adobe
    adaptó sus productos a las nuevas tecnologías. Ahora Photoshop CS4 está al completo
    utiliza menos los recursos de los adaptadores gráficos (es necesario descargar un especial
    complemento). Con programas como Badaboom media Converter y RapiHD puedes
    decodificar vídeo en formato MPEG-2. Bueno para el procesamiento de audio
    La utilidad gratuita Accelero es adecuada. La cantidad de software diseñado para la API CUDA,
    sin duda crecerá.

    Y en este momento...

    Mientras tanto, están leyendo este material, trabajadores incansables de las empresas de procesadores.
    están desarrollando sus propias tecnologías para integrar GPU en CPU. Desde el exterior AMD Todo
    está claro: tienen una tremenda experiencia adquirida junto con ATI.

    La creación de los “microdispositivos”, Fusion, constará de varios núcleos bajo
    nombre en clave Bulldozer y chip de video RV710 (Kong). Su relación será
    llevado a cabo a través del bus HyperTransport mejorado. Dependiendo de
    número de núcleos y sus características de frecuencia AMD planea crear un precio completo
    jerarquía de "piedras". También está previsto producir procesadores para portátiles (Falcon),
    y para dispositivos multimedia (Bobcat). Además, es la aplicación de la tecnología.
    en dispositivos portátiles será el desafío inicial para los canadienses. Con desarrollo
    Computación paralela, el uso de tales "piedras" debería ser muy popular.

    Intel un poco retrasado en el tiempo con su Larrabee. Productos AMD,
    si no ocurre nada, aparecerán en los lineales de las tiendas a finales de 2009, comenzando
    2010. Y la decisión del enemigo saldrá a la luz sólo en casi dos
    año.

    Larrabee tendrá una gran cantidad (léase: cientos) de núcleos. Al principio
    También habrá productos diseñados para 8 – 64 núcleos. Son muy similares a los Pentium, pero
    bastante reelaborado. Cada núcleo tiene 256 kilobytes de caché L2
    (su tamaño aumentará con el tiempo). La relación se llevará a cabo a través de
    Bus de anillo bidireccional de 1024 bits. Intel dice que su "niño" será
    funciona perfectamente con DirectX y Open GL API (para desarrolladores de Apple), así que no
    no se requiere intervención de software.

    ¿Por qué te dije todo esto? Es obvio que Larrabee y Fusion no desplazarán
    procesadores estacionarios regulares del mercado, del mismo modo que no se verán obligados a salir del mercado
    tarjetas de video. Para los jugadores y entusiastas de los deportes extremos, el sueño definitivo seguirá existiendo.
    CPU multinúcleo y un tándem de varios VGA de gama alta. Pero que incluso
    Las empresas de procesadores están cambiando a la computación paralela basándose en los principios.
    similar a GPGPU, dice mucho. En particular, sobre lo que tal
    tecnología como CUDA tiene derecho a existir y, muy probablemente, lo hará
    muy popular.

    Un breve resumen

    La computación paralela usando una tarjeta de video es solo una buena herramienta
    en manos de un programador trabajador. Difícilmente procesadores guiados por la ley de Moore
    el fin llegará. Empresas Nvidia todavía queda un largo camino por recorrer
    promocionando su API entre las masas (lo mismo puede decirse de la creación ATI/AMD).
    Cómo será, el futuro lo mostrará. Entonces CUDA volverá :).

    PD Recomiendo que los programadores principiantes y las personas interesadas visiten
    los siguientes “establecimientos virtuales”:

    Sitio web y sitio web oficial de NVIDIA
    GPGPU.com. Todo
    La información proporcionada está en inglés, pero al menos gracias porque no está en inglés.
    Chino ¡Así que adelante! Espero que el autor te haya ayudado al menos un poco.
    ¡Emocionantes comienzos del conocimiento CUDA!

    Y está diseñado para traducir el código del host (código principal, de control) y el código del dispositivo (código de hardware) (archivos con extensión .cu) en archivos objeto adecuados para el proceso de ensamblaje del programa o biblioteca final en cualquier entorno de programación, por ejemplo. en NetBeans.

    La arquitectura CUDA utiliza un modelo de memoria de cuadrícula, modelado de subprocesos de clúster e instrucciones SIMD. Aplicable no sólo para la informática gráfica de alto rendimiento, sino también para diversas informáticas científicas que utilizan tarjetas de vídeo nVidia. Los científicos e investigadores utilizan ampliamente CUDA en una variedad de campos, incluida la astrofísica, la biología y la química computacionales, el modelado de dinámica de fluidos, las interacciones electromagnéticas, la tomografía computarizada, el análisis sísmico y más. CUDA tiene la capacidad de conectarse a aplicaciones utilizando OpenGL y Direct3D. CUDA es un software multiplataforma para sistemas operativos como Linux, Mac OS X y Windows.

    El 22 de marzo de 2010, nVidia lanzó CUDA Toolkit 3.0, que contenía soporte para OpenCL.

    Equipo

    La plataforma CUDA apareció por primera vez en el mercado con el lanzamiento del chip NVIDIA G80 de octava generación y estuvo presente en todas las series posteriores de chips gráficos que se utilizan en las familias de aceleradores GeForce, Quadro y NVidia Tesla.

    La primera serie de hardware compatible con CUDA SDK, el G8x, tenía un procesador vectorial de precisión simple de 32 bits que utilizaba CUDA SDK como API (CUDA admite el tipo doble C, pero su precisión ahora se ha reducido a 32 bits). punto flotante). Los procesadores GT200 posteriores admiten la precisión de 64 bits (solo SFU), pero el rendimiento es significativamente peor que el de la precisión de 32 bits (debido al hecho de que solo hay dos SFU por multiprocesador de flujo, mientras que hay ocho procesadores escalares). La GPU organiza subprocesos múltiples de hardware, lo que le permite utilizar todos los recursos de la GPU. Así, se abre la posibilidad de transferir las funciones del acelerador físico al acelerador de gráficos (un ejemplo de implementación es nVidia PhysX). También abre amplias posibilidades para utilizar hardware de gráficos por computadora para realizar cálculos no gráficos complejos: por ejemplo, en biología computacional y otras ramas de la ciencia.

    Ventajas

    En comparación con el enfoque tradicional para organizar la informática de propósito general a través de API gráficas, la arquitectura CUDA tiene las siguientes ventajas en esta área:

    Restricciones

    • Todas las funciones ejecutables en el dispositivo no admiten la recursividad (CUDA Toolkit 3.1 admite punteros y recursividad) y tienen algunas otras limitaciones.

    GPU y aceleradores de gráficos compatibles

    La lista de dispositivos del fabricante de equipos Nvidia con soporte total declarado para la tecnología CUDA se proporciona en el sitio web oficial de Nvidia: CUDA-Enabled GPU Products (inglés).

    De hecho, los siguientes periféricos soportan actualmente la tecnología CUDA en el mercado de hardware de PC:

    Versión de especificación GPU Tarjetas de video
    1.0 G80, G92, G92b, G94, G94b GeForce 8800GTX/Ultra, 9400GT, 9600GT, 9800GT, Tesla C/D/S870, FX4/5600, 360M, GT 420
    1.1 G86, G84, G98, G96, G96b, G94, G94b, G92, G92b GeForce 8400GS/GT, 8600GT/GTS, 8800GT/GTS, 9600 GSO, 9800GTX/GX2, GTS 250, GT 120/30/40, FX 4/570, 3/580, 17/18/3700, 4700x2, 1xxM, 32 /370M, 3/5/770M, 16/17/27/28/36/37/3800M, NVS420/50
    1.2 GT218, GT216, GT215 GeForce 210, GT 220/40, FX380 LP, 1800M, 370/380M, NVS 2/3100M
    1.3 GT200, GT200b GeForce GTX 260, GTX 275, GTX 280, GTX 285, GTX 295, Tesla C/M1060, S1070, Quadro CX, FX 3/4/5800
    2.0 GF100, GF110 GEFORCE (GF100) GTX 465, GTX 470, GTX 480, Tesla C2050, C2070, S/M2050/70, Quadro Plex 7000, Quadro 4000, 6000, GeForce (GF110) GTX 560 TI 448, GTX570, GTX580, GTX59, GTX59, GTX59, GTX59 0.
    2.1 GF104, GF114, GF116, GF108, GF106 GeForce 610M, GT 430, GT 440, GTS 450, GTX 460, GTX 550 Ti, GTX 560, GTX 560 Ti, 500M, Quadro 600, 2000
    3.0 GK104, GK106, GK107 GeForce GTX 690, GTX 680, GTX 670, GTX 660 Ti, GTX 660, GTX 650 Ti, GTX 650, GT 640, GeForce GTX 680MX, GeForce GTX 680M, GeForce GTX 675MX, GeForce GTX 670MX, GTX 660M, GeForce GT 650M, GeForce GT 645M, GeForce GT 640M
    3.5 GK110
    Nvidia GeForce para computadoras de escritorio
    GeForce GTX 590
    GeForce GTX 580
    GeForce GTX 570
    GeForce GTX 560Ti
    GeForce GTX 560
    GeForce GTX 550Ti
    GeForce GTX 520
    GeForce GTX 480
    GeForce GTX 470
    GeForce GTX 465
    GeForce GTX 460
    GeForce GTS 450
    GeForce GTX 295
    GeForce GTX 285
    GeForce GTX 280
    GeForce GTX 275
    GeForce GTX 260
    GeForce GTS 250
    GeForce GT 240
    GeForce GT 220
    GeForce 210
    GeForce GTS 150
    GeForce GT 130
    GeForce GT 120
    GeForce G100
    GeForce 9800 GX2
    GeForce 9800 GTX+
    GeForce 9800 GTX
    GeForce 9800 GT
    GeForce 9600 OSG
    GeForce 9600GT
    GeForce 9500 GT
    GeForce 9400 GT
    GeForce 9400 mGPU
    GeForce 9300 mGPU
    GeForce 8800 GTS 512
    GeForce 8800 GT
    GeForce 8600 GTS
    GeForce 8600 GT
    GeForce 8500GT
    GeForce 8400GS
    Nvidia GeForce para ordenadores móviles
    GeForce GTX 580M
    GeForce GTX 570M
    GeForce GTX 560M
    GeForce GT 555M
    GeForce GT 540M
    GeForce GT 525M
    GeForce GT 520M
    GeForce GTX 485M
    GeForce GTX 480M
    GeForce GTX 470M
    GeForce GTX 460M
    GeForce GT 445M
    GeForce GT 435M
    GeForce GT 425M
    GeForce GT 420M
    GeForce GT 415M
    GeForce GTX 285M
    GeForce GTX 280M
    GeForce GTX 260M
    GeForce GTS 360M
    GeForce GTS 350M
    GeForce GTS 160M
    GeForce GTS 150M
    GeForce GT 335M
    GeForce GT 330M
    GeForce GT 325M
    GeForce GT 240M
    GeForce GT 130M
    GeForce G210M
    GeForce G110M
    GeForce G105M
    GeForce 310M
    GeForce 305M
    GeForce 9800M GTX
    GeForce 9800M GT
    GeForce 9800M GTS
    GeForce 9700M GTS
    GeForce 9700M GT
    GeForce 9650MGS
    GeForce 9600M GT
    GeForce 9600MGS
    GeForce 9500MGS
    GeForce 9500M G
    GeForce 9300MGS
    GeForce 9300M G
    GeForce 9200MGS
    GeForce 9100M G
    GeForce 8800M GTS
    GeForce 8700M GT
    GeForce 8600M GT
    GeForce 8600MGS
    GeForce 8400M GT
    GeForce 8400MGS
    NVIDIA Tesla *
    Tesla C2050/C2070
    Tesla M2050/M2070/M2090
    Tesla S2050
    Tesla S1070
    Tesla M1060
    Tesla C1060
    Tesla C870
    Tesla D870
    Tesla S870
    Nvidia Quadro para computadoras de escritorio
    Cuadro 6000
    Cuadro 5000
    Cuadro 4000
    cuadro 2000
    Cuadro 600
    Cuadro FX 5800
    Cuadro FX 5600
    Cuadro FX 4800
    Cuadro FX 4700 X2
    Cuadro FX 4600
    Cuadro FX 3700
    Cuadro FX 1700
    Cuadro FX 570
    Cuadro FX 470
    Quadro FX 380 Perfil Bajo
    Cuadro FX 370
    Quadro FX 370 Perfil Bajo
    Cuadro CX
    Cuadro NVS 450
    Cuadro NVS 420
    Cuadro NVS 290
    Cuadro Plex 2100 D4
    Cuadro Plex 2200 D2
    Cuadro Plex 2100 S4
    Quadro Plex 1000 Modelo IV
    Nvidia Quadro para informática móvil
    Cuadro 5010M
    Cuadro 5000M
    Cuadro 4000M
    Cuadro 3000M
    Cuadro 2000M
    Cuadro 1000M
    Cuadro FX 3800M
    Cuadro FX 3700M
    Cuadro FX 3600M
    Cuadro FX 2800M
    Cuadro FX 2700M
    Cuadro FX 1800M
    Cuadro FX 1700M
    Cuadro FX 1600M
    Cuadro FX 880M
    Cuadro FX 770M
    Cuadro FX 570M
    Cuadro FX 380M
    Cuadro FX 370M
    Cuadro FX 360M
    Cuadro NVS 5100M
    Cuadro NVS 4200M
    Cuadro NVS 3100M
    Cuadro NVS 2100M
    Cuadro NVS 320M
    Cuadro NVS 160M
    Cuadro NVS 150M
    Cuadro NVS 140M
    Cuadro NVS 135M
    Cuadro NVS 130M
    • Los modelos Tesla C1060, Tesla S1070, Tesla C2050/C2070, Tesla M2050/M2070, Tesla S2050 permiten cálculos de GPU con doble precisión.

    Características y especificaciones de varias versiones

    Soporte de funciones (las funciones no enumeradas son
    compatible con todas las capacidades informáticas)
    Capacidad informática (versión)
    1.0 1.1 1.2 1.3 2.x

    Palabras de 32 bits en la memoria global.
    No

    valores de coma flotante en la memoria global
    Funciones atómicas enteras que operan en
    Palabras de 32 bits en memoria compartida
    No
    atomicExch() operando en 32 bits
    valores de punto flotante en la memoria compartida
    Funciones atómicas enteras que operan en
    Palabras de 64 bits en la memoria global.
    Funciones de voto warp
    Operaciones de punto flotante de doble precisión No
    Funciones atómicas que funcionan en 64 bits.
    valores enteros en memoria compartida
    No
    Adición atómica de punto flotante operando en
    Palabras de 32 bits en memoria global y compartida.
    _votación()
    _threadfence_system()
    _syncthreads_count(),
    _syncthreads_and(),
    _syncthreads_o()
    Funciones de superficie
    Rejilla 3D de bloque de hilo
    Especificaciones técnicas Capacidad informática (versión)
    1.0 1.1 1.2 1.3 2.x
    Máxima dimensionalidad de la rejilla de bloques de hilos. 2 3
    Dimensión máxima x, y o z de una rejilla de bloques de hilos 65535
    Máxima dimensionalidad del bloque de hilo. 3
    Dimensión x o y máxima de un bloque 512 1024
    Dimensión z máxima de un bloque 64
    Número máximo de subprocesos por bloque 512 1024
    Tamaño de deformación 32
    Número máximo de bloques residentes por multiprocesador 8
    Número máximo de warps residentes por multiprocesador 24 32 48
    Número máximo de subprocesos residentes por multiprocesador 768 1024 1536
    Número de registros de 32 bits por multiprocesador 8K 16K 32K
    Cantidad máxima de memoria compartida por multiprocesador 16 KB 48KB
    Número de bancos de memoria compartida 16 32
    Cantidad de memoria local por hilo 16 KB 512KB
    Tamaño de memoria constante 64KB
    Conjunto de trabajo de caché por multiprocesador para memoria constante 8 KB
    Conjunto de trabajo en caché por multiprocesador para memoria de texturas Depende del dispositivo, entre 6 KB y 8 KB
    Ancho máximo para textura 1D
    8192 32768
    Ancho máximo para textura 1D
    referencia vinculada a la memoria lineal
    2 27
    Ancho máximo y número de capas.
    para una referencia de textura en capas 1D
    8192x512 16384x2048
    Ancho y alto máximos para 2D
    referencia de textura vinculada a
    memoria lineal o una matriz CUDA
    65536x32768 65536x65535
    Ancho, alto y número máximos
    de capas para una referencia de textura en capas 2D
    8192 x 8192 x 512 16384 x 16384 x 2048
    Ancho, alto y fondo máximos
    para una referencia de textura 3D vinculada a lineal
    memoria o una matriz CUDA
    2048 x 2048 x 2048
    Número máximo de texturas que
    se puede unir a un núcleo
    128
    Ancho máximo para una superficie 1D
    referencia vinculada a una matriz CUDA
    No
    apoyado
    8192
    Ancho y alto máximos para un 2D
    referencia de superficie vinculada a una matriz CUDA
    8192 x 8192
    Número máximo de superficies que
    se puede unir a un núcleo
    8
    Número máximo de instrucciones por
    núcleo
    2 millones

    Ejemplo

    CudaArray* cu_array;< float , 2 >textura< float>Texas; // Asignar matriz cudaMalloc( & cu_array, cudaCreateChannelDesc (), ancho, alto);// Copiar datos de la imagen a la matriz cudaMemcpy( cu_array, image, width* height, cudaMemcpyHostToDevice);<<< gridDim, blockDim, 0 >// Vincula la matriz a la textura

    cudaBindTexture(tex, cu_array); // Ejecuta el kernel dim3 blockDim(16, 16, 1); dim3 gridDim(ancho/blockDim.x, alto/blockDim.y, 1);

    núcleo

    En diciembre de 2009, el modelo de software CUDA se enseña en 269 universidades de todo el mundo. En Rusia, los cursos de formación sobre CUDA se imparten en la Universidad Politécnica de San Petersburgo, Universidad Estatal de Yaroslavl. P. G. Demidov, Moscú, Nizhny Novgorod, San Petersburgo, Tver, Kazán, Novosibirsk, Universidad Técnica Estatal de Novosibirsk, Universidades Estatales de Omsk y Perm, Universidad Internacional de la Naturaleza de la Sociedad y el Hombre "Dubna", Universidad Estatal de Energía de Ivanovo, Universidad Estatal de Belgorod , MSTU ellos. Bauman, Universidad Técnica Química de Rusia que lleva el nombre. Mendeleev, Centro Interregional de Supercomputadoras RAS, . Además, en diciembre de 2009, se anunció que comenzó a funcionar el primer centro científico y educativo ruso "Parallel Computing", ubicado en la ciudad de Dubna, cuyas tareas incluyen capacitación y asesoramiento sobre la resolución de problemas informáticos complejos en GPU.

    En Ucrania, los cursos sobre CUDA se imparten en el Instituto de Análisis de Sistemas de Kiev.

    Campo de golf

    Recursos oficiales

    • Zona CUDA (ruso) - sitio web oficial de CUDA
    • CUDA GPU Computing (inglés): foros web oficiales dedicados a la informática CUDA

    Recursos no oficiales

    Hardware de Tom
    • Dmitri Chekánov. nVidia CUDA: ¿computación en una tarjeta de video o la muerte de la CPU? . Hardware de Tom (22 de junio de 2008).
    • Dmitri Chekánov. nVidia CUDA: evaluación comparativa de aplicaciones de GPU para el mercado masivo. Tom's Hardware (19 de mayo de 2009). Archivado desde el original el 4 de marzo de 2012. Consultado el 19 de mayo de 2009.
    iXBT.com
    • Alexéi Berillo. NVIDIA CUDA: informática sin gráficos en GPU. Parte 1. iXBT.com (23 de septiembre de 2008). Archivado desde el original el 4 de marzo de 2012. Consultado el 20 de enero de 2009.
    • Alexéi Berillo. NVIDIA CUDA: informática no gráfica en GPU. Parte 2. iXBT.com (22 de octubre de 2008). - Ejemplos de implementación de NVIDIA CUDA. Archivado desde el original el 4 de marzo de 2012. Consultado el 20 de enero de 2009.
    Otros recursos
    • Boreskov Alexey Viktorovich. Conceptos básicos de CUDA (20 de enero de 2009). Archivado desde el original el 4 de marzo de 2012. Consultado el 20 de enero de 2009.
    • Vladimir Frólov. Introducción a la tecnología CUDA. Revista online “Informática Gráfica y Multimedia” (19 de diciembre de 2008). Archivado desde el original el 4 de marzo de 2012. Consultado el 28 de octubre de 2009.
    • Ígor Oskolkov. NVIDIA CUDA es un billete asequible al mundo de la gran informática. Computerra (30 de abril de 2009). Consultado el 3 de mayo de 2009.
    • Vladimir Frólov. Introducción a la tecnología CUDA (1 de agosto de 2009). Archivado desde el original el 4 de marzo de 2012. Consultado el 3 de abril de 2010.
    • GPGPU.ru. Usando tarjetas de video para computación
    • . Centro de Computación Paralela

    Notas

    Ver también

    – un conjunto de interfaces de software de bajo nivel ( API) para crear juegos y otras aplicaciones multimedia de alto rendimiento. Incluye soporte de alto rendimiento 2D- Y 3D-dispositivos gráficos, de sonido y de entrada.

    directo3D (D3D) – interfaz para visualización tridimensional primitivos(cuerpos geométricos). Incluido en .

    OpenGL(del ingles Abrir biblioteca de gráficos, literalmente, biblioteca de gráficos abierta) es una especificación que define una interfaz de programación multiplataforma independiente del lenguaje de programación para escribir aplicaciones que utilizan gráficos de computadora bidimensionales y tridimensionales. Incluye más de 250 funciones para dibujar escenas 3D complejas a partir de primitivos simples. Se utiliza para crear videojuegos, realidad virtual y visualización en investigaciones científicas. en la plataforma ventanas compite con .

    OpenCL(del ingles Lenguaje informático abierto, literalmente – un lenguaje abierto de cálculos) – estructura(marco de sistema de software) para escribir programas informáticos relacionados con la computación paralela en varios gráficos ( GPU) Y ( ). al marco OpenCL Incluye un lenguaje de programación y una interfaz de programación de aplicaciones ( API). OpenCL proporciona paralelismo a nivel de instrucción y a nivel de datos y es una implementación de la técnica GPGPU.

    GPGPU(abreviado del inglés) Unidades de procesamiento de gráficos de uso general, literalmente - GPU Propósito general) es una técnica para usar una unidad de procesamiento de gráficos (GPU) o una tarjeta de video para computación general que normalmente realiza una computadora.

    sombreador(Inglés) sombreador) – un programa para construir sombras sobre imágenes sintetizadas, utilizado en gráficos tridimensionales para determinar los parámetros finales de un objeto o imagen. Por lo general, incluye descripciones arbitrariamente complejas de absorción y dispersión de la luz, mapeo de texturas, reflexión y refracción, sombreado, desplazamiento de superficies y efectos de posprocesamiento. Las superficies complejas se pueden visualizar utilizando formas geométricas simples.

    Representación(Inglés) representación) – visualización, en gráficos por computadora, el proceso de obtener una imagen a partir de un modelo mediante software.

    SDK(abreviado del inglés) Kit de desarrollo de software) – un conjunto de herramientas de desarrollo de software.

    UPC(abreviado del inglés) Unidad Central de Procesamiento, literalmente – dispositivo informático central/principal/principal) – central (micro); una pieza de hardware responsable de realizar operaciones computacionales (especificadas por el sistema operativo y el software de la aplicación) y coordinar el funcionamiento de todos los dispositivos.

    GPU(abreviado del inglés) Unidad de procesamiento gráfico, literalmente – dispositivo informático gráfico) – procesador gráfico; un dispositivo o consola de juegos independiente que realiza representación gráfica (visualización). Las GPU modernas son muy eficientes a la hora de procesar y mostrar gráficos de computadora de forma realista. El procesador de gráficos en los adaptadores de video modernos se usa como acelerador de gráficos 3D, pero en algunos casos también se puede usar para cálculos ( GPGPU).

    Problemas UPC

    Durante mucho tiempo, el aumento en el rendimiento de los tradicionales se produjo principalmente debido a un aumento constante en la frecuencia del reloj (aproximadamente el 80% del rendimiento estaba determinado por la frecuencia del reloj) con un aumento simultáneo en el número de transistores en un chip. . Sin embargo, un aumento adicional en la frecuencia del reloj (a una frecuencia de reloj de más de 3,8 GHz, ¡los chips simplemente se sobrecalientan!) encuentra una serie de barreras físicas fundamentales (ya que el proceso tecnológico casi se ha acercado al tamaño de un átomo: , y el tamaño de un átomo de silicio es aproximadamente 0,543 nm):

    En primer lugar, a medida que disminuye el tamaño del cristal y aumenta la frecuencia del reloj, aumenta la corriente de fuga de los transistores. Esto conduce a un mayor consumo de energía y mayores emisiones de calor;

    En segundo lugar, los beneficios de velocidades de reloj más altas se ven parcialmente anulados por la latencia de acceso a la memoria, ya que los tiempos de acceso a la memoria no se mantienen al ritmo de las velocidades de reloj crecientes;

    En tercer lugar, para algunas aplicaciones, las arquitecturas seriales tradicionales se vuelven ineficientes a medida que aumentan las velocidades de reloj debido al llamado "cuello de botella de von Neumann", una limitación de rendimiento resultante del flujo de cálculo secuencial. Al mismo tiempo, aumentan los retrasos en la transmisión de señales resistivo-capacitivas, lo que es un cuello de botella adicional asociado con un aumento en la frecuencia del reloj.

    Desarrollo GPU

    Paralelamente a esto, hubo (¡y hay!) desarrollo GPU:

    Noviembre 2008 – Intel introdujo una línea de 4 núcleos IntelCore i7, que se basan en una microarquitectura de nueva generación Nehalem. Los procesadores funcionan a una frecuencia de reloj de 2,6-3,2 GHz. Fabricado con una tecnología de proceso de 45 nm.

    Diciembre de 2008: comenzaron las entregas de 4 núcleos. AMD Phenom II 940(nombre en clave - deneb). Funciona a una frecuencia de 3 GHz y se produce mediante una tecnología de proceso de 45 nm.

    mayo de 2009 – empresa AMD presentó la versión GPU ATI Radeon HD 4890 con la velocidad del reloj central aumentada de 850 MHz a 1 GHz. Este es el primero gráfico Procesador funcionando a 1 GHz. La potencia informática del chip, gracias al aumento de la frecuencia, aumentó de 1,36 a 1,6 teraflops. El procesador contiene 800 (!) núcleos informáticos y admite memoria de video. GDDR5, DirectX 10.1, ATI CrossFireX y todas las demás tecnologías inherentes a los modelos modernos de tarjetas de video. El chip está fabricado con tecnología de 55 nm.

    Principales diferencias GPU

    Características distintivas GPU(en comparación con ) son:

    – una arquitectura orientada al máximo a aumentar la velocidad de cálculo de texturas y objetos gráficos complejos;

    – potencia máxima típica GPU mucho más alto que eso ;

    – gracias a una arquitectura de transporte especializada, GPU mucho más eficiente en el procesamiento de información gráfica que .

    "Crisis del género"

    "Crisis de género" para maduró en 2005, fue entonces cuando. Pero, a pesar del desarrollo de la tecnología, el aumento de la productividad de los convencionales disminuyó notablemente. Al mismo tiempo el rendimiento GPU sigue creciendo. Entonces, en 2003, esta idea revolucionaria cristalizó: Utilice la potencia informática de los gráficos para sus necesidades.. Las GPU se utilizan cada vez más para informática “no gráfica” (simulación física, procesamiento de señales, matemáticas/geometría computacional, operaciones de bases de datos, biología computacional, economía computacional, visión por computadora, etc.).

    El principal problema era que no existía una interfaz de programación estándar. GPU. Los desarrolladores utilizaron OpenGL o directo3D, pero fue muy conveniente. Corporación Nvidia(uno de los mayores fabricantes de procesadores de gráficos, medios y comunicaciones, así como procesadores de medios inalámbricos; fundado en 1993) comenzó a desarrollar un estándar unificado y conveniente e introdujo la tecnología CUDA.

    como empezó

    2006 – Nvidia demuestra CUDA™; El comienzo de una revolución en la informática. GPU.

    2007 – Nvidia arquitectura de lanzamientos CUDA(versión original SDK CUDA fue presentado el 15 de febrero de 2007); nominación “Mejor Producto Nuevo” de la revista Ciencia popular y "Readers' Choice" de la publicación. Cable HPC.

    2008 – tecnología NVIDIA CUDA ganó la categoría “Excelencia Técnica” de Revista PC.

    Qué ha pasado CUDA

    CUDA(abreviado del inglés) Arquitectura de dispositivo unificado de computación, literalmente – arquitectura informática unificada de dispositivos) – arquitectura (un conjunto de software y hardware) que le permite producir en GPU cálculos de propósito general, mientras GPU En realidad actúa como un poderoso coprocesador.

    Tecnología NVIDIA CUDA™ es el único entorno de desarrollo en un lenguaje de programación do, que permite a los desarrolladores crear software que resuelva problemas informáticos complejos en menos tiempo, gracias a la potencia de procesamiento de las GPU. Millones de personas ya trabajan en el mundo GPU con apoyo CUDA y miles de programadores ya están utilizando herramientas (¡gratuitas!) CUDA para acelerar aplicaciones y resolver las tareas más complejas y que consumen muchos recursos, desde codificación de video y audio hasta exploración de petróleo y gas, modelado de productos, imágenes médicas e investigación científica.

    CUDA le brinda al desarrollador la oportunidad, a su propia discreción, de organizar el acceso al conjunto de instrucciones del acelerador de gráficos y administrar su memoria, así como organizar cálculos paralelos complejos en él. Soporte de acelerador de gráficos CUDA se convierte en una poderosa arquitectura abierta programable, similar a la actual. Todo esto proporciona al desarrollador acceso de bajo nivel, distribuido y de alta velocidad al hardware, haciendo CUDA una base necesaria para construir herramientas serias de alto nivel, como compiladores, depuradores, bibliotecas matemáticas y plataformas de software.

    Uralsky, especialista líder en tecnología Nvidia, comparando GPU Y , dice esto: “ - Este es un todoterreno. Conduce siempre y a todas partes, pero no muy rápido. A GPU- Este es un auto deportivo. En una carretera en mal estado simplemente no irá a ninguna parte, pero dale una buena superficie y mostrará toda su velocidad, ¡que un SUV nunca había soñado!..."

    Capacidades tecnológicas CUDA

    Desde hace décadas está en vigor la Ley de Moore, que establece que cada dos años se duplicará el número de transistores en un chip. Sin embargo, esto fue en 1965, y durante los últimos 5 años la idea de múltiples núcleos físicos en procesadores de consumo comenzó a desarrollarse rápidamente: en 2005, Intel presentó el Pentium D y AMD presentó el Athlon X2. En aquel entonces, las aplicaciones que utilizaban 2 núcleos se podían contar con los dedos de una mano. Sin embargo, la próxima generación de procesadores Intel, que supuso una revolución, tenía exactamente 2 núcleos físicos. Además, la serie Quad apareció en enero de 2007, momento en el que el propio Moore admitió que su ley pronto dejaría de aplicarse.

    ¿Y ahora qué? Los procesadores de doble núcleo, incluso en sistemas de oficina económicos, y 4 núcleos físicos se han convertido en la norma, y ​​esto en sólo 2 o 3 años. No se aumenta la frecuencia de los procesadores, pero se mejora la arquitectura, se aumenta el número de núcleos físicos y virtuales. Sin embargo, la idea de utilizar adaptadores de vídeo equipados con decenas o incluso cientos de "unidades" informáticas existe desde hace mucho tiempo.

    Y aunque las perspectivas para la computación GPU son enormes, la solución más popular es Nvidia CUDA, que es gratuita, tiene mucha documentación y en general es muy fácil de implementar, no hay muchas aplicaciones que utilicen esta tecnología. Básicamente, se trata de todo tipo de cálculos especializados, que al usuario medio en la mayoría de los casos no le importan. Pero también existen programas pensados ​​para el usuario masivo, y de ellos hablaremos en este artículo.

    Primero, un poco sobre la tecnología en sí y para qué se utiliza. Porque Al escribir un artículo, me centro en una amplia gama de lectores, por lo que intentaré explicarlo en un lenguaje accesible, sin términos complejos y de forma algo breve.

    CUDA(Arquitectura de dispositivo unificada de computación en inglés) es una arquitectura de software y hardware que le permite realizar cálculos utilizando procesadores gráficos NVIDIA que admiten la tecnología GPGPU (computación arbitraria en tarjetas de video). La arquitectura CUDA apareció por primera vez en el mercado con el lanzamiento del chip NVIDIA de octava generación: G80 y está presente en todas las series posteriores de chips gráficos que se utilizan en las familias de aceleradores GeForce, Quadro y Tesla. (c) Wikipedia.org

    Los flujos entrantes se procesan independientemente unos de otros, es decir. paralelo.

    Hay una división en 3 niveles:

    Red- centro. Contiene una matriz de bloques de una, dos o tres dimensiones.

    Bloquear– contiene muchos hilos. Los hilos de diferentes bloques no pueden interactuar entre sí. ¿Por qué fue necesario introducir bloques? Cada bloque es esencialmente responsable de su propia subtarea. Por ejemplo, una imagen grande (que es una matriz) se puede dividir en varias partes más pequeñas (matrices) y trabajar con cada parte de la imagen en paralelo.

    Hilo- fluir. Los subprocesos dentro de un bloque pueden interactuar a través de la memoria compartida, que, por cierto, es mucho más rápida que la memoria global, o mediante herramientas de sincronización de subprocesos.

    Urdimbre es una unión de subprocesos que interactúan entre sí; para todas las GPU modernas, el tamaño Warp es 32. Luego viene media deformación, que es la mitad de la deformación, porque El acceso a la memoria generalmente ocurre por separado para la primera y la segunda mitad de la deformación.

    Como puede ver, esta arquitectura es excelente para paralelizar tareas. Y aunque la programación se realiza en lenguaje C con algunas restricciones, en realidad no todo es tan sencillo, porque… No todo se puede paralelizar. Tampoco existen funciones estándar para generar números aleatorios (o la inicialización debe implementarse por separado); Y aunque hay muchas opciones ya preparadas, nada de esto trae alegría. La posibilidad de utilizar la recursividad apareció hace relativamente poco tiempo.

    Para mayor claridad, se escribió un pequeño programa de consola (para minimizar el código) que realiza operaciones con dos matrices de tipo flotante, es decir. con valores no enteros. Por las razones expuestas anteriormente, la inicialización (llenar la matriz con varios valores arbitrarios) la llevó a cabo la CPU. A continuación, se realizaron 25 operaciones diferentes con los elementos correspondientes de cada matriz, los resultados intermedios se escribieron en la tercera matriz. El tamaño de la matriz cambió, los resultados son los siguientes:

    Se realizaron un total de 4 pruebas:

    1024 elementos en cada matriz:

    Se ve claramente que con un número tan pequeño de elementos, la computación paralela es de poca utilidad, porque Los cálculos en sí son mucho más rápidos que su preparación.

    4096 elementos en cada matriz:

    Y ahora puede ver que la tarjeta de video realiza operaciones en matrices 3 veces más rápido que el procesador. Además, el tiempo de ejecución de esta prueba en la tarjeta de video no aumentó (una ligera disminución en el tiempo puede atribuirse a un error).

    Ahora hay 12288 elementos en cada matriz:

    La separación de la tarjeta de video ha aumentado 2 veces. Nuevamente vale la pena señalar que el tiempo de ejecución en la tarjeta de video ha aumentado.
    de manera insignificante, pero en el procesador más de 3 veces, es decir. proporcional a la complejidad de la tarea.

    Y la última prueba es 36864 elementos en cada matriz:

    En este caso, la aceleración alcanza valores impresionantes: casi 22 veces más rápido en una tarjeta de video. Y nuevamente, el tiempo de ejecución en la tarjeta de video aumentó ligeramente, pero en el procesador, el requerido 3 veces, lo que nuevamente es proporcional a la complejidad de la tarea.

    Si continúas complicando los cálculos, la tarjeta de video gana cada vez más. Aunque el ejemplo es algo exagerado, la situación general lo demuestra claramente. Pero como se mencionó anteriormente, no todo se puede paralelizar. Por ejemplo, calcular Pi. Sólo hay ejemplos escritos utilizando el método Monte Carlo, pero la precisión del cálculo es de 7 decimales, es decir flotación regular. Para aumentar la precisión de los cálculos se requiere una larga aritmética, y aquí es donde surgen los problemas, porque Es muy, muy difícil implementar esto de manera efectiva. No pude encontrar ejemplos en Internet que usen CUDA y calculen Pi con 1 millón de decimales. Se han realizado intentos para escribir una aplicación de este tipo, pero el método más simple y eficiente para calcular Pi es el algoritmo Brent-Salamin o la fórmula de Gauss. El conocido SuperPI probablemente (a juzgar por la velocidad de funcionamiento y el número de iteraciones) utiliza la fórmula gaussiana. Y, a juzgar por
    Debido al hecho de que SuperPI es de un solo subproceso, la falta de ejemplos en CUDA y el fracaso de mis intentos, es imposible paralelizar efectivamente el conteo de Pi.

    Por cierto, puedes notar cómo aumenta la carga en la GPU durante los cálculos, así como la asignación de memoria.

    Pasemos ahora a los beneficios más prácticos de CUDA, es decir, los programas actualmente existentes que utilizan esta tecnología. En su mayor parte, se trata de todo tipo de conversores y editores de audio/vídeo.

    Se utilizaron 3 archivos de vídeo diferentes en las pruebas:

        *La historia de la realización de la película Avatar - 1920x1080, MPEG4, h.264.
        *Serie "Mienteme" - 1280x720, MPEG4, h.264.
        *Serie “Siempre hace sol en Filadelfia” - 624x464, xvid.

    El contenedor y tamaño de los dos primeros archivos fue .mkv y 1,55 GB, y el último fue .avi y 272 MB.

    Comencemos con un producto muy sensacional y popular: Badaboom. Versión utilizada - 1.2.1.74 . El costo del programa es $29.90 .

    La interfaz del programa es simple e intuitiva: a la izquierda seleccionamos el archivo o disco fuente, y a la derecha, el dispositivo requerido para el que codificaremos. También hay un modo de usuario en el que los parámetros se configuran manualmente, que es el que usamos.

    Primero, veamos con qué rapidez y eficiencia se codifica el vídeo "en sí mismo", es decir, Misma resolución y aproximadamente el mismo tamaño. Mediremos la velocidad en fps y no en el tiempo transcurrido; de esta manera es más conveniente comparar y calcular cuánto se comprimirá un video de duración arbitraria. Porque Hoy estamos considerando la tecnología "verde", entonces los gráficos serán correspondientes -)

    La velocidad de codificación depende directamente de la calidad, esto es obvio. Vale la pena señalar que la resolución de la luz (llamémosla tradicionalmente SD) no es un problema para Badaboom: la velocidad de codificación es 5,5 veces mayor que la velocidad de cuadros del video original (24 fps). E incluso los vídeos pesados ​​de 1080p son convertidos por el programa en tiempo real. Vale la pena señalar que la calidad del video final es muy cercana al material del video original, es decir. Badaboom codifica de manera muy, muy eficiente.

    Pero normalmente transfieren video a una resolución más baja, veamos cómo van las cosas en este modo. A medida que disminuyó la resolución, la tasa de bits del video también disminuyó. Eran 9500 kbps para un archivo de salida de 1080p, 4100 kbps para 720p y 2400 kbps para 720x404. La elección se basó en una relación tamaño/calidad razonable.

    No se necesitan comentarios. Si realiza una copia de 720p a calidad SD normal, transcodificar una película que dure 2 horas le llevará unos 30 minutos. Y al mismo tiempo, la carga del procesador será insignificante, podrás dedicarte a tus asuntos sin sentir molestias.

    ¿Qué pasa si conviertes el vídeo a un formato para un dispositivo móvil? Para hacer esto, seleccione el perfil del iPhone (tasa de bits 1 Mbit/s, 480x320) y observe la velocidad de codificación:

    ¿Necesito decir algo? Una película de dos horas en calidad normal para iPhone se transcodifica en menos de 15 minutos. Con calidad HD es más difícil, pero sigue siendo muy rápido. Lo principal es que la calidad del material de vídeo de salida se mantiene en un nivel bastante alto cuando se ve en la pantalla de un teléfono.

    En general, las impresiones de Badaboom son positivas, la velocidad de funcionamiento es satisfactoria y la interfaz es sencilla y clara. Se han solucionado todo tipo de errores en versiones anteriores (utilicé la versión beta en 2008). Excepto por una cosa: la ruta al archivo fuente, así como a la carpeta en la que se guarda el video terminado, no debe contener letras rusas. Pero en comparación con las ventajas del programa, este inconveniente es insignificante.

    El siguiente en la fila tendremos Super LoiLoScope. Para la versión regular preguntan 3.280 rublos, y para la versión táctil, que admite control táctil en Windows 7, piden tanto 4.440 rublos. Intentemos descubrir por qué el desarrollador quiere esa cantidad de dinero y por qué el editor de vídeo necesita soporte multitáctil. Última versión utilizada - 1.8.3.3 .

    Es bastante difícil describir la interfaz del programa con palabras, así que decidí hacer un vídeo corto. Diré de inmediato que, como todos los conversores de video para CUDA, la aceleración de GPU solo se admite para salida de video a MPEG4 con el códec h.264.

    Durante la codificación, la carga del procesador es del 100%, pero esto no causa molestias. El navegador y otras aplicaciones ligeras no se ralentizan.

    Pasemos ahora al rendimiento. Para empezar, todo es igual que con Badaboom: transcodificar el vídeo a uno de calidad similar.

    Los resultados son mucho mejores que los de Badaboom. La calidad también es excelente, la diferencia con el original sólo se puede notar comparando los marcos de dos en dos con una lupa.

    Vaya, aquí LoiloScope supera a Badaboom 2,5 veces. Al mismo tiempo, puedes cortar y codificar fácilmente otro vídeo en paralelo, leer noticias e incluso ver películas, e incluso reproducir FullHD sin problemas, aunque la carga del procesador sea máxima.

    Ahora intentemos hacer un vídeo para un dispositivo móvil, llamemos al perfil igual que en Badaboom - iPhone (480x320, 1 Mbit/s):

    No hay ningún error. Todo se volvió a comprobar varias veces y cada vez el resultado fue el mismo. Lo más probable es que esto suceda por la sencilla razón de que el archivo SD se grabó con un códec diferente y en un contenedor diferente. Al transcodificar, el vídeo primero se decodifica, se divide en matrices de cierto tamaño y se comprime. El decodificador ASP utilizado en el caso de xvid es más lento que AVC (para h.264) cuando se decodifica en paralelo. Sin embargo, 192 fps es 8 veces más rápido que la velocidad del vídeo original; una serie de 23 minutos se comprime en menos de 4 minutos. La situación se repitió con otros archivos comprimidos en xvid/DivX.

    Loiloscopio Solo dejé impresiones agradables: la interfaz, a pesar de su singularidad, es conveniente y funcional, y la velocidad de funcionamiento es incomparable. La funcionalidad relativamente pobre es algo frustrante, pero a menudo con una instalación simple solo necesitas ajustar ligeramente los colores, hacer transiciones suaves y agregar texto, y LoiloScope hace un excelente trabajo con esto. El precio también es algo aterrador: más de 100 dólares por la versión normal es normal en el extranjero, pero esas cifras todavía nos parecen un poco descabelladas. Aunque admito que si yo, por ejemplo, filmara y editara videos caseros con frecuencia, tal vez habría pensado en comprarlo. Al mismo tiempo, por cierto, verifiqué la posibilidad de editar contenido HD (o más bien AVCHD) directamente desde una cámara de video sin convertir primero a otro formato; LoiloScope no reveló ningún problema con archivos como .mts.




Arriba