Hogar / Oficina / Minería en una tarjeta gráfica GPU: una guía completa. Uso efectivo de GPU Por qué no tengo GPU en el administrador de tareas

Minería en una tarjeta gráfica GPU: una guía completa. Uso efectivo de GPU Por qué no tengo GPU en el administrador de tareas

Un desarrollador debe aprender a utilizar la unidad de procesamiento de gráficos (GPU) del dispositivo de forma eficaz para que la aplicación no se ralentice ni realice trabajos innecesarios.

Configurar los ajustes de renderizado de GPU

Si su aplicación es lenta, significa que algunos o todos los fotogramas de actualización de la pantalla tardan más de 16 milisegundos en actualizarse. Para ver visualmente las actualizaciones de cuadros en la pantalla, puede habilitar una opción especial en el dispositivo (Representación de GPU de perfil).

Podrás ver rápidamente cuánto tiempo lleva renderizar los fotogramas. Permítame recordarle que debe mantenerlo dentro de los 16 milisegundos.

La opción está disponible en dispositivos a partir de Android 4.1. El modo desarrollador debe estar activado en el dispositivo. En dispositivos con versión 4.2 y superiores, el modo está oculto de forma predeterminada. Para activar vaya a Configuración | Sobre el telefono y haga clic en la línea siete veces Número de compilación.

Después de la activación, vaya a Opciones de desarrollador y encontrar el punto Configurar los ajustes de renderizado de GPU(Renderizado de GPU de perfil) que debe estar habilitado. En la ventana emergente, seleccione la opción En la pantalla en forma de columnas.(En pantalla como barras). En este caso, el gráfico se mostrará encima de la aplicación en ejecución.

Puede probar no solo su aplicación, sino también otras. Inicie cualquier aplicación y comience a trabajar con ella. Mientras trabaja, verá un gráfico actualizado en la parte inferior de la pantalla. El eje horizontal representa el tiempo transcurrido. El eje vertical muestra el tiempo de cada cuadro en milisegundos. Al interactuar con la aplicación, se dibujan barras verticales en la pantalla, que aparecen de izquierda a derecha, y muestran el rendimiento de los fotogramas a lo largo del tiempo. Cada una de estas columnas representa un cuadro para dibujar la pantalla. Cuanto mayor sea la altura de la columna, más tiempo llevará dibujar. La delgada línea verde es una guía y corresponde a 16 milisegundos por fotograma. Por lo tanto, debe esforzarse para asegurarse de que el gráfico no se desvíe más allá de esta línea al estudiar su solicitud.

Veamos una versión más grande del gráfico.

La línea verde es responsable de 16 milisegundos. Para mantenerse dentro de los 60 fotogramas por segundo, cada barra del gráfico debe dibujarse debajo de esta línea. En algún momento, la columna será demasiado grande y estará mucho más alta que la línea verde. Esto significa que el programa se está ralentizando. Cada columna tiene cian, morado (Lollipop y superiores), rojo y naranja.

El color azul es responsable del tiempo utilizado para crear y actualizar. Vista.

La parte violeta representa el tiempo dedicado a transferir los recursos de renderizado del hilo.

El color rojo representa el momento de dibujar.

El color naranja muestra cuánto tiempo le tomó a la CPU esperar a que la GPU completara su trabajo. Esta es la fuente de problemas en valores grandes.

Existen técnicas especiales para reducir la carga en la GPU.

Indicador de sobregiro de GPU de depuración

Otra configuración le permite saber con qué frecuencia se vuelve a dibujar la misma parte de la pantalla (es decir, se realiza trabajo adicional). Vamos otra vez Opciones de desarrollador y encontrar el punto Indicador de sobregiro de GPU de depuración(Depuración de sobregiro de GPU) que debería estar habilitado. En la ventana emergente, seleccione la opción Mostrar zonas superpuestas(Mostrar áreas sobregiradas). ¡No tengas miedo! Algunos elementos de la pantalla cambiarán de color.

Vuelve a cualquier aplicación y observa cómo funciona. El color indicará áreas problemáticas en su aplicación.

Si el color en la aplicación no ha cambiado, entonces todo está bien. No hay capas de un color sobre otro.

El color azul indica que se está dibujando una capa encima de la capa inferior. Bien.

Color verde: redibujado dos veces. Necesitas pensar en la optimización.

Color rosa: redibujado tres veces. Todo está muy mal.

Color rojo: redibujado muchas veces. Algo salió mal.

Puede comprobar su aplicación usted mismo para encontrar áreas problemáticas. Crear una actividad y colocar un componente en ella. Vista de texto. Dale al elemento raíz y a la etiqueta de texto algo de fondo en el atributo. Android: fondo. Obtendrá lo siguiente: primero, pintó la capa inferior de actividad con un color. Luego se dibuja una nueva capa encima de ella. Vista de texto. Por cierto, en realidad Vista de texto También se dibuja el texto.

En algunos puntos, no se puede evitar la superposición de colores. Pero imagina que configuras el fondo de la lista de la misma manera. Vista de la lista, que ocupa toda el área de actividad. El sistema cumplirá una doble función, aunque el usuario nunca verá la capa inferior de actividad. Y si, además, crea su propio marcado para cada elemento de la lista con su propio fondo, generalmente se exagerará.

Un pequeño consejo. Colocar después del método establecerContentView() llamando a un método que eliminará la pantalla para que no se pinte con el color del tema. Esto ayudará a eliminar una superposición de color adicional:

GetWindow().setBackgroundDrawable(nulo);

Uso de la informática GPU con C++ AMP

Hasta ahora, al analizar técnicas de programación paralela, hemos considerado sólo los núcleos del procesador. Hemos adquirido algunas habilidades para paralelizar programas en múltiples procesadores, sincronizar el acceso a recursos compartidos y usar primitivas de sincronización de alta velocidad sin usar bloqueos.

Sin embargo, existe otra forma de paralelizar programas: unidades de procesamiento de gráficos (GPU), teniendo más núcleos que incluso los procesadores de alto rendimiento. Los núcleos de GPU son excelentes para implementar algoritmos de procesamiento de datos paralelos, y su gran número compensa con creces las molestias de ejecutar programas en ellos. En este artículo nos familiarizaremos con una de las formas de ejecutar programas en una GPU, utilizando un conjunto de extensiones del lenguaje C++ llamado C++AMP.

Las extensiones AMP de C++ se basan en el lenguaje C++, por lo que este artículo mostrará ejemplos en C++. Sin embargo, con un uso moderado del mecanismo de interacción en. NET, puede utilizar algoritmos AMP de C++ en sus programas .NET. Pero hablaremos de esto al final del artículo.

Introducción a C++ AMP

Básicamente, una GPU es el mismo procesador que cualquier otro, pero con un conjunto especial de instrucciones, una gran cantidad de núcleos y su propio protocolo de acceso a la memoria. Sin embargo, existen grandes diferencias entre las GPU modernas y los procesadores convencionales, y comprenderlas es clave para crear programas que utilicen de manera eficiente la potencia informática. GPU.

    Las GPU modernas tienen un conjunto de instrucciones muy pequeño. Esto implica algunas limitaciones: falta de capacidad para llamar funciones, conjunto limitado de tipos de datos admitidos, falta de funciones de biblioteca y otras. Algunas operaciones, como las sucursales condicionales, pueden costar mucho más que operaciones similares realizadas en procesadores convencionales. Obviamente, mover grandes cantidades de código de la CPU a la GPU en tales condiciones requiere un esfuerzo significativo.

    La cantidad de núcleos en una GPU promedio es significativamente mayor que en un procesador convencional promedio. Sin embargo, algunas tareas son demasiado pequeñas o no se pueden dividir en partes lo suficientemente grandes como para beneficiarse de la GPU.

    El soporte de sincronización entre núcleos de GPU que realizan la misma tarea es muy deficiente y está completamente ausente entre núcleos de GPU que realizan diferentes tareas. Esta circunstancia requiere la sincronización del procesador gráfico con un procesador convencional.

Inmediatamente surge la pregunta: ¿qué tareas se pueden resolver en una GPU? Tenga en cuenta que no todos los algoritmos son adecuados para su ejecución en una GPU. Por ejemplo, las GPU no tienen acceso a dispositivos de E/S, por lo que no podrá mejorar el rendimiento de un programa que extrae fuentes RSS de Internet utilizando una GPU. Sin embargo, muchos algoritmos computacionales se pueden transferir a la GPU y paralelizarse masivamente. A continuación se muestran algunos ejemplos de dichos algoritmos (esta lista no está completa):

    aumento y disminución de la nitidez de las imágenes y otras transformaciones;

    transformada rápida de Fourier;

    transposición y multiplicación de matrices;

    clasificación de números;

    inversión de hash directa.

Una excelente fuente de ejemplos adicionales es el blog Microsoft Native Concurrency, que proporciona fragmentos de código y explicaciones para varios algoritmos implementados en C++ AMP.

C++ AMP es un marco incluido con Visual Studio 2012 que brinda a los desarrolladores de C++ una manera fácil de realizar cálculos en la GPU, que solo requiere un controlador DirectX 11. Microsoft ha lanzado C++ AMP como una especificación abierta que puede implementar cualquier proveedor de compiladores.

El marco C++ AMP le permite ejecutar código en aceleradores de gráficos, que son dispositivos informáticos. Utilizando el controlador DirectX 11, el marco C++ AMP detecta dinámicamente todos los aceleradores. C++ AMP también incluye un emulador de acelerador de software y un emulador basado en procesador convencional, WARP, que sirve como respaldo en sistemas sin GPU o con GPU, pero carece de un controlador DirectX 11 y utiliza múltiples núcleos e instrucciones SIMD.

Ahora comencemos a explorar un algoritmo que se puede paralelizar fácilmente para su ejecución en una GPU. La siguiente implementación toma dos vectores de igual longitud y calcula el resultado puntual. Es difícil imaginar algo más sencillo:

Void VectorAddExpPointwise(flotante* primero, flotante* segundo, flotante* resultado, longitud int) (for (int i = 0; i< length; ++i) { result[i] = first[i] + exp(second[i]); } }

Para paralelizar este algoritmo en un procesador normal, es necesario dividir el rango de iteración en varios subrangos y ejecutar un subproceso de ejecución para cada uno de ellos. Dedicamos bastante tiempo en artículos anteriores exactamente a este método de paralelizar nuestro primer ejemplo de búsqueda. números primos- Hemos visto cómo podemos hacer esto creando subprocesos manualmente, pasando trabajos a un grupo de subprocesos y usando Parallel.For y PLINQ para paralelizar automáticamente. Recuerde también que al paralelizar algoritmos similares en un procesador convencional, tuvimos especial cuidado de no dividir el problema en tareas demasiado pequeñas.

Para la GPU, estas advertencias no son necesarias. Las GPU tienen múltiples núcleos que ejecutan subprocesos muy rápidamente y el costo del cambio de contexto es significativamente menor que el de los procesadores convencionales. A continuación se muestra un fragmento que intenta utilizar la función. paralelo_para_cada desde el marco C++ AMP:

#incluir #incluir usando concurrencia de espacios de nombres; void VectorAddExpPointwise(flotante* primero, flotante* segundo, flotante* resultado, longitud int) ( array_view avFirst(longitud, primero); vista_matriz avSecond(longitud, segundo); vista_matriz avResult(longitud, resultado); avResult.discard_data(); paralelo_para_cada(avResult.extent, [=](índice<1>i) restringir(amp) ( avResult[i] = avFirst[i] + fast_math::exp(avSecond[i]); )); avResult.synchronize(); )

Ahora examinemos cada parte del código por separado. Observemos de inmediato que se ha conservado la forma general del bucle principal, pero el bucle for utilizado originalmente ha sido reemplazado por una llamada a la función paralelo_for_each. De hecho, el principio de convertir un bucle en una función o llamada a un método no es nuevo para nosotros; dicha técnica se ha demostrado anteriormente utilizando los métodos Parallel.For() y Parallel.ForEach() de la biblioteca TPL.

A continuación, los datos de entrada (parámetros primero, segundo y resultado) se empaquetan con instancias. vista_matriz. La clase array_view se utiliza para empaquetar los datos pasados ​​a la GPU (acelerador). Su parámetro de plantilla especifica el tipo de datos y su dimensión. Para ejecutar instrucciones en una GPU que accede a datos originalmente procesados ​​en una CPU convencional, alguien o algo debe encargarse de copiar los datos a la GPU porque la mayoría de las tarjetas gráficas modernas son dispositivos separados con su propia memoria. Las instancias array_view resuelven este problema: proporcionan copia de datos bajo demanda y solo cuando es realmente necesario.

Cuando la GPU completa la tarea, los datos se vuelven a copiar. Al crear una instancia de array_view con un argumento constante, nos aseguramos de que el primero y el segundo se copien en la memoria de la GPU, pero no se vuelvan a copiar. Asimismo, llamando descartar_datos(), excluimos copiar el resultado de la memoria de un procesador normal a la memoria del acelerador, pero estos datos se copiarán en la dirección opuesta.

La función paralelo_for_each toma un objeto de extensión que especifica la forma de los datos que se procesarán y una función que se aplicará a cada elemento del objeto de extensión. En el ejemplo anterior, utilizamos una función lambda, cuyo soporte apareció en el estándar ISO C++2011 (C++11). La palabra clave restrict (amp) indica al compilador que verifique si el cuerpo de la función se puede ejecutar en la GPU y deshabilita la mayor parte de la sintaxis de C++ que no se puede compilar en instrucciones de GPU.

Parámetro de función lambda, índice<1>objeto, representa un índice unidimensional. Debe coincidir con el objeto de extensión que se utiliza: si declaramos que el objeto de extensión es bidimensional (por ejemplo, definiendo la forma de los datos de origen como una matriz bidimensional), el índice también tendría que ser dos -dimensional. A continuación se ofrece un ejemplo de tal situación.

Finalmente, la llamada al método sincronizar() al final del método VectorAddExpPointwise, garantiza que los resultados del cálculo de array_view avResult, producido por la GPU, se copien nuevamente en la matriz de resultados.

Con esto concluye nuestra primera introducción al mundo de C++ AMP, y ahora estamos listos para una investigación más detallada, así como para ejemplos más interesantes que demuestran los beneficios de usar computación paralela en una GPU. La suma de vectores no es un buen algoritmo y no es el mejor candidato para demostrar el uso de GPU debido a la gran sobrecarga que supone copiar datos. La siguiente subsección mostrará dos ejemplos más interesantes.

Multiplicación de matrices

El primer ejemplo "real" que veremos es la multiplicación de matrices. Para la implementación, tomaremos un algoritmo simple de multiplicación de matrices cúbicas, y no el algoritmo de Strassen, que tiene un tiempo de ejecución cercano a ~O cúbico (n 2,807). Dadas dos matrices, una matriz A de m x w y una matriz B de w x n, el siguiente programa las multiplicará y devolverá el resultado, una matriz C de m x n:

Matriz vacíaMultiplicar(int* A, int m, int w, int* B, int n, int* C) ( para (int i = 0; i< m; ++i) { for (int j = 0; j < n; ++j) { int sum = 0; for (int k = 0; k < w; ++k) { sum += A * B; } C = sum; } } }

Hay varias formas de paralelizar esta implementación, y si desea paralelizar este código para ejecutarlo en un procesador normal, la opción correcta sería paralelizar el bucle externo. Sin embargo, la GPU tiene una cantidad bastante grande de núcleos y, al paralelizar solo el bucle externo, no podremos crear una cantidad suficiente de trabajos para cargar todos los núcleos con trabajo. Por lo tanto, tiene sentido paralelizar los dos bucles exteriores, dejando el bucle interior intacto:

Matriz vacíaMultiplicar (int* A, int m, int w, int* B, int n, int* C) ( array_view avA(m, w, A); vista_matriz avB(w, n, B); vista_matriz avC(m, n, C); avC.discard_data(); paralelo_para_cada(avC.extent, [=](índice<2>idx) restringir(amp) ( int suma = 0; for (int k = 0; k< w; ++k) { sum + = avA(idx*w, k) * avB(k*w, idx); } avC = sum; }); }

Esta implementación todavía se parece mucho a la implementación secuencial de la multiplicación de matrices y al ejemplo de suma de vectores dado anteriormente, con la excepción del índice, que ahora es bidimensional y accesible en el bucle interno mediante el operador. ¿Cuánto más rápida es esta versión que la alternativa secuencial que se ejecuta en un procesador normal? Multiplicando dos matrices (enteros) de tamaño 1024 x 1024, la versión secuencial en una CPU normal tarda una media de 7350 milisegundos, mientras que la versión GPU (agárrate fuerte) tarda 50 milisegundos, ¡147 veces más rápido!

Simulación de movimiento de partículas.

Los ejemplos de resolución de problemas de GPU presentados anteriormente tienen una implementación muy simple del bucle interno. Está claro que este no será siempre el caso. El blog Native Concurrency, vinculado anteriormente, muestra un ejemplo de modelado de interacciones gravitacionales entre partículas. La simulación implica un número infinito de pasos; en cada paso, se calculan nuevos valores de los elementos del vector de aceleración para cada partícula y luego se determinan sus nuevas coordenadas. Aquí, el vector de partículas está paralelizado: con una cantidad suficientemente grande de partículas (de varios miles y más), puede crear una cantidad suficientemente grande de tareas para cargar todos los núcleos de GPU con trabajo.

La base del algoritmo es la implementación para determinar el resultado de las interacciones entre dos partículas, como se muestra a continuación, que se puede transferir fácilmente a la GPU:

// aquí float4 son vectores con cuatro elementos // que representan las partículas involucradas en las operaciones void bodybody_interaction (float4& aceleración, const float4 p1, const float4 p2) restrict(amp) ( float4 dist = p2 – p1; // no se usa aquí float absDist = dist.x*dist.x + dist.y*dist.y + dist.z*dist.z; float invDist = 1.0f / sqrt(absDist); float invDistCube = invDist*invDist = dist*; PARTICLE_MASS*invDistCube)

Los datos iniciales en cada paso del modelado son una matriz con las coordenadas y velocidades de las partículas y, como resultado de los cálculos, se crea una nueva matriz con las coordenadas y velocidades de las partículas:

Partícula de estructura (posición float4, velocidad; // implementaciones de constructor, constructor de copia y // operador = con restricción (amp) omitida para ahorrar espacio); vacío simulación_paso (matriz & anterior, matriz & siguiente, int cuerpos) (extensión<1>text(cuerpos); paralelo_para_cada (ext, [&](índice<1>idx) restringir(amp) ( partícula p = anterior; aceleración float4(0, 0, 0, 0); for (int cuerpo = 0; cuerpo< bodies; ++body) { bodybody_interaction (acceleration, p.position, previous.position); } p.velocity + = acceleration*DELTA_TIME; p.position + = p.velocity*DELTA_TIME; next = p; }); }

Con la ayuda de una interfaz gráfica adecuada, el modelado puede resultar muy interesante. El ejemplo completo proporcionado por el equipo de C++ AMP se puede encontrar en el blog de Native Concurrency. En mi sistema con un procesador Intel Core i7 y una tarjeta gráfica Geforce GT 740M, la simulación de 10.000 partículas se ejecuta a ~2,5 fps (pasos por segundo) usando la versión secuencial ejecutándose en el procesador normal y a 160 fps usando la versión optimizada ejecutándose. en la GPU: un enorme aumento en el rendimiento.

Antes de concluir esta sección, hay una característica más importante del marco AMP de C++ que puede mejorar aún más el rendimiento del código que se ejecuta en la GPU. Soporte de GPU caché de datos programable(llamado a menudo memoria compartida). Los valores almacenados en este caché son compartidos por todos los subprocesos de ejecución en un solo mosaico. Gracias al mosaico de memoria, los programas basados ​​en el marco C++ AMP pueden leer datos de la memoria de la tarjeta gráfica en la memoria compartida del mosaico y luego acceder a ellos desde múltiples subprocesos de ejecución sin volver a recuperar los datos de la memoria de la tarjeta gráfica. Acceder a la memoria compartida en mosaico es aproximadamente 10 veces más rápido que la memoria de la tarjeta gráfica. En otras palabras, tienes motivos para seguir leyendo.

Para proporcionar una versión en mosaico del bucle paralelo, se pasa el método paralelo_for_each dominio en mosaico_extensión, que divide el objeto de extensión multidimensional en mosaicos multidimensionales, y el parámetro lambda Tiled_index, que especifica el ID global y local del subproceso dentro del mosaico. Por ejemplo, una matriz de 16x16 se puede dividir en mosaicos de 2x2 (como se muestra en la imagen a continuación) y luego pasar a la función paralelo_for_each:

Medida<2>matriz(16,16); extensión_enlosado<2,2>TiledMatrix = matriz.tile<2,2>(); paralelo_para_cada(tiledMatrix, [=](tiled_index<2,2>idx) restringir(amp) ( // ... ));

Cada uno de los cuatro hilos de ejecución pertenecientes a un mismo mosaico puede compartir los datos almacenados en el bloque.

Al realizar operaciones con matrices, en el núcleo de la GPU, en lugar del índice estándar<2>, como en los ejemplos anteriores, puedes usar idx.global. El uso adecuado de la memoria en mosaico local y de los índices locales puede proporcionar mejoras de rendimiento significativas. Para declarar la memoria en mosaico compartida por todos los subprocesos de ejecución en un solo mosaico, las variables locales se pueden declarar con el especificador Tile_static.

En la práctica, se suele utilizar la técnica de declarar memoria compartida e inicializar sus bloques individuales en diferentes hilos de ejecución:

Paralelo_para_cada(tiledMatrix, [=](tiled_index<2,2>idx) restrict(amp) ( // 32 bytes son compartidos por todos los subprocesos en el bloque Tile_static int local; // asigna un valor al elemento para este subproceso de ejecución local = 42; ));

Obviamente, los beneficios del uso de la memoria compartida sólo se pueden obtener si el acceso a esta memoria está sincronizado; es decir, los subprocesos no deben acceder a la memoria hasta que uno de ellos la haya inicializado. La sincronización de hilos en un mosaico se realiza mediante objetos. barrera_azulejos(que recuerda a la clase Barrier de la biblioteca TPL): podrán continuar la ejecución solo después de llamar al método Tile_barrier.Wait(), que devolverá el control solo cuando todos los subprocesos hayan llamado a Tile_barrier.Wait. Por ejemplo:

Paralelo_para_cada(tiledMatrix, (tiled_index<2,2>idx) restrict(amp) ( // 32 bytes son compartidos por todos los subprocesos en el bloque Tile_static int local; // asigna un valor al elemento para este subproceso de ejecución local = 42; // idx.barrier es una instancia de Tile_barrier idx.barrier.wait(); // Ahora este hilo puede acceder a la matriz "local", // utilizando los índices de otros hilos de ejecución ));

Ahora es el momento de traducir lo que has aprendido en un ejemplo concreto. Volvamos a la implementación de la multiplicación de matrices, realizada sin el uso de organización de memoria en mosaico, y agreguemos la optimización descrita. Supongamos que el tamaño de la matriz es múltiplo de 256; esto nos permitirá trabajar con bloques de 16 x 16. La naturaleza de las matrices permite la multiplicación bloque por bloque y podemos aprovechar esta característica (de hecho, dividir). matrices en bloques es una optimización típica del algoritmo de multiplicación de matrices, que proporciona un uso más eficiente de la memoria caché de la CPU).

La esencia de esta técnica se reduce a lo siguiente. Para encontrar C i,j (el elemento en la fila i y la columna j en la matriz de resultados), necesita calcular el producto escalar entre A i,* (i-ésima fila de la primera matriz) y B *,j (j -ésima columna de la segunda matriz). Sin embargo, esto equivale a calcular los productos escalares parciales de la fila y la columna y luego sumar los resultados. Podemos usar este hecho para convertir el algoritmo de multiplicación de matrices en una versión en mosaico:

Matriz vacíaMultiplicar(int* A, int m, int w, int* B, int n, int* C) ( array_view avA(m, w, A); vista_matriz avB(w, n, B); vista_matriz avC(m, n, C); avC.discard_data(); paralelo_para_cada(avC.extent.tile<16,16>(), [=](tiled_index<16,16>idx) restringir(amp) ( int suma = 0; int localRow = idx.local, localCol = idx.local; for (int k = 0; k

La esencia de la optimización descrita es que cada hilo en el mosaico (se crean 256 hilos para un bloque de 16 x 16) inicializa su elemento en 16 x 16 copias locales de fragmentos de las matrices originales A y B. Cada hilo en el mosaico requiere solo una fila y una columna de estos bloques, pero todos los hilos juntos accederán a cada fila y cada columna 16 veces. Este enfoque reduce significativamente el número de accesos a la memoria principal.

Para calcular el elemento (i,j) en la matriz de resultados, el algoritmo requiere la i-ésima fila completa de la primera matriz y la j-ésima columna de la segunda matriz. Cuando los subprocesos son mosaicos de 16x16 representados en el diagrama y k = 0, las regiones sombreadas en la primera y segunda matrices se leerán en la memoria compartida. El elemento informático del hilo de ejecución (i,j) en la matriz de resultados calculará el producto escalar parcial de los primeros k elementos de la i-ésima fila y j-ésima columna de las matrices originales.

En este ejemplo, el uso de una organización en mosaico proporciona un enorme aumento del rendimiento. La versión en mosaico de la multiplicación de matrices es mucho más rápida que la versión simple, ya que tarda aproximadamente 17 milisegundos (para las mismas matrices de entrada de 1024 x 1024), ¡que es 430 veces más rápida que la versión que se ejecuta en un procesador convencional!

Antes de finalizar nuestra discusión sobre el marco AMP de C++, nos gustaría mencionar las herramientas (en Visual Studio) disponibles para los desarrolladores. Visual Studio 2012 ofrece un depurador de unidad de procesamiento de gráficos (GPU) que le permite establecer puntos de interrupción, examinar la pila de llamadas y leer y cambiar valores de variables locales (algunos aceleradores admiten la depuración de GPU directamente; para otros, Visual Studio utiliza un simulador de software) y un generador de perfiles que le permite evaluar los beneficios que recibe una aplicación al paralelizar operaciones mediante una GPU. Para obtener más información sobre las capacidades de depuración en Visual Studio, consulte el artículo Tutorial. Depuración de una aplicación C++ AMP" en MSDN.

Alternativas de computación GPU en .NET

Si bien hasta ahora este artículo solo ha mostrado ejemplos en C++, existen varias formas de aprovechar el poder de la GPU en aplicaciones administradas. Una forma es utilizar herramientas de interoperabilidad que le permitan descargar el trabajo con núcleos de GPU a componentes C++ de bajo nivel. Esta solución es excelente para aquellos que desean usar el marco C++ AMP o tienen la capacidad de usar componentes C++ AMP prediseñados en aplicaciones administradas.

Otra forma es utilizar una biblioteca que funcione directamente con la GPU desde código administrado. Actualmente existen varias bibliotecas de este tipo. Por ejemplo, GPU.NET y CUDAfy.NET (ambas ofertas comerciales). A continuación se muestra un ejemplo del repositorio GPU.NET GitHub que demuestra la implementación del producto escalar de dos vectores:

void estático público MultiplyAddGpu(doble a, doble b, doble c) ( int ThreadId = BlockDimension.X * BlockIndex.X + ThreadIndex.X; int TotalThreads = BlockDimension.X * GridDimension.X; for (int ElementIdx = ThreadId; ElementIdx

Soy de la opinión de que es mucho más fácil y eficiente aprender una extensión de lenguaje (basada en C++ AMP) que intentar orquestar interacciones a nivel de biblioteca o realizar cambios significativos en el lenguaje IL.

Entonces, después de analizar las posibilidades de la programación paralela en .NET y el uso de GPU, nadie duda de que organizar la computación paralela es una forma importante de aumentar la productividad. En muchos servidores y estaciones de trabajo de todo el mundo, la inestimable potencia de procesamiento de las CPU y GPU no se utiliza porque las aplicaciones simplemente no la utilizan.

La biblioteca paralela de tareas nos brinda una oportunidad única de incluir todos los núcleos de CPU disponibles, aunque esto requerirá resolver algunos problemas interesantes de sincronización, fragmentación excesiva de tareas y distribución desigual del trabajo entre subprocesos de ejecución.

El marco C++ AMP y otras bibliotecas de computación paralela de GPU multipropósito se pueden utilizar con éxito para paralelizar cálculos en cientos de núcleos de GPU. Finalmente, existe una oportunidad previamente inexplorada de obtener ganancias de productividad mediante el uso de tecnologías de computación distribuida en la nube, que recientemente se han convertido en una de las principales direcciones en el desarrollo de la tecnología de la información.

Computación GPU

La tecnología CUDA (Compute Unified Device Architecture) es una arquitectura de hardware y software que permite la computación utilizando procesadores gráficos NVIDIA que admiten la tecnología GPGPU (computación aleatoria 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, ION, Quadro y Tesla.

CUDA SDK permite a los programadores implementar algoritmos ejecutables en las GPU NVIDIA en un dialecto simplificado especial del lenguaje de programación C e incluir funciones especiales en el texto de un programa C. CUDA 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.

Historia

En 2003, Intel y AMD estaban en una carrera conjunta para encontrar el procesador más potente. Durante varios años, como resultado de esta carrera, las velocidades de reloj aumentaron significativamente, especialmente después del lanzamiento del Intel Pentium 4.

Tras el aumento de las frecuencias de reloj (entre 2001 y 2003, la frecuencia de reloj del Pentium 4 se duplicó de 1,5 a 3 GHz), los usuarios tuvieron que contentarse con décimas de gigahercios, que fueron introducidas en el mercado por los fabricantes (de 2003 a En 2005, las frecuencias de reloj aumentaron de 3 a 3,8 GHz).

Las arquitecturas optimizadas para altas frecuencias de reloj, como Prescott, también comenzaron a experimentar dificultades, y no sólo de producción. Los fabricantes de chips enfrentan desafíos para superar las leyes de la física. Algunos analistas incluso predijeron que la Ley de Moore dejaría de aplicarse. Pero eso no sucedió. El significado original de la ley a menudo se distorsiona, pero se refiere al número de transistores en la superficie del núcleo de silicio. Durante mucho tiempo, un aumento en el número de transistores en una CPU iba acompañado de un aumento correspondiente en el rendimiento, lo que conducía a una distorsión del significado. Pero luego la situación se complicó más. Los desarrolladores de la arquitectura de la CPU se acercaron a la ley de reducción del crecimiento: la cantidad de transistores que era necesario agregar para el aumento requerido en el rendimiento se hizo cada vez mayor, lo que llevó a un callejón sin salida.

La razón por la que los fabricantes de GPU no se han encontrado con este problema es muy simple: las CPU están diseñadas para obtener el máximo rendimiento en un flujo de instrucciones que procesan diferentes datos (tanto números enteros como de coma flotante), realizan acceso aleatorio a la memoria, etc. Hasta ahora, los desarrolladores intentan proporcionar un mayor paralelismo de instrucciones, es decir, ejecutar tantas instrucciones como sea posible en paralelo. Por ejemplo, con el Pentium apareció la ejecución superescalar, cuando bajo ciertas condiciones era posible ejecutar dos instrucciones por ciclo de reloj. Pentium Pro recibió ejecución de instrucciones desordenada, lo que permitió optimizar el funcionamiento de las unidades informáticas. El problema es que existen limitaciones obvias para ejecutar un flujo secuencial de instrucciones en paralelo, por lo que aumentar ciegamente el número de unidades computacionales no proporciona ningún beneficio ya que seguirán inactivas la mayor parte del tiempo.

El funcionamiento de la GPU es relativamente sencillo. Consiste en tomar un grupo de polígonos de un lado y generar un grupo de píxeles del otro. Los polígonos y los píxeles son independientes entre sí, por lo que pueden procesarse en paralelo. Así, en una GPU es posible destinar una gran parte del cristal a unidades computacionales que, a diferencia de la CPU, realmente se utilizarán.

La GPU se diferencia de la CPU no sólo en este aspecto. El acceso a la memoria en la GPU está muy acoplado: si se lee un texel, después de algunos ciclos de reloj se leerá el texel vecino; Cuando se graba un píxel, después de algunos ciclos de reloj se grabará el vecino. Al organizar la memoria de forma inteligente, puede lograr un rendimiento cercano al rendimiento teórico. Esto significa que la GPU, a diferencia de la CPU, no requiere un caché enorme, ya que su función es acelerar las operaciones de texturizado. Todo lo que se necesita son unos pocos kilobytes que contengan algunos texels utilizados en filtros bilineales y trilineales.

Primeros cálculos sobre GPU

Los primeros intentos de este tipo de aplicaciones se limitaron al uso de determinadas funciones de hardware, como la rasterización y el almacenamiento en búfer Z. Pero en el siglo actual, con la llegada de los sombreadores, los cálculos matriciales comenzaron a acelerarse. En 2003, en SIGGRAPH, se asignó una sección separada para la computación GPU, y se llamó GPGPU (computación de propósito general en GPU).

El más conocido es BrookGPU, un compilador del lenguaje de programación de streaming Brook, diseñado para realizar cálculos no gráficos en la GPU. Antes de su aparición, los desarrolladores que utilizaban las capacidades de los chips de vídeo para realizar cálculos elegían una de dos API comunes: Direct3D u OpenGL. Esto limitó seriamente el uso de GPU, porque los gráficos 3D utilizan sombreadores y texturas que los especialistas en programación paralela no necesitan conocer; utilizan subprocesos y núcleos; Brook pudo ayudar a facilitar su tarea. Estas extensiones de transmisión del lenguaje C, desarrolladas en la Universidad de Stanford, ocultaron la API 3D a los programadores y presentaron el chip de video como un coprocesador paralelo. El compilador procesó el archivo .br con código y extensiones C++, produciendo código vinculado a una biblioteca habilitada para DirectX, OpenGL o x86.

La aparición de Brook despertó el interés de NVIDIA y ATI y posteriormente abrió un sector completamente nuevo: las computadoras paralelas basadas en chips de video.

Posteriormente, algunos investigadores del proyecto Brook se unieron al equipo de desarrollo de NVIDIA para introducir una estrategia de computación paralela de hardware y software, abriendo una nueva cuota de mercado. Y la principal ventaja de esta iniciativa de NVIDIA es que los desarrolladores conocen todas las capacidades de sus GPU hasta el último detalle, y no necesitan utilizar la API gráfica, y pueden trabajar con el hardware directamente usando el controlador. El resultado de los esfuerzos de este equipo fue NVIDIA CUDA.

Áreas de aplicación de cálculos paralelos en GPU

Al transferir cálculos a la GPU, muchas tareas logran una aceleración de 5 a 30 veces en comparación con los rápidos procesadores universales. Los números más grandes (¡del orden de 100x de aceleración o incluso más!) se logran con código que no es muy adecuado para cálculos que utilizan bloques SSE, pero que es bastante conveniente para GPU.

Estos son solo algunos ejemplos de aceleraciones para código sintético en la GPU versus código vectorizado SSE en la CPU (según NVIDIA):

Microscopía de fluorescencia: 12x.

Dinámica molecular (cálculo de fuerza no enlazada): 8-16x;

Electrostática (suma de Coulomb directa y multinivel): 40-120x y 7x.

Una tabla que NVIDIA muestra en todas las presentaciones muestra la velocidad de las GPU en relación con las CPU.

Lista de las principales aplicaciones en las que se utiliza la computación GPU: análisis y procesamiento de imágenes y señales, simulación física, matemáticas computacionales, biología computacional, cálculos financieros, bases de datos, dinámica de gases y líquidos, criptografía, radioterapia adaptativa, astronomía, procesamiento de audio , bioinformática, simulaciones biológicas, visión por computadora, minería de datos, cine y televisión digitales, simulaciones electromagnéticas, sistemas de información geográfica, aplicaciones militares, planificación minera, dinámica molecular, imágenes por resonancia magnética (MRI), redes neuronales, investigación oceanográfica, física de partículas, proteínas. simulación de plegado, química cuántica, trazado de rayos, visualización, radar, simulación de yacimientos, inteligencia artificial, análisis de datos satelitales, exploración sísmica, cirugía, ultrasonido, videoconferencia.

Ventajas y limitaciones de CUDA

Desde la perspectiva de un programador, una canalización de gráficos es una colección de etapas de procesamiento. El bloque de geometría genera los triángulos y el bloque de rasterización genera los píxeles que se muestran en el monitor. El modelo de programación tradicional de GPGPU se ve así:

Para transferir cálculos a la GPU dentro de este modelo, se necesita un enfoque especial. Incluso la suma de dos vectores por elementos requerirá dibujar la figura en la pantalla o en un búfer fuera de la pantalla. La figura está rasterizada, el color de cada píxel se calcula utilizando un programa determinado (pixel shader). El programa lee los datos de entrada de las texturas para cada píxel, los agrega y los escribe en el búfer de salida. ¡Y todas estas numerosas operaciones son necesarias para algo que está escrito en un solo operador en un lenguaje de programación normal!

Por lo tanto, el uso de GPGPU para computación de propósito general tiene la limitación de ser demasiado difícil de capacitar a los desarrolladores. Y hay suficientes otras restricciones, porque un sombreador de píxeles es solo una fórmula para la dependencia del color final de un píxel de sus coordenadas, y el lenguaje de sombreadores de píxeles es un lenguaje para escribir estas fórmulas con una sintaxis similar a C. Los primeros métodos GPGPU son un truco ingenioso que le permite utilizar la potencia de la GPU, pero sin ninguna comodidad. Los datos allí están representados por imágenes (texturas) y el algoritmo está representado por el proceso de rasterización. De particular interés es el modelo muy específico de memoria y ejecución.

La arquitectura de software y hardware de NVIDIA para la computación GPU se diferencia de los modelos GPGPU anteriores en que le permite escribir programas para la GPU en lenguaje C real con sintaxis estándar, punteros y la necesidad de un mínimo de extensiones para acceder a los recursos informáticos de los chips de video. CUDA es independiente de las API de gráficos y tiene algunas características diseñadas específicamente para informática de propósito general.

Ventajas de CUDA sobre el enfoque tradicional de la informática GPGPU

CUDA proporciona acceso a 16 KB de memoria compartida por subprocesos por multiprocesador, que se puede utilizar para organizar un caché con mayor ancho de banda que las recuperaciones de texturas;

Transferencia de datos más eficiente entre el sistema y la memoria de video;

No se necesitan API de gráficos con redundancia y gastos generales;

Memoria lineal de direccionamiento, recopilación y dispersión, capacidad de escribir en direcciones arbitrarias;

Soporte de hardware para operaciones con números enteros y bits.

Principales limitaciones de CUDA:

Falta de soporte de recursividad para funciones ejecutables;

El ancho mínimo del bloque es de 32 hilos;

Arquitectura CUDA cerrada propiedad de NVIDIA.

Las debilidades de la programación con métodos GPGPU anteriores son que estos métodos no utilizan unidades de ejecución de sombreador de vértices en arquitecturas no unificadas anteriores, los datos se almacenan en texturas y se envían a un búfer fuera de la pantalla, y los algoritmos de múltiples pasadas utilizan unidades de sombreado de píxeles. Las limitaciones de GPGPU pueden incluir: uso insuficiente de las capacidades del hardware, limitaciones del ancho de banda de la memoria, falta de operación de dispersión (solo recopilación), uso obligatorio de la API de gráficos.

Las principales ventajas de CUDA sobre los métodos GPGPU anteriores se derivan del hecho de que la arquitectura está diseñada para hacer un uso eficiente de la computación no gráfica en la GPU y utiliza el lenguaje de programación C sin requerir que los algoritmos se transfieran a una forma amigable con el concepto de canalización de gráficos. . CUDA ofrece un nuevo camino hacia la computación GPU que no utiliza API de gráficos y ofrece acceso aleatorio a la memoria (dispersión o recopilación). Esta arquitectura no tiene las desventajas de GPGPU y utiliza todas las unidades de ejecución, y también amplía las capacidades mediante matemáticas enteras y operaciones de desplazamiento de bits.

CUDA abre algunas capacidades de hardware que no están disponibles en las API de gráficos, como la memoria compartida. Se trata de una memoria pequeña (16 kilobytes por multiprocesador) a la que tienen acceso los bloques de subprocesos. Le permite almacenar en caché los datos a los que se accede con más frecuencia y puede proporcionar velocidades más rápidas que el uso de búsquedas de texturas para esta tarea. Lo que, a su vez, reduce la sensibilidad del rendimiento de los algoritmos paralelos en muchas aplicaciones. Por ejemplo, es útil para álgebra lineal, transformada rápida de Fourier y filtros de procesamiento de imágenes.

El acceso a la memoria también es más conveniente en CUDA. El código API de gráficos genera datos como 32 valores de punto flotante de precisión simple (valores RGBA simultáneamente en ocho objetivos de renderizado) en áreas predefinidas, y CUDA admite escritura dispersa: un número ilimitado de registros en cualquier dirección. Estas ventajas permiten ejecutar algunos algoritmos en la GPU que no se pueden implementar de manera eficiente utilizando métodos GPGPU basados ​​en API de gráficos.

Además, las API de gráficos necesariamente almacenan datos en texturas, lo que requiere un empaquetado preliminar de grandes matrices en texturas, lo que complica el algoritmo y obliga al uso de direccionamiento especial. Y CUDA le permite leer datos en cualquier dirección. Otra ventaja de CUDA es el intercambio de datos optimizado entre la CPU y la GPU. Y para los desarrolladores que desean acceso de bajo nivel (por ejemplo, cuando escriben otro lenguaje de programación), CUDA ofrece capacidades de programación en lenguaje ensamblador de bajo nivel.

Desventajas de CUDA

Una de las pocas desventajas de CUDA es su escasa portabilidad. Esta arquitectura sólo funciona en chips de vídeo de esta empresa, y no en todos, sino empezando por las series GeForce 8 y 9 y las correspondientes Quadro, ION y Tesla. NVIDIA cita una cifra de 90 millones de chips de vídeo compatibles con CUDA.

Alternativas CUDA

Un marco para escribir programas informáticos relacionados con la computación paralela en varios gráficos y procesadores centrales. El marco OpenCL incluye un lenguaje de programación basado en el estándar C99 y una interfaz de programación de aplicaciones (API). OpenCL proporciona paralelismo a nivel de instrucción y de datos y es una implementación de la técnica GPGPU. OpenCL es un estándar completamente abierto y su uso está libre de regalías.

El objetivo de OpenCL es complementar OpenGL y OpenAL, que son estándares industriales abiertos para audio y gráficos por computadora en 3D, aprovechando la potencia de la GPU. OpenCL es desarrollado y mantenido por el consorcio sin fines de lucro Khronos Group, que incluye muchas empresas grandes, incluidas Apple, AMD, Intel, nVidia, Sun Microsystems, Sony Computer Entertainment y otras.

CAL/IL (Capa de abstracción informática/Lenguaje intermedio)

La tecnología ATI Stream es un conjunto de tecnologías de hardware y software que permiten utilizar las GPU AMD junto con una CPU para acelerar muchas aplicaciones (no solo gráficos).

Las aplicaciones de ATI Stream incluyen aplicaciones computacionalmente intensivas, como análisis financiero o procesamiento de datos sísmicos. El uso de un procesador de flujo permitió aumentar 55 veces la velocidad de algunos cálculos financieros en comparación con la solución del mismo problema utilizando solo el procesador central.

NVIDIA no considera que la tecnología ATI Stream sea un competidor muy fuerte. CUDA y Stream son dos tecnologías diferentes que se encuentran en diferentes niveles de desarrollo. La programación de productos ATI es mucho más compleja: su lenguaje se parece más al lenguaje ensamblador. CUDA C, a su vez, es un lenguaje de nivel mucho más alto. Escribir en él es más cómodo y sencillo. Esto es muy importante para las grandes empresas promotoras. Si hablamos de rendimiento, podemos ver que su valor máximo en los productos ATI es mayor que en las soluciones NVIDIA. Pero nuevamente todo se reduce a cómo conseguir este poder.

DirectX11 (DirectCompute)

Una interfaz de programación de aplicaciones que forma parte de DirectX, un conjunto de API de Microsoft diseñado para ejecutarse en computadoras compatibles con IBM PC que ejecutan sistemas operativos Microsoft Windows. DirectCompute está diseñado para realizar computación de propósito general en GPU, una implementación del concepto GPGPU. DirectCompute se publicó originalmente como parte de DirectX 11, pero luego estuvo disponible para DirectX 10 y DirectX 10.1.

NVDIA CUDA en la comunidad científica rusa.

En diciembre de 2009, el modelo de software CUDA se enseña en 269 universidades de todo el mundo. En Rusia, se imparten cursos de formación sobre CUDA en las universidades estatales de Moscú, San Petersburgo, Kazán, Novosibirsk y Perm, la Universidad Internacional de la Naturaleza de la Sociedad y el Hombre "Dubna", el Instituto Conjunto de Investigaciones Nucleares, el Instituto de Electrónica de Moscú Tecnología, Universidad Estatal de Energía de Ivanovo, BSTU. V. G. Shukhov, MSTU im. Bauman, Universidad Técnica Química de Rusia que lleva su nombre. Mendeleev, Centro Científico Ruso "Instituto Kurchatov", Centro Interregional de Supercomputadoras de la Academia de Ciencias de Rusia, Instituto Tecnológico Taganrog (TTI SFU).

Una vez tuve la oportunidad de hablar en el mercado de la informática con el director técnico de una de las muchas empresas que venden portátiles. Este “especialista” intentó explicarme, echando espuma por la boca, exactamente qué configuración de portátil necesitaba. El mensaje principal de su monólogo fue que la época de las unidades centrales de procesamiento (CPU) se acabó y ahora todas las aplicaciones utilizan activamente cálculos en el procesador gráfico (GPU) y, por lo tanto, el rendimiento de una computadora portátil depende completamente de la GPU, y usted No es necesario prestar atención a la atención de la CPU. Al darme cuenta de que discutir y tratar de razonar con este director técnico era absolutamente inútil, no perdí el tiempo y compré el portátil que necesitaba en otro pabellón. Sin embargo, el hecho mismo de una incompetencia tan flagrante del vendedor me llamó la atención. Sería comprensible que intentara engañarme como comprador. De nada. Creía sinceramente en lo que decía. Sí, aparentemente, los especialistas en marketing de NVIDIA y AMD no se comen el pan y lograron inculcar en algunos usuarios la idea del papel dominante de un procesador gráfico en una computadora moderna.

El hecho de que la informática con unidades de procesamiento gráfico (GPU) se esté volviendo cada vez más popular hoy en día está fuera de toda duda. Sin embargo, esto no disminuye en absoluto el papel del procesador central. Es más, si hablamos de la gran mayoría de aplicaciones de usuario, hoy en día su rendimiento depende enteramente del rendimiento de la CPU. Es decir, la gran mayoría de las aplicaciones de usuario no utilizan computación GPU.

En general, la computación GPU se realiza principalmente en sistemas HPC especializados para computación científica. Pero las aplicaciones de usuario que utilizan computación GPU se pueden contar con los dedos de una mano. Cabe señalar de inmediato que el término "computación GPU" en este caso no es del todo correcto y puede inducir a error. El hecho es que si una aplicación utiliza computación GPU, esto no significa que el procesador central esté inactivo. La computación GPU no implica transferir la carga del procesador central al procesador gráfico. Como regla general, el procesador central permanece ocupado y el uso de un procesador gráfico, junto con el procesador central, puede mejorar el rendimiento, es decir, reducir el tiempo necesario para completar una tarea. Además, la propia GPU aquí actúa como una especie de coprocesador de la CPU, pero en ningún caso la reemplaza por completo.

Para comprender por qué la informática GPU no es una panacea y por qué es incorrecto decir que sus capacidades informáticas superan las de la CPU, es necesario comprender la diferencia entre un procesador central y un procesador gráfico.

Diferencias en arquitecturas de GPU y CPU

Los núcleos de CPU están diseñados para ejecutar un único flujo de instrucciones secuenciales con el máximo rendimiento, mientras que los núcleos de GPU están diseñados para ejecutar rápidamente una gran cantidad de flujos de instrucciones en paralelo. Ésta es la diferencia fundamental entre GPU y procesadores centrales. La CPU es un procesador de propósito general o de propósito general optimizado para un alto rendimiento a partir de un único flujo de instrucciones que maneja números enteros y de punto flotante. En este caso, el acceso a la memoria con datos e instrucciones se produce predominantemente de forma aleatoria.

Para mejorar el rendimiento de la CPU, están diseñados para ejecutar tantas instrucciones como sea posible en paralelo. Por ejemplo, para ello, los núcleos del procesador utilizan una unidad de ejecución de instrucciones desordenadas, que permite reordenar las instrucciones fuera del orden en que fueron recibidas, lo que permite aumentar el nivel de paralelismo en el Implementación de instrucciones a nivel de un hilo. Sin embargo, esto todavía no permite la ejecución en paralelo de una gran cantidad de instrucciones, y la sobrecarga de paralelizar instrucciones dentro del núcleo del procesador resulta ser muy significativa. Esta es la razón por la que los procesadores de propósito general no tienen una gran cantidad de unidades de ejecución.

El procesador gráfico está diseñado de manera fundamentalmente diferente. Originalmente fue diseñado para ejecutar una gran cantidad de flujos de comandos paralelos. Además, estos flujos de comandos están paralelizados desde el principio y simplemente no hay costos generales por paralelizar instrucciones en la GPU. La GPU está diseñada para renderizar imágenes. En pocas palabras, toma un grupo de polígonos como entrada, realiza todas las operaciones necesarias y genera píxeles. El procesamiento de polígonos y píxeles es independiente; se pueden procesar en paralelo, por separado unos de otros. Por lo tanto, debido a la organización del trabajo inherentemente paralela, la GPU utiliza una gran cantidad de unidades de ejecución, que son fáciles de cargar, a diferencia del flujo secuencial de instrucciones de la CPU.

Los procesadores gráficos y centrales también difieren en los principios de acceso a la memoria. En una GPU, el acceso a la memoria es fácilmente predecible: si se lee un texel de textura de la memoria, después de un tiempo llegará la fecha límite para los texels vecinos. Al grabar, sucede lo mismo: si se escribe un píxel en el framebuffer, luego de algunos ciclos de reloj se escribirá el píxel ubicado al lado. Por lo tanto, la GPU, a diferencia de la CPU, simplemente no necesita una gran memoria caché y las texturas solo necesitan unos pocos kilobytes. El principio de funcionamiento con memoria también es diferente para GPU y CPU. Entonces, todas las GPU modernas tienen varios controladores de memoria y la memoria gráfica en sí es más rápida, por lo que las GPU tienen mucho más. oh mayor ancho de banda de memoria en comparación con los procesadores universales, lo cual también es muy importante para cálculos paralelos que operan con grandes flujos de datos.

En procesadores universales oh La mayor parte del área del chip está ocupada por varios buffers de comando y datos, unidades de decodificación, unidades de predicción de rama de hardware, unidades de reordenamiento de instrucciones y memoria caché del primer, segundo y tercer nivel. Todas estas unidades de hardware son necesarias para acelerar la ejecución de algunos subprocesos de comando al paralelizarlos en el nivel del núcleo del procesador.

Las propias unidades de ejecución ocupan relativamente poco espacio en un procesador universal.

En un procesador gráfico, por el contrario, el área principal está ocupada por numerosas unidades de ejecución, lo que le permite procesar simultáneamente varios miles de hilos de comando.

Podemos decir que, a diferencia de las CPU modernas, las GPU están diseñadas para cálculos paralelos con una gran cantidad de operaciones aritméticas.

Es posible utilizar la potencia informática de las GPU para tareas no gráficas, pero sólo si el problema que se está resolviendo permite la posibilidad de paralelizar algoritmos en cientos de unidades de ejecución disponibles en la GPU. En particular, los cálculos de GPU muestran excelentes resultados cuando se aplica la misma secuencia de operaciones matemáticas a un gran volumen de datos. En este caso, los mejores resultados se obtienen si la relación entre el número de instrucciones aritméticas y el número de accesos a la memoria es suficientemente grande. Esta operación requiere menos control de ejecución y no requiere una gran memoria caché.

Hay muchos ejemplos de cálculos científicos en los que la ventaja de la GPU sobre la CPU en términos de eficiencia computacional es innegable. Por lo tanto, muchas aplicaciones científicas en modelado molecular, dinámica de gases, dinámica de fluidos y otras se adaptan perfectamente a los cálculos en la GPU.

Entonces, si el algoritmo para resolver un problema se puede paralelizar en miles de subprocesos individuales, entonces la eficiencia de resolver dicho problema usando una GPU puede ser mayor que resolverlo usando solo un procesador de propósito general. Sin embargo, no se puede transferir tan fácilmente la solución de algún problema de la CPU a la GPU, aunque sólo sea porque la CPU y la GPU utilizan comandos diferentes. Es decir, cuando se escribe un programa para una solución en una CPU, se utiliza el conjunto de comandos x86 (o un conjunto de comandos compatible con una arquitectura de procesador específica), pero para la GPU se utilizan conjuntos de comandos completamente diferentes, que nuevamente tienen en cuenta cuenta su arquitectura y capacidades. Al desarrollar juegos 3D modernos, se utilizan las API DirectX y OpenGL, lo que permite a los programadores trabajar con sombreadores y texturas. Sin embargo, utilizar las API DirectX y OpenGL para informática no gráfica en la GPU no es la mejor opción.

APLICACIÓN NVIDIA CUDA y AMD

Por eso, cuando comenzaron los primeros intentos de implementar computación no gráfica en GPU (GPU de propósito general, GPGPU), surgió el compilador BrookGPU. Antes de su creación, los desarrolladores tenían que acceder a los recursos de la tarjeta de video a través de la API de gráficos OpenGL o Direct3D, lo que complicaba significativamente el proceso de programación, ya que requería conocimientos específicos: debían aprender los principios de trabajar con objetos 3D (sombreadores, texturas, etc.). ). Ésta fue la razón del uso muy limitado de GPGPU en productos de software. BrookGPU se ha convertido en una especie de “traductor”. Estas extensiones de transmisión al lenguaje C ocultaron la API 3D a los programadores y, al usarla, la necesidad de conocimientos de programación 3D prácticamente desapareció. La potencia informática de las tarjetas de vídeo está disponible para los programadores en forma de un coprocesador adicional para cálculos paralelos. El compilador BrookGPU procesó el archivo con código C y extensiones, creando código vinculado a una biblioteca con soporte DirectX u OpenGL.

Gracias en gran parte a BrookGPU, NVIDIA y ATI (ahora AMD) se dieron cuenta de la tecnología emergente de computación de propósito general en GPU y comenzaron a desarrollar sus propias implementaciones que brindan acceso directo y más transparente a las unidades de computación de los aceleradores 3D.

Como resultado, NVIDIA ha desarrollado una arquitectura de hardware y software para computación paralela, CUDA (Compute Unified Device Architecture). La arquitectura CUDA permite la informática sin gráficos en las GPU NVIDIA.

El lanzamiento de la versión beta pública del CUDA SDK tuvo lugar en febrero de 2007. La API CUDA se basa en un dialecto simplificado del lenguaje C. La arquitectura CUDA SDK permite a los programadores implementar algoritmos que se ejecutan en GPU NVIDIA e incluyen funciones especiales en el texto del programa C. Para traducir correctamente el código a este lenguaje, el SDK de CUDA incluye el compilador de línea de comandos nvcc propio de NVIDIA.

CUDA es un software multiplataforma para sistemas operativos como Linux, Mac OS X y Windows.

AMD (ATI) también ha desarrollado su propia versión de la tecnología GPGPU, que antes se llamaba ATI Stream, y ahora AMD Accelerated Parallel Processing (APP). La aplicación AMD se basa en el estándar abierto de la industria OpenCL (Open Computing Language). El estándar OpenCL proporciona paralelismo a nivel de instrucción y de datos y es una implementación de la técnica GPGPU. Es un estándar completamente abierto y su uso está libre de regalías. Tenga en cuenta que la aplicación AMD y NVIDIA CUDA son incompatibles entre sí; sin embargo, la última versión de NVIDIA CUDA también es compatible con OpenCL.

Prueba de GPGPU en convertidores de vídeo

Entonces, descubrimos que la tecnología CUDA se usa para implementar GPGPU en las GPU NVIDIA y la API de la aplicación se usa en las GPU AMD. Como ya se señaló, el uso de computación no gráfica en la GPU es aconsejable sólo si el problema que se está resolviendo se puede paralelizar en muchos subprocesos. Sin embargo, la mayoría de las aplicaciones de usuario no cumplen con este criterio. Sin embargo hay algunas excepciones. Por ejemplo, la mayoría de los convertidores de video modernos admiten la capacidad de utilizar computación en GPU NVIDIA y AMD.

Para descubrir con qué eficiencia se utiliza la computación GPU en los convertidores de video personalizados, seleccionamos tres soluciones populares: Xilisoft Video Converter Ultimate 7.7.2, Wondershare Video Converter Ultimate 6.0.3.2 y Movavi Video Converter 10.2.1. Estos convertidores admiten la capacidad de usar GPU NVIDIA y AMD, y puede desactivar esta función en la configuración del convertidor de video, lo que le permite evaluar la efectividad del uso de la GPU.

Para la conversión de video, utilizamos tres videos diferentes.

El primer vídeo tenía una duración de 3 minutos y 35 segundos y un tamaño de 1,05 GB. Estaba grabado en el formato de almacenamiento de datos mkv (contenedor) y tenía las siguientes características:

  • video:
    • formato: vídeo MPEG4 (H264),
    • resolución - 1920*um*1080,
    • modo de tasa de bits - Variable,
    • tasa de bits de vídeo promedio: 42,1 Mbit/s,
    • tasa de bits de vídeo máxima: 59,1 Mbit/s,
    • velocidad de fotogramas: 25 fps;
  • audio:
    • formato: audio MPEG-1,
    • tasa de bits de audio: 128 Kbps,
    • número de canales - 2,

El segundo vídeo tuvo una duración de 4 minutos 25 segundos y un tamaño de 1,98 GB. Estaba grabado en el formato de almacenamiento de datos MPG (contenedor) y tenía las siguientes características:

  • video:
    • formato - MPEG-PS (vídeo MPEG2),
    • resolución - 1920*um*1080,
    • modo de tasa de bits - Variable.
    • tasa de bits de vídeo promedio: 62,5 Mbit/s,
    • tasa de bits de vídeo máxima: 100 Mbit/s,
    • velocidad de fotogramas: 25 fps;
  • audio:
    • formato: audio MPEG-1,
    • tasa de bits de audio: 384 Kbps,
    • número de canales - 2,

El tercer video tuvo una duración de 3 minutos 47 segundos y un tamaño de 197 MB. Fue escrito en formato de almacenamiento de datos MOV (contenedor) y tenía las siguientes características:

  • video:
    • formato: vídeo MPEG4 (H264),
    • resolución - 1920*um*1080,
    • modo de tasa de bits - Variable,
    • tasa de bits de vídeo: 7024 Kbps,
    • velocidad de fotogramas: 25 fps;
  • audio:
    • formato - AAC,
    • tasa de bits de audio: 256 Kbps,
    • número de canales - 2,
    • frecuencia de muestreo - 48 kHz.

Los tres videos de prueba se convirtieron usando convertidores de video al formato de almacenamiento de datos MP4 (códec H.264) para verlos en la tableta iPad 2. La resolución del archivo de video de salida fue 1280*um*720.

Tenga en cuenta que no utilizamos exactamente la misma configuración de conversión en los tres convertidores. Por eso es incorrecto comparar la eficiencia de los propios conversores de vídeo en función del tiempo de conversión. Por lo tanto, en el conversor de vídeo Xilisoft Video Converter Ultimate 7.7.2, se utilizó el preajuste de iPad 2: vídeo HD H.264 para la conversión. Este ajuste preestablecido utiliza las siguientes configuraciones de codificación:

  • códec - MPEG4 (H.264);
  • resolución - 1280*um*720;
  • velocidad de fotogramas: 29,97 fps;
  • tasa de bits de vídeo: 5210 Kbps;
  • códec de audio - AAC;
  • tasa de bits de audio: 128 Kbps;
  • número de canales - 2;
  • frecuencia de muestreo - 48 kHz.

Wondershare Video Converter Ultimate 6.0.3.2 usó el ajuste preestablecido de iPad 2 con las siguientes configuraciones adicionales:

  • códec - MPEG4 (H.264);
  • resolución - 1280*um*720;
  • velocidad de fotogramas: 30 fps;
  • tasa de bits de vídeo: 5000 Kbps;
  • códec de audio - AAC;
  • tasa de bits de audio: 128 Kbps;
  • número de canales - 2;
  • frecuencia de muestreo - 48 kHz.

Movavi Video Converter 10.2.1 utilizó el ajuste preestablecido de iPad (1280*um*720, H.264) (*.mp4) con las siguientes configuraciones adicionales:

  • formato de vídeo: H.264;
  • resolución - 1280*um*720;
  • velocidad de fotogramas: 30 fps;
  • tasa de bits de vídeo: 2500 Kbps;
  • códec de audio - AAC;
  • tasa de bits de audio: 128 Kbps;
  • número de canales - 2;
  • frecuencia de muestreo: 44,1 kHz.

Cada vídeo fuente se convirtió cinco veces en cada uno de los convertidores de vídeo, utilizando tanto la GPU como solo la CPU. Después de cada conversión, la computadora se reinicia.

Como resultado, cada vídeo se convirtió diez veces en cada convertidor de vídeo. Para automatizar este trabajo de rutina, se escribió una utilidad especial con una interfaz gráfica que le permite automatizar completamente el proceso de prueba.

Configuración del banco de pruebas

El banco de pruebas tenía la siguiente configuración:

  • procesador: Intel Core i7-3770K;
  • placa base: Gigabyte GA-Z77X-UD5H;
  • conjunto de chips de la placa base: Intel Z77 Express;
  • memoria - DDR3-1600;
  • capacidad de memoria: 8 GB (dos módulos GEIL de 4 GB cada uno);
  • modo de funcionamiento de la memoria: doble canal;
  • tarjeta de video: NVIDIA GeForce GTX 660Ti (controlador de video 314.07);
  • unidad: Intel SSD 520 (240 GB).

En el stand se instaló el sistema operativo Windows 7 Ultimate (64 bits).

Inicialmente, probamos el procesador y todos los demás componentes del sistema en modo normal. Al mismo tiempo, el procesador Intel Core i7-3770K funcionó a una frecuencia estándar de 3,5 GHz con el modo Turbo Boost activado (la frecuencia máxima del procesador en el modo Turbo Boost es 3,9 GHz).

Luego repetimos el proceso de prueba, pero con el procesador overclockeado a una frecuencia fija de 4,5 GHz (sin usar el modo Turbo Boost). Esto permitió identificar la dependencia de la velocidad de conversión de la frecuencia del procesador (CPU).

En la siguiente etapa de prueba, volvimos a la configuración estándar del procesador y repetimos las pruebas con otras tarjetas de video:

  • NVIDIA GeForce GTX 280 (controlador 314.07);
  • NVIDIA GeForce GTX 460 (controlador 314.07);
  • AMD Radeon HD6850 (controlador 13.1).

Así, la conversión de vídeo se realizó en cuatro tarjetas de vídeo de diferentes arquitecturas.

La tarjeta de video senior NVIDIA GeForce 660Ti se basa en un procesador gráfico del mismo nombre, codificado GK104 (arquitectura Kepler), producido utilizando una tecnología de proceso de 28 nm. Esta GPU contiene 3,54 mil millones de transistores y tiene un área de matriz de 294 mm2.

Recuerde que el procesador gráfico GK104 incluye cuatro grupos de procesamiento de gráficos (Graphics Processing Clusters, GPC). Los clusters GPC son dispositivos independientes dentro del procesador y son capaces de funcionar como dispositivos separados, ya que tienen todos los recursos necesarios: rasterizadores, motores de geometría y módulos de textura.

Cada uno de estos grupos tiene dos multiprocesadores SMX (Streaming Multiprocessor), pero en el procesador GK104 un multiprocesador está bloqueado en uno de los grupos, por lo que hay siete multiprocesadores SMX en total.

Cada multiprocesador de transmisión SMX contiene 192 núcleos de computación de transmisión (núcleos CUDA), por lo que el procesador GK104 tiene un total de 1344 núcleos CUDA. Además, cada multiprocesador SMX contiene 16 unidades de textura (TMU), 32 unidades de funciones especiales (SFU), 32 unidades de almacenamiento de carga (LSU), un motor PolyMorph y mucho más.

La GeForce GTX 460 se basa en una GPU codificada GF104 basada en la arquitectura Fermi. Este procesador se fabrica utilizando una tecnología de proceso de 40 nm y contiene alrededor de 1,95 mil millones de transistores.

La GPU GF104 incluye dos grupos de procesamiento de gráficos GPC. Cada uno de ellos tiene cuatro multiprocesadores de transmisión SM, pero en el procesador GF104 de uno de los clústeres un multiprocesador está bloqueado, por lo que solo hay siete multiprocesadores SM.

Cada multiprocesador de transmisión SM contiene 48 núcleos de computación de transmisión (núcleos CUDA), por lo que el procesador GK104 tiene un total de 336 núcleos CUDA. Además, cada multiprocesador SM contiene ocho unidades de textura (TMU), ocho unidades de funciones especiales (SFU), 16 unidades de almacenamiento de carga (LSU), un motor PolyMorph y mucho más.

La GPU GeForce GTX 280 es parte de la segunda generación de la arquitectura de GPU unificada de NVIDIA y es muy diferente en arquitectura a la de Fermi y Kepler.

La GPU GeForce GTX 280 consta de grupos de procesamiento de texturas (TPC) que, aunque similares, son muy diferentes de los grupos de procesamiento de gráficos GPC en las arquitecturas Fermi y Kepler. En total, hay diez grupos de este tipo en el procesador GeForce GTX 280. Cada clúster TPC incluye tres multiprocesadores de transmisión SM y ocho unidades de filtrado y muestreo de texturas (TMU). Cada multiprocesador consta de ocho procesadores de flujo (SP). Los multiprocesadores también contienen bloques para muestrear y filtrar datos de textura, utilizados tanto en gráficos como en algunas tareas computacionales.

Por lo tanto, en un clúster TPC hay 24 procesadores de flujo, y en la GPU GeForce GTX 280 ya hay 240.

En la tabla se presentan las características resumidas de las tarjetas de video en las GPU NVIDIA utilizadas en las pruebas.

La siguiente tabla no incluye la tarjeta de video AMD Radeon HD6850, lo cual es bastante natural, ya que sus características técnicas son difíciles de comparar con las tarjetas de video NVIDIA. Por tanto, lo consideraremos por separado.

La GPU AMD Radeon HD6850, con nombre en código Barts, se fabrica utilizando una tecnología de proceso de 40 nm y contiene 1,7 mil millones de transistores.

La arquitectura del procesador AMD Radeon HD6850 es una arquitectura unificada con una variedad de procesadores comunes para el procesamiento en streaming de múltiples tipos de datos.

El procesador AMD Radeon HD6850 consta de 12 núcleos SIMD, cada uno de los cuales contiene 16 unidades de procesador de flujo superescalar y cuatro unidades de textura. Cada procesador de flujo superescalar contiene cinco procesadores de flujo de propósito general. Así, en total, la GPU AMD Radeon HD6850 tiene 12*um*16*um*5=960 procesadores de flujo universales.

La frecuencia de GPU de la tarjeta de video AMD Radeon HD6850 es de 775 MHz y la frecuencia efectiva de la memoria GDDR5 es de 4000 MHz. La capacidad de la memoria es de 1024 MB.

Resultados de la prueba

Así que veamos los resultados de las pruebas. Comencemos con la primera prueba, cuando utilizamos la tarjeta de video NVIDIA GeForce GTX 660Ti y el modo operativo estándar del procesador Intel Core i7-3770K.

En la Fig. 1-3 muestran los resultados de la conversión de tres videos de prueba usando tres convertidores en modos con y sin GPU.

Como se puede ver en los resultados de las pruebas, el efecto del uso de la GPU es obvio. Para Xilisoft Video Converter Ultimate 7.7.2, cuando se utiliza una GPU, el tiempo de conversión se reduce en un 14, 9 y 19 % para el primer, segundo y tercer vídeo, respectivamente.

Para Wondershare Video Converter Ultimate 6.0.32, el uso de una GPU reduce el tiempo de conversión en un 10%, 13% y 23% para el primer, segundo y tercer video, respectivamente.

Pero el conversor que más se beneficia del uso de un procesador gráfico es Movavi Video Converter 10.2.1. Para el primer, segundo y tercer vídeo, la reducción del tiempo de conversión es del 64, 81 y 41%, respectivamente.

Está claro que el beneficio de usar una GPU depende tanto del vídeo de origen como de la configuración de conversión de vídeo, que, de hecho, es lo que demuestran nuestros resultados.

Ahora veamos cuál será la ganancia en el tiempo de conversión al overclockear el procesador Intel Core i7-3770K a 4,5 GHz. Si asumimos que en modo normal todos los núcleos del procesador se cargan durante la conversión y en modo Turbo Boost funcionan a una frecuencia de 3,7 GHz, entonces aumentar la frecuencia a 4,5 GHz corresponde a un overclock de frecuencia del 22%.

En la Fig. 4-6 muestran los resultados de convertir tres videos de prueba al hacer overclocking del procesador en modos con y sin procesador gráfico. En este caso, el uso de un procesador gráfico permite ganar tiempo de conversión.

Para el conversor de vídeo Xilisoft Video Converter Ultimate 7.7.2, cuando se utiliza una GPU, el tiempo de conversión se reduce en un 15, 9 y 20% para el primer, segundo y tercer vídeo, respectivamente.

Para Wondershare Video Converter Ultimate 6.0.32, el uso de una GPU puede reducir el tiempo de conversión en un 10, 10 y 20 % para el primer, segundo y tercer video, respectivamente.

Para Movavi Video Converter 10.2.1, el uso de un procesador gráfico puede reducir el tiempo de conversión en un 59, 81 y 40%, respectivamente.

Naturalmente, es interesante ver cómo el overclocking de la CPU puede reducir los tiempos de conversión con y sin GPU.

En la Fig. Las Figuras 7-9 muestran los resultados de comparar el tiempo para convertir videos sin usar un procesador de gráficos en el modo de operación normal del procesador y en el modo overclockeado. Dado que en este caso la conversión la realiza únicamente la CPU sin cálculos en la GPU, es obvio que aumentar la frecuencia del reloj del procesador conduce a una reducción del tiempo de conversión (aumento de la velocidad de conversión). Es igualmente obvio que la reducción en la velocidad de conversión debería ser aproximadamente la misma para todos los vídeos de prueba. Así, para el conversor de vídeo Xilisoft Video Converter Ultimate 7.7.2, al overclockear el procesador, el tiempo de conversión se reduce en un 9, 11 y 9% para el primer, segundo y tercer vídeo, respectivamente. Para Wondershare Video Converter Ultimate 6.0.32, el tiempo de conversión se reduce en un 9, 9 y 10% para el primer, segundo y tercer video, respectivamente. Bueno, para el conversor de vídeo Movavi Video Converter 10.2.1, el tiempo de conversión se reduce en un 13, 12 y 12%, respectivamente.

Por lo tanto, al overclockear la frecuencia del procesador en un 20%, el tiempo de conversión se reduce en aproximadamente un 10%.

Comparemos el tiempo para convertir videos usando un procesador gráfico en modo de procesador normal y en modo overclocking (Fig. 10-12).

Para el convertidor de vídeo Xilisoft Video Converter Ultimate 7.7.2, al hacer overclocking del procesador, el tiempo de conversión se reduce en un 10, 10 y 9% para el primer, segundo y tercer vídeo, respectivamente. Para Wondershare Video Converter Ultimate 6.0.32, el tiempo de conversión se reduce en un 9, 6 y 5% para el primer, segundo y tercer video, respectivamente. Bueno, para el conversor de vídeo Movavi Video Converter 10.2.1, el tiempo de conversión se reduce en un 0,2, 10 y 10%, respectivamente.

Como puede ver, para los convertidores Xilisoft Video Converter Ultimate 7.7.2 y Wondershare Video Converter Ultimate 6.0.32, la reducción en el tiempo de conversión al overclockear el procesador es aproximadamente la misma tanto cuando se usa un procesador gráfico como sin usarlo, lo cual es Es lógico, ya que estos convertidores no utilizan la computación GPU de manera muy eficiente. Pero para Movavi Video Converter 10.2.1, que utiliza efectivamente la computación GPU, el overclocking del procesador en el modo de computación GPU tiene poco efecto en la reducción del tiempo de conversión, lo cual también es comprensible, ya que en este caso la carga principal recae en el procesador gráfico. .

Ahora veamos los resultados de las pruebas con varias tarjetas de video.

Parecería que cuanto más potente sea la tarjeta de video y más núcleos CUDA (o procesadores de flujo universales para tarjetas de video AMD) haya en el procesador de video, más efectiva debería ser la conversión de video cuando se usa un procesador de video. Pero en la práctica las cosas no son así.

En cuanto a las tarjetas de video basadas en GPU NVIDIA, la situación es la siguiente. Cuando se utilizan los convertidores Xilisoft Video Converter Ultimate 7.7.2 y Wondershare Video Converter Ultimate 6.0.32, el tiempo de conversión prácticamente no depende del tipo de tarjeta de video utilizada. Es decir, para las tarjetas de video NVIDIA GeForce GTX 660Ti, NVIDIA GeForce GTX 460 y NVIDIA GeForce GTX 280 en el modo de computación GPU, el tiempo de conversión es el mismo (Fig. 13-15).

Arroz. 1. Resultados de convertir el primero.
vídeo de prueba en modo normal
operación del procesador

procesador en tarjetas de video en modo GPU

Arroz. 14. Resultados de la comparación del tiempo de conversión del segundo video.

Arroz. 15. Resultados de la comparación del tiempo de conversión del tercer video.
en varias tarjetas de video en modo GPU

Esto sólo puede explicarse por el hecho de que el algoritmo de cálculo de GPU implementado en los convertidores Xilisoft Video Converter Ultimate 7.7.2 y Wondershare Video Converter Ultimate 6.0.32 es simplemente ineficaz y no permite el uso activo de todos los núcleos gráficos. Por cierto, esto es precisamente lo que explica el hecho de que para estos convertidores la diferencia en el tiempo de conversión entre los modos con y sin uso de la GPU sea pequeña.

En Movavi Video Converter 10.2.1 la situación es ligeramente diferente. Como recordamos, este convertidor es capaz de hacer un uso muy eficiente de los cálculos de GPU y, por lo tanto, en modo GPU, el tiempo de conversión depende del tipo de tarjeta de video utilizada.

Pero con la tarjeta de video AMD Radeon HD 6850 todo es como de costumbre. O el controlador de la tarjeta de video está "torcido" o los algoritmos implementados en los convertidores necesitan mejoras importantes, pero cuando se utiliza la computación GPU, los resultados no mejoran o empeoran.

Más concretamente, la situación es la siguiente. Para Xilisoft Video Converter Ultimate 7.7.2, cuando se usa una GPU para convertir el primer video de prueba, el tiempo de conversión aumenta en un 43% y al convertir el segundo video, en un 66%.

Además, Xilisoft Video Converter Ultimate 7.7.2 también se caracteriza por resultados inestables. ¡La variación en el tiempo de conversión puede alcanzar el 40%! Por eso repetimos todas las pruebas diez veces y calculamos el resultado medio.

Pero para Wondershare Video Converter Ultimate 6.0.32 y Movavi Video Converter 10.2.1, cuando se usa una GPU para convertir los tres videos, ¡el tiempo de conversión no cambia en absoluto! Es probable que Wondershare Video Converter Ultimate 6.0.32 y Movavi Video Converter 10.2.1 no utilicen la tecnología AMD APP al realizar la conversión o que el controlador de vídeo AMD simplemente esté "torcido", por lo que la tecnología AMD APP no funciona. .

conclusiones

Sobre la base de las pruebas, se pueden extraer las siguientes conclusiones importantes. Los conversores de vídeo modernos pueden utilizar la tecnología informática GPU, lo que permite una mayor velocidad de conversión. Sin embargo, esto no significa que todos los cálculos se transfieran íntegramente a la GPU y la CPU quede sin uso. Como muestran las pruebas, cuando se utiliza la tecnología GPGPU, el procesador central permanece ocupado, lo que significa que el uso de potentes procesadores centrales multinúcleo en sistemas utilizados para la conversión de vídeo sigue siendo relevante. La excepción a esta regla es la tecnología AMD APP en las GPU AMD. Por ejemplo, cuando se utiliza Xilisoft Video Converter Ultimate 7.7.2 con la tecnología AMD APP activada, la carga en la CPU efectivamente se reduce, pero esto lleva al hecho de que el tiempo de conversión no se reduce, sino que, por el contrario, aumenta.

En general, si hablamos de convertir vídeo con el uso adicional de un procesador gráfico, entonces para solucionar este problema es recomendable utilizar tarjetas de vídeo con GPU NVIDIA. Como muestra la práctica, sólo en este caso se puede lograr un aumento en la velocidad de conversión. Además, debes recordar que el aumento real de la velocidad de conversión depende de muchos factores. Estos son los formatos de vídeo de entrada y salida y, por supuesto, el propio conversor de vídeo. Los convertidores Xilisoft Video Converter Ultimate 7.7.2 y Wondershare Video Converter Ultimate 6.0.32 no son adecuados para esta tarea, pero el convertidor y Movavi Video Converter 10.2.1 pueden utilizar de manera muy efectiva las capacidades de la GPU NVIDIA.

En cuanto a las tarjetas de video en las GPU AMD, no deben usarse en absoluto para tareas de conversión de video. En el mejor de los casos, esto no aumentará la velocidad de conversión y, en el peor, puede reducirla.

Hoy en día, las noticias sobre el uso de GPU para la informática en general se pueden escuchar en todos los rincones. Palabras como CUDA, Stream y OpenCL se han convertido en sólo dos años en casi las más citadas en Internet de TI. Sin embargo, no todo el mundo sabe qué significan estas palabras y qué significan las tecnologías detrás de ellas. Y para los usuarios de Linux, que están acostumbrados a “estar sobre la marcha”, todo esto parece un bosque oscuro.

Nacimiento de GPGPU

Todos estamos acostumbrados a pensar que el único componente de una computadora capaz de ejecutar cualquier código que se le indique es el procesador central. Durante mucho tiempo, casi todos los PC producidos en serie estaban equipados con un único procesador que manejaba todos los cálculos imaginables, incluido el código del sistema operativo, todo nuestro software y virus.

Más tarde aparecieron los procesadores multinúcleo y los sistemas multiprocesador, en los que había varios de estos componentes. Esto permitió que las máquinas realizaran múltiples tareas simultáneamente y el rendimiento general (teórico) del sistema aumentó exactamente tanto como la cantidad de núcleos instalados en la máquina. Sin embargo, resultó que era demasiado difícil y costoso producir y diseñar procesadores multinúcleo.

Cada núcleo tenía que albergar un procesador completo de una arquitectura x86 compleja e intrincada, con su propio caché (bastante grande), canal de instrucciones, bloques SSE, muchos bloques que realizan optimizaciones, etc. etcétera. Por lo tanto, el proceso de aumento del número de núcleos se ralentizó significativamente, y los batas universitarias blancas, para quienes dos o cuatro núcleos claramente no eran suficientes, encontraron una manera de utilizar para sus cálculos científicos otra potencia informática, que abundaba en la tarjeta de video. (como resultado, incluso apareció la herramienta BrookGPU, que emula un procesador adicional utilizando llamadas a funciones DirectX y OpenGL).

Los procesadores gráficos, desprovistos de muchas de las desventajas del procesador central, resultaron ser una máquina calculadora excelente y muy rápida, y muy pronto los propios fabricantes de GPU comenzaron a observar más de cerca los desarrollos de las mentes científicas (y nVidia de hecho contrató a la mayoría de los investigadores trabajar para ellos). Como resultado, apareció la tecnología nVidia CUDA, que define una interfaz con la que fue posible transferir el cálculo de algoritmos complejos a los hombros de la GPU sin muletas. Posteriormente le siguió ATi (AMD) con su propia versión de la tecnología llamada Close to Metal (ahora Stream), y muy pronto apareció una versión estándar de Apple, llamada OpenCL.

¿La GPU lo es todo?

A pesar de todas las ventajas, la técnica GPGPU tiene varios problemas. El primero de ellos es el ámbito de aplicación muy limitado. Las GPU han ido muy por delante del procesador central en términos de aumento de la potencia informática y del número total de núcleos (las tarjetas de vídeo llevan una unidad informática que consta de más de cien núcleos), pero una densidad tan alta se logra maximizando la simplificación del diseño. del propio chip.

En esencia, la tarea principal de la GPU se reduce a cálculos matemáticos utilizando algoritmos simples que reciben como entrada cantidades no muy grandes de datos predecibles. Por esta razón, los núcleos de GPU tienen un diseño muy simple, tamaños de caché reducidos y un conjunto de instrucciones modesto, lo que finalmente resulta en su bajo costo de producción y la posibilidad de una colocación muy densa en el chip. Las GPU son como una fábrica china con miles de trabajadores. Hacen bastante bien algunas cosas sencillas (y lo más importante, de forma rápida y económica), pero si les confías el montaje de un avión, el resultado será, como mucho, un ala delta.

Por lo tanto, la primera limitación de las GPU es su enfoque en cálculos matemáticos rápidos, lo que limita el ámbito de aplicación de las GPU a la asistencia en aplicaciones multimedia, así como a cualquier programa involucrado en el procesamiento de datos complejos (por ejemplo, archivadores o sistemas de cifrado, así como como software relacionado con microscopía de fluorescencia, dinámica molecular, electrostática y otras cosas de poco interés para los usuarios de Linux).

El segundo problema con GPGPU es que no todos los algoritmos se pueden adaptar para su ejecución en la GPU. Los núcleos de GPU individuales son bastante lentos y su potencia sólo se hace evidente cuando trabajan juntos. Esto significa que el algoritmo será tan efectivo como el programador pueda paralelizarlo de manera efectiva. En la mayoría de los casos, sólo un buen matemático puede realizar este tipo de trabajos, de los cuales hay muy pocos desarrolladores de software.

Y en tercer lugar, las GPU funcionan con la memoria instalada en la propia tarjeta gráfica, por lo que cada vez que se utiliza la GPU habrá dos operaciones de copia adicionales: datos de entrada desde la RAM de la aplicación y datos de salida desde GRAM a la memoria de la aplicación. Como puede imaginar, esto puede anular cualquier beneficio en el tiempo de ejecución de la aplicación (como es el caso de la herramienta FlacCL, que veremos más adelante).

Pero eso no es todo. A pesar de la existencia de un estándar generalmente aceptado en forma de OpenCL, muchos programadores todavía prefieren utilizar implementaciones específicas del proveedor de la técnica GPGPU. CUDA resultó ser especialmente popular, que, aunque proporciona una interfaz de programación más flexible (por cierto, OpenCL en los controladores nVidia se implementa sobre CUDA), vincula estrechamente la aplicación a las tarjetas de video del mismo fabricante.

KGPU o kernel de Linux acelerado por GPU

Investigadores de la Universidad de Utah han desarrollado un sistema KGPU que permite ejecutar algunas funciones del kernel de Linux en una GPU utilizando el marco CUDA. Para realizar esta tarea, se utiliza un kernel de Linux modificado y un demonio especial que se ejecuta en el espacio del usuario, escucha las solicitudes del kernel y las pasa al controlador de la tarjeta de video usando la biblioteca CUDA. Curiosamente, a pesar de la importante sobrecarga que genera dicha arquitectura, los autores de KGPU lograron crear una implementación del algoritmo AES, que aumenta 6 veces la velocidad de cifrado del sistema de archivos eCryptfs.

¿Qué hay ahora?

Debido a su juventud, así como a los problemas descritos anteriormente, GPGPU nunca se ha convertido en una tecnología verdaderamente extendida, pero existe software útil que utiliza sus capacidades (aunque en pequeñas cantidades). Entre los primeros en aparecer se encuentran crackers de varios hashes, cuyos algoritmos son muy fáciles de paralelizar.

También nacieron aplicaciones multimedia, como el codificador FlacCL, que permite transcodificar una pista de audio al formato FLAC. Algunas aplicaciones preexistentes también han adquirido soporte GPGPU, la más notable de las cuales es ImageMagick, que ahora puede descargar parte de su trabajo a la GPU usando OpenCL. También hay proyectos para transferir archivadores de datos y otros sistemas de compresión de información a CUDA/OpenCL (los ATi Unixoid no son del agrado). Analizaremos los proyectos más interesantes en las siguientes secciones del artículo, pero por ahora intentemos descubrir qué necesitamos para que todo comience y funcione de manera estable.

Las GPU han superado durante mucho tiempo a los procesadores x86 en rendimiento

· En segundo lugar, se deben instalar en el sistema los controladores propietarios más recientes para la tarjeta de video; estos brindarán soporte tanto para las tecnologías GPGPU nativas de la tarjeta como para OpenCL abierto.

· Y en tercer lugar, dado que los desarrolladores de distribuciones aún no han comenzado a distribuir paquetes de aplicaciones con soporte GPGPU, tendremos que crear aplicaciones nosotros mismos, y para ello necesitamos SDK oficiales de los fabricantes: CUDA Toolkit o ATI Stream SDK. Contienen los archivos de encabezado y las bibliotecas necesarias para crear aplicaciones.

Instalar el kit de herramientas CUDA

Siga el enlace anterior y descargue CUDA Toolkit para Linux (puede elegir entre varias versiones, para las distribuciones Fedora, RHEL, Ubuntu y SUSE, existen versiones para arquitecturas x86 y x86_64). Además, también debe descargar allí los kits de controladores para desarrolladores (controladores de desarrollador para Linux, son los primeros en la lista).

Inicie el instalador del SDK:

$ sudo sh cudatoolkit_4.0.17_linux_64_ubuntu10.10.run

Cuando finalice la instalación, procedemos a instalar los controladores. Para hacer esto, apague el servidor X:

# sudo /etc/init.d/gdm detener

Abre la consola y ejecute el instalador del controlador:

$ sudo sh devdriver_4.0_linux_64_270.41.19.run

Una vez completada la instalación, inicie X:

Para que las aplicaciones puedan trabajar con CUDA/OpenCL, configuramos la ruta al directorio con las bibliotecas CUDA en la variable LD_LIBRARY_PATH:

$ exportar LD_LIBRARY_PATH=/usr/local/cuda/lib64

O, si instaló la versión de 32 bits:

$ exportar LD_LIBRARY_PATH=/usr/local/cuda/lib32

También debe especificar la ruta a los archivos de encabezado CUDA para que el compilador los encuentre en la etapa de compilación de la aplicación:

$ exportar C_INCLUDE_PATH=/usr/local/cuda/include

Eso es todo, ahora puedes empezar a crear software CUDA/OpenCL.

Instalar el SDK de ATI Stream

Stream SDK no requiere instalación, por lo que simplemente puede descomprimir el archivo AMD descargado del sitio web en cualquier directorio (/opt es la mejor opción) y escribir la ruta en la misma variable LD_LIBRARY_PATH:

$wget http://goo.gl/CNCNo

$ sudo tar -xzf ~/AMD-APP-SDK-v2.4-lnx64.tgz -C /opt

$ exportar LD_LIBRARY_PATH=/opt/AMD-APP-SDK-v2.4-lnx64/lib/x86_64/

$ exportar C_INCLUDE_PATH=/opt/AMD-APP-SDK-v2.4-lnx64/include/

Al igual que con CUDA Toolkit, x86_64 debe reemplazarse por x86 en sistemas de 32 bits. Ahora vaya al directorio raíz y descomprima el archivo icd-registration.tgz (este es un tipo de clave de licencia gratuita):

$ sudo tar -xzf /opt/AMD-APP-SDK-v2.4-lnx64/icd-registration.tgz - CON /

Comprobamos la correcta instalación/funcionamiento del paquete mediante la herramienta clinfo:

$ /opt/AMD-APP-SDK-v2.4-lnx64/bin/x86_64/cinfo

ImageMagick y OpenCL

La compatibilidad con OpenCL ha estado disponible en ImageMagick desde hace bastante tiempo, pero no está habilitada de forma predeterminada en ninguna distribución. Por lo tanto, tendremos que compilar IM nosotros mismos desde el código fuente. No hay nada complicado en esto, todo lo que necesita ya está en el SDK, por lo que el ensamblaje no requiere la instalación de bibliotecas adicionales de nVidia o AMD. Entonces, descarga/descomprime el archivo con las fuentes:

$wget http://goo.gl/F6VYV

$ tar -xjf ImageMagick-6.7.0-0.tar.bz2

$cdImageMagick-6.7.0-0

$ sudo apt-get install compilación esencial

Lanzamos el configurador y tomamos su salida para soporte OpenCL:

$ LDFLAGS=-L$LD_LIBRARY_PATH ./confi gura | grep -e cl.h -e OpenCL

La salida correcta del comando debería verse así:

comprobando la usabilidad de CL/cl.h... sí

comprobando presencia de CL/cl.h... sí

comprobando CL/cl.h... sí

comprobando la usabilidad de OpenCL/cl.h... no

comprobando la presencia de OpenCL/cl.h... no

buscando OpenCL/cl.h... no

buscando la biblioteca OpenCL... -lOpenCL

La palabra "sí" debe marcarse en las tres primeras líneas o en la segunda (o en ambas opciones a la vez). Si este no es el caso, lo más probable es que la variable C_INCLUDE_PATH no se haya inicializado correctamente. Si la última línea está marcada con la palabra "no", entonces el problema está en la variable LD_LIBRARY_PATH. Si todo está bien, inicie el proceso de compilación/instalación:

$ sudo hacer que la instalación sea limpia

Comprobemos que ImageMagick realmente se compiló con soporte OpenCL:

$ /usr/local/bin/convert -version | Características de grep

Características: OpenMP OpenCL

Ahora midamos la ganancia de velocidad resultante. Los desarrolladores de ImageMagick recomiendan utilizar el filtro de convolución para esto:

$ tiempo /usr/bin/convertir imagen.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" imagen2.jpg

$ tiempo /usr/local/bin/convertir imagen.jpg -convolve "-1, -1, -1, -1, 9, -1, -1, -1, -1" imagen2.jpg

Algunas otras operaciones, como cambiar el tamaño, ahora también deberían funcionar mucho más rápido, pero no debe esperar que ImageMagick comience a procesar gráficos a una velocidad vertiginosa. Hasta ahora, una parte muy pequeña del paquete se ha optimizado utilizando OpenCL.

FlacCL (Flacuda)

FlacCL es un codificador de archivos de audio en formato FLAC, que utiliza las capacidades de OpenCL en su trabajo. Está incluido en el paquete CUETools para Windows, pero gracias a mono también se puede utilizar en Linux. Para obtener un archivo con un codificador, ejecute el siguiente comando:

$ mkdir flaccl && cd flaccl

$wgetwww.cuetools.net/install/flaccl03.rar

$ sudo apt-get install unrar mono

$ unrar x fl accl03.rar

Para que el programa pueda encontrar la biblioteca OpenCL, realizamos un enlace simbólico:

$ ln -s $LD_LIBRARY_PATH/libOpenCL.so libopencl.so

Ahora ejecutemos el codificador:

$ mono CUETools.FLACCL.cmd.exe música.wav

Si aparece en la pantalla el mensaje de error "Error: el tamaño de compilación solicitado es mayor que el tamaño requerido del grupo de trabajo de 32", entonces la tarjeta de video de nuestro sistema es demasiado débil y la cantidad de núcleos involucrados debe reducirse al número especificado. usando el '-- flag group-size XX', donde XX es el número requerido de núcleos.

Diré de inmediato que debido al largo tiempo de inicialización de OpenCL, solo se pueden obtener ganancias notables en pistas suficientemente largas. FlacCL procesa archivos de audio cortos casi a la misma velocidad que su versión tradicional.

oclHashcat o fuerza bruta rápidamente

Como ya dije, los desarrolladores de varios crackers y sistemas de contraseñas de fuerza bruta estuvieron entre los primeros en agregar soporte GPGPU a sus productos. Para ellos, la nueva tecnología se convirtió en un verdadero santo grial, que hizo posible transferir fácilmente código paralelizado de forma natural a los procesadores GPU rápidos. Por lo tanto, no es sorprendente que ahora existan docenas de implementaciones diferentes de este tipo de programas. Pero en este artículo hablaré sólo de uno de ellos: oclHashcat.

oclHashcat es un hacker que puede adivinar contraseñas usando su hash a una velocidad extremadamente alta, usando la potencia de la GPU usando OpenCL. Si nos fijamos en las mediciones publicadas en el sitio web del proyecto, la velocidad de selección de contraseñas MD5 en la nVidia GTX580 es de hasta 15.800 millones de combinaciones por segundo, gracias a lo cual oclHashcat es capaz de encontrar una contraseña de ocho caracteres de complejidad media en sólo 9 minutos.

El programa admite los algoritmos OpenCL y CUDA, MD5, md5($pass.$salt), md5(md5($pass)), vBulletin< v3.8.5, SHA1, sha1($pass.$salt), хэши MySQL, MD4, NTLM, Domain Cached Credentials, SHA256, поддерживает распределенный подбор паролей с задействованием мощности нескольких машин.

$7z x oclHashcat-0.25.7z

$cd oclHashcat-0.25

Y ejecute el programa (usaremos una lista de muestra de hashes y un diccionario de muestra):

$ ./oclHashcat64.bin ejemplo.hash ?l?l?l?l ejemplo.dict

oclHashcat abrirá el texto del acuerdo de usuario, que deberá aceptar escribiendo "SÍ". Luego de esto comenzará el proceso de búsqueda, cuyo progreso se puede conocer presionando . Para pausar el proceso, haga clic en

Resumir - . También puede utilizar la enumeración directa (por ejemplo, de aaaaaaaa a zzzzzzzz):

$ ./oclHashcat64.bin hash.txt ?l?l?l?l ?l?l?l?l

Y varias modificaciones del diccionario y del método de búsqueda directa, así como sus combinaciones (puedes leer sobre esto en el archivo docs/examples.txt). En mi caso, la velocidad de búsqueda en todo el diccionario fue de 11 minutos, mientras que la búsqueda directa (de aaaaaaaa a zzzzzzzz) duró unos 40 minutos. La velocidad media de la GPU (chip RV710) fue de 88,3 millones/s.

conclusiones

A pesar de muchas limitaciones diferentes y de la complejidad del desarrollo de software, GPGPU es el futuro de las computadoras de escritorio de alto rendimiento. Pero lo más importante es que puedes utilizar las capacidades de esta tecnología ahora mismo, y esto se aplica no sólo a las máquinas con Windows, sino también a las de Linux.