En la programación de software, ¿sería posible tener cargas de CPU y GPU al 100%?

43

Esta es una pregunta general sobre un tema que he encontrado interesante como jugador: cuellos de botella y programación de CPU / GPU. Si no me equivoco, he llegado a comprender que tanto la CPU como la GPU calculan cosas, pero esa es mejor en algunos cálculos que la otra debido a la diferencia en la arquitectura. Por ejemplo, descifrar hashes o minería de criptomonedas parece mucho más eficiente en las GPU que en las CPU.

Entonces me he preguntado: ¿es inevitable tener una GPU al 100% de carga mientras que la CPU está al 50% (por ejemplo)?

O, más precisamente: ¿Pueden algunos cálculos que normalmente realiza la GPU la CPU si el primero tiene una carga del 100%, de modo que ambos alcancen una carga del 100%?

He buscado un poco sobre el tema, pero he vuelto con las manos vacías. ¡Creo y espero que esto tenga su lugar en esta subsección y estoy abierto a cualquier documentación o conferencia que me puedan dar!

MadWard
fuente
53
Es trivialmente posible que tanto la CPU como la GPU ejecuten un bucle infinito de NO-OPs al mismo tiempo, lo que hará que ambos tengan una carga del 100%.
Jörg W Mittag
17
Siguiendo el punto de @ Jörg, lo único medido por el% de CPU es qué fracción de tiempo no se pasa esperando a otros procesadores. El 100% puede ser algo bueno si el programa es eficiente, o algo malo si el programa es ineficiente. La mayor parte del tiempo, las personas se centran en el% de CPU como si fuera una medida de rendimiento, no lo es.
Mike Dunlavey
22
El Crysis original hizo esto muy bien.
CubicleSoft
55
@MikeDunlavey traes un buen punto. Con los automóviles no medimos su rendimiento por RPM, medimos la velocidad.
Capitán Man
1
@ JörgWMittag: La CPU, tal vez. Pero los sistemas operativos y las GPU tienen solucionadores de problemas para detener los bucles infinitos. Es decir, si un sombreador no se completa en un período de tiempo razonable, muere y la GPU se restablece.
Nicol Bolas

Respuestas:

62

Teóricamente sí, pero prácticamente raramente vale la pena.

Tanto las CPU como las GPU están completas , por lo que cualquier algoritmo que pueda ser calculado por uno también puede ser calculado por el otro. La pregunta es qué tan rápido y qué tan conveniente.

Si bien la GPU es excelente para hacer los mismos cálculos simples en muchos puntos de datos de un gran conjunto de datos, la CPU es mejor en algoritmos más complejos con muchas ramificaciones. Con la mayoría de los problemas, la diferencia de rendimiento entre las implementaciones de CPU y GPU es enorme. Eso significa que usar uno para quitarle el trabajo al otro cuando se está estancando realmente no conduciría a un aumento notable en el rendimiento.

Sin embargo, el precio que debe pagar por esto es que debe programar todo dos veces, una para la CPU y otra para la GPU. Eso es más del doble de trabajo porque también tendrá que implementar la lógica de conmutación y sincronización. Esa lógica es extremadamente difícil de probar, porque su comportamiento depende de la carga actual. Espere errores muy oscuros e imposibles de reproducir de este truco.

Philipp
fuente
1
Usted mencionó que con la mayoría de los problemas, la diferencia de rendimiento entre las implementaciones de CPU y GPU es enorme , en realidad estoy bastante interesado hasta qué punto la brecha de rendimiento va. ¿Tendría algún número o artículo sobre esto (por ejemplo, en el ejemplo de renderizado 3D de texturas)? ¡Gracias por tu respuesta y por tu tiempo!
MadWard
2
Es posible que desee agregar que existen costos de rendimiento para la sincronización entre la CPU y la GPU, por lo que generalmente desea minimizar el número de transferencias entre los dos. Además, agregar ingenuamente ramas para "no ejecutar en los elementos en los que la CPU ya trabajó" no le compraría nada, ya que los subprocesos de la GPU operan en bloque.
Ethan
3
@gardenhead Nada en el universo admite la recursión ilimitada, porque el universo es de tamaño finito y tiene densidad de información finita. La "integridad de Turing" de un sistema es generalmente una discusión de lo que sería posible con tales restricciones eliminadas.
Random832
3
No tengo dudas de que una GPU moderna es técnicamente al menos tan cercana a la integridad de Turing como una PC de los 80 ... sin embargo, si intenta ejecutar algoritmos generales en una GPU, generalmente degenerará en un procesador secuencial que tampoco será más rápido que una PC de los 80, por lo que la integridad de Turing de una GPU es en la práctica apenas más útil que la integridad de Turing de Brainfuck .
Leftaroundabout
77
@leftaroundabout Las GPU modernas son trivialmente completas como cualquier CPU . La integridad de Turing no tiene nada que ver con: 1) rendimiento 2) legibilidad de la fuente. La CPU de los 80 estaba tan cerca de TC como todo lo demás: o eran TC o no (la última opción es una tontería).
Margaret Bloom
36

No está relacionado con la programación del juego. Algunos códigos científicos también pueden usar tanto la GPU como la CPU.

Con una programación cuidadosa y dolorosa, por ejemplo, al usar OpenCL o CUDA , puede cargar su GPU y su CPU cerca del 100%. Es muy probable que necesite escribir diferentes piezas de código para la GPU (llamado código "kernel") y para la CPU, y algún código de pegamento aburrido (especialmente para enviar a la GPU el código del kernel compilado).

Sin embargo, el código sería complejo y probablemente deba ajustarlo al hardware particular en el que se está ejecutando, en particular porque la transmisión de datos entre GPU y CPU es costosa.

Lea más sobre computación heterogénea .

Consulte también OpenACC , compatible con versiones recientes de GCC (por ejemplo, GCC 6 en junio de 2016)

Basile Starynkevitch
fuente
1
Tienes razón, mis etiquetas y mi título fueron engañosos, eliminaron juegos y agregaron rendimiento / optimización. No quise decir que era exclusivo de los juegos, pero ahí es donde lo noté. Pensé que también tendría que ser muy específico del hardware. Gracias por tu respuesta y enlaces!
MadWard
3
Esto prácticamente terminaría con dos algoritmos. Lo intenté una vez: imagen completa a la vez para GPU y varias imágenes a la vez para CPU (para abusar de la memoria caché grande). De hecho, es doloroso, especialmente para mantener.
PTwr
11

Desde el punto de vista de la supercomputación, es mejor no pensar en la carga de la CPU / GPU en porcentaje, sino determinar cuántas operaciones necesita su problema en cuestión y luego compararlo con el rendimiento máximo del sistema.

Si obtiene un 100% de utilización de la CPU, no necesariamente significa que obtendrá todo el rendimiento del sistema. Las CPU a menudo pueden hacer varias cosas diferentes al mismo tiempo, digamos una división y una adición. Si puede comenzar la división temprano, posiblemente se puede superponer con la adición. Su CPU de escritorio probablemente tenga una unidad fuera de servicio que reordenará las declaraciones para beneficiarse de tales superposiciones. O si tienes el siguiente programa:

if (expr1)
    expr2;
else
    expr3;

Una CPU reordenada intentará calcular las tres expresiones al mismo tiempo y luego descartará el resultado de una de ellas. Esto lo hace más rápido en general. Si tiene algún bloqueador en su programa y no puede reordenar, entonces está utilizando menos carriles en la CPU, pero probablemente todavía mostrará el 100%.

Entonces tiene características SIMD en las CPU que son operaciones vectoriales. Es como GPGPU-light en el sentido de que generalmente solo tiene cuatro u ocho operaciones al mismo tiempo, las GPU hacen 32 o 64. Sin embargo, debe usar eso para arrancar los FLOPS.

Cosas como el intercambio falso pueden generar un alto costo de sincronización que generalmente aparece como carga del kernel en Linux. La CPU se usa por completo, pero no tiene un rendimiento muy útil.

He hecho algo de programación en una máquina IBM Blue Gene / Q. Tiene muchos niveles de jerarquía ( esquema de Blue Gene / L obsoleto ) y, por lo tanto, es difícil de programar de manera eficiente. Tendrá que usar la jerarquía completa hasta SIMD y SMT (Intel llama a esto HyperThreading) para obtener el rendimiento.

Y luego la red a menudo te limita. Por lo tanto, resulta que es más rápido en tiempo (reloj de pared) calcular cosas en múltiples CPU al mismo tiempo en lugar de comunicarlo a través de la red. Esto pondrá más carga en las CPU y hará que el programa se ejecute más rápido. Pero el rendimiento real del programa no es tan bueno como parece por los números sin procesar.

Si agrega GPU a la mezcla, será aún más difícil organizar todo esto para obtener un rendimiento. Esa será una de las cosas que comenzaré a hacer en mi Tesis de maestría de Lattice QCD en un par de meses.

Martin Ueding
fuente
1

Puede que le interese comprobar el motor del navegador Servo que se está desarrollando en Mozilla Research y, más específicamente, su Web Render (video) .

Si bien cambiar una tarea de CPU a GPU dinámicamente puede no ser práctico, como se menciona en otras respuestas (especialmente @ Philip's), puede ser práctico estudiar de antemano la carga de CPU / GPU en cargas de trabajo típicas y cambiar algunas tareas a las generalmente menos cargadas uno.

En el caso de Web Render, la novedad es que tradicionalmente los navegadores realizan la mayor parte de su trabajo de renderizado en la CPU (es decir, la CPU se usa para calcular qué objetos mostrar, dónde cortar, etc.). La GPU normalmente es mejor en eso ... excepto que no todos los casos de uso son triviales de implementar (eliminación parcial, sombras, ... y texto).

Una versión inicial de Web Render demostró ser muy exitosa en el aumento del rendimiento, pero no trató de abordar el problema de la representación de texto (y tenía algunas otras limitaciones). Mozilla Research ahora está trabajando en una segunda versión que tiene menos limitaciones y, en particular, es compatible con la representación de texto.

El objetivo, por supuesto, es descargar la mayor cantidad posible del proceso de renderización a la GPU, dejando a la CPU libre para ejecutar Javascript, actualizar el DOM y todas las demás tareas.

Por lo tanto, aunque no es tan extremo como su sugerencia, va en la dirección de diseñar una estrategia de cálculo teniendo en cuenta tanto la CPU como la GPU.

Matthieu M.
fuente
0

Con un enfoque en los juegos (ya que lo mencionó específicamente en su publicación), hay algunas formas en que puede equilibrar la carga. Un ejemplo es "desollar", es decir, animar un modelo. Para cada cuadro a renderizar, debe generar las matrices de transformación para cada cuadro de animación y aplicarlo a los vértices del modelo para transformarlo en la pose en la que debe estar. También debe interpolar cuadros para obtener un movimiento suave , a menos que desee que su animación se vea como el Quake original (es decir, desigual).

En esta situación, puede hacerlo en la CPU y cargar los resultados en la GPU para renderizar, o hacer el cálculo y renderizar en la GPU. Creo que hoy en día se hace en la GPU (conocida como "skinning de hardware"): tiene sentido hacerlo dado que tiene cálculos relativamente simples que deben realizarse miles de veces, y cada vértice puede calcularse simultáneamente desde el resultado del vértice A no tiene relación con el resultado del vértice B.

Sin embargo, en teoría, podría cambiar dinámicamente entre hacerlo en la CPU o GPU dependiendo de qué tan sobrecargadas estén la GPU y la CPU.

Sin embargo, el principal bloqueador para hacer esto en todos los cálculos es que la CPU y la GPU tienen diferentes fortalezas y debilidades. Los trabajos masivamente paralelos se realizan mejor en la GPU, mientras que las tareas lineales intensivas con ramificación se realizan mejor en la CPU. Solo unos pocos trabajos podrían realizarse de manera realista en ambos sin un impacto serio en el rendimiento.

En general, el principal problema con la programación de GPU (al menos con OpenGL y DirectX 11 y versiones anteriores) es que tienes poco control sobre cómo la GPU interpreta tu código de sombreador. La ramificación dentro de un sombreador es arriesgada porque si accidentalmente crea una dependencia entre los cálculos, la GPU puede decidir comenzar a renderizar sus píxeles uno por uno, convirtiendo 60 fps a 10 fps en un instante a pesar de que los datos reales serán idénticos.

Richard Greenlees
fuente
0

Un ejemplo del mundo real es el motor de renderizado LuxRender de código abierto , que es capaz de cargar completamente una CPU y GPU al mismo tiempo. Además, puede cargar múltiples GPU al mismo tiempo y también puede distribuirse en múltiples computadoras.

LuxRender usa OpenCL para facilitar esto, aunque también existen compilaciones sin OpenCL.

Esto es práctico porque los algoritmos que utiliza LuxRender son altamente paralelizables. El algoritmo más común que utiliza LuxRender es el trazado de ruta , donde muchas rutas de luz individuales se pueden calcular de forma independiente, una situación ideal para la computación de GPU y una que no requiere sincronización compleja entre nodos de computación. Sin embargo, las limitaciones de las GPU (cantidades menores de memoria, falta de soporte para algunas características de renderizado complejas y falta general de disponibilidad para algunos artistas) aseguran que el soporte de CPU sigue siendo esencial.

PythonNut
fuente
¿Cuál es el punto de mostrar esta imagen, cómo es relevante para la pregunta que se hace?
mosquito
1
Ehh bien Lo borraré. Estaba pensando que demostraría fácilmente qué tipo de software es. Pero tal vez es realmente una distracción. (Hay muchos tipos diferentes de motores de renderizado; este está dirigido a imágenes fotorrealistas).
PythonNut
0

Sí, ciertamente es posible.

Cualquier cálculo que pueda hacer una CPU, una GPU también puede hacer, y viceversa.

Pero es poco común porque:

  • Complejidad de ingeniería Si bien es posible ejecutar el mismo código en una CPU y GPU (por ejemplo, CUDA), los procesadores tienen diferentes capacidades y características de rendimiento. Uno es MIMD; el otro, SIMD. Lo que es rápido en uno es lento en el otro (por ejemplo, ramificación), por lo que debe escribir un código separado para maximizar el rendimiento.

  • Las GPU de rentabilidad son, en conjunto, mucho más potentes que las CPU. La idea general de las GPU es usar procesadores más baratos, más lentos, pero más numerosos para realizar cálculos mucho más rápido que las CPU por el mismo costo. Las GPU son más eficientes en cuanto a costos en uno o dos órdenes de magnitud.

Si logra que su algoritmo se ejecute en GPU, tiene más sentido optimizarlo y agregar tantos como necesite.

Paul Draper
fuente