Pesimismo sobre el multiproceso

La concurrencia masiva y de hardware son temas candentes del siglo XXI. Hay varias buenas razones para esto, y una bastante triste.

Dos buenas razones: una combinación de excelente rendimiento de la GPU en los juegos y, al mismo tiempo, su uso secundario inesperado en el aprendizaje profundo de la IA, ya que el paralelismo masivo se implementa a nivel de hardware allí. La triste razón es que la velocidad de los sistemas uniprocesadores ha estado en contra de las leyes de la física desde 2006. Los problemas actuales con fugas y ruptura térmica limitan drásticamente el aumento de la frecuencia de reloj, y la caída de voltaje clásica ahora se encuentra con serios problemas con el ruido cuántico.

Compitiendo por la atención del público, los fabricantes de procesadores están tratando de introducir más y más núcleos de procesador en cada chip, promocionando el rendimiento general teórico. Los esfuerzos de transporte y los métodos de ejecución especulativos, que utilizan subprocesos múltiples bajo el capó, también están creciendo rápidamente, de modo que un único procesador visible para el programador puede procesar las instrucciones más rápido.

La verdad incómoda es que muchas de nuestras tareas informáticas menos glamorosas simplemente no pueden usar multithreading visible muy bien. Hay varias razones para esto, que tienen diferentes consecuencias para el programador, y hay mucha confusión. En este artículo quiero aclarar un poco la situación.

Primero, debe comprender claramente dónde funciona mejor el paralelismo de hardware y por qué. Veamos los cálculos de gráficos, redes neuronales, procesamiento de señales y minería de bitcoins. Hay un patrón: los algoritmos de paralelización funcionan mejor en equipos que están (a) especialmente diseñados para ejecutarlos; (b) no puede hacer nada más!

También vemos que la entrada para los algoritmos paralelos más exitosos (clasificación, coincidencia de cadenas, transformación rápida de Fourier, operaciones matriciales, cuantización inversa de imágenes, etc.) se ve bastante similar. Como regla general, tienen una estructura métrica y la diferencia entre datos "cercanos" y "distantes" está implícita, lo que nos permite cortarlos en partes, ya que la conexión entre los elementos distantes es insignificante.

En términos del último artículo sobre localidad semántica, podemos decir que los métodos paralelos son principalmente aplicables cuando los datos tienen buena localidad. Y funcionan mejor en equipos que solo admiten conexiones de "corto alcance", como la matriz sistólica en el corazón de la GPU.

Por otro lado, es muy difícil escribir software que produzca efectivamente una sección para datos de entrada con poca localidad en computadoras de uso general (arquitectura von Neumann).

Como resultado, podemos formular una heurística simple: las posibilidades de utilizar la computación paralela son inversamente proporcionales al grado de no localidad semántica irreducible en los datos de entrada.

Otra limitación de la computación paralela es que algunos algoritmos importantes no pueden ser paralelizados en absoluto, incluso teóricamente. Cuando discutí este tema por primera vez en mi blog, se me ocurrió el término "algoritmo enfermo", donde SICK significa "Serie, intrínsecamente: ¡Cope, Kiddo!" Ejemplos significativos incluyen: el algoritmo de Dijkstra para encontrar el camino más corto; detección de ciclos en gráficos dirigidos (usando 3-SAT en solucionadores); búsqueda profunda calcular el enésimo miembro en la cadena de hash criptográfica; optimización de flujo de red ... y esta no es una lista completa.

La pobre localidad de los datos de entrada también juega un papel aquí, especialmente en los contextos del gráfico y la estructura de árbol. Las cadenas hash criptográficas no se pueden paralelizar, porque los registros se calculan en orden estricto; esta es realmente una regla importante para proteger la cadena de la falsificación.

Y aquí entra el bloqueo: no puedes paralelizar nada mientras el algoritmo SICK esté funcionando.

No hemos terminado Hay al menos dos clases de obstáculos, y muy comunes.

En primer lugar, no hay herramientas necesarias. La mayoría de los idiomas no admiten nada más que un mutex y semáforos. Esto es conveniente, las primitivas son fáciles de implementar, pero esta situación causa explosiones terribles de complejidad en la cabeza: es casi imposible comprender la escala de más de cuatro cerraduras que interactúan.

Si tiene suerte, obtendrá un conjunto de primitivas más complaciente, como los canales Go (también conocidos como Procesos secuenciales de comunicación) o el sistema de propiedad / envío / sincronización de Rust. Pero, de hecho, no sabemos cuál es el lenguaje "correcto" de las primitivas para la implementación del paralelismo en la arquitectura von Neumann. Quizás ni siquiera hay un conjunto correcto de primitivas. Quizás dos o tres conjuntos diferentes son adecuados para diferentes áreas problemáticas, pero son inconmensurables como una unidad y una raíz cuadrada de dos. Hasta la fecha, en 2018 nadie lo sabe realmente.

Y la última, pero no menos importante limitación es el cerebro humano. Incluso en un algoritmo claro con buena ubicación de datos y herramientas eficientes, la programación paralela es simplemente difícil para las personas, incluso si el algoritmo se aplica de manera bastante simple. Nuestro cerebro no modela muy bien los espacios de estado más simples de los programas puramente secuenciales, y especialmente los paralelos.

Sabemos esto porque hay mucha evidencia real de que depurar código paralelo es más que difícil. Esto se ve obstaculizado por las condiciones de carrera, los puntos muertos, las cerraduras autodestructivas, la corrupción insidiosa de datos debido a un orden de instrucciones ligeramente inseguro.

Creo que comprender estas limitaciones se vuelve más importante después del colapso de la ley de ampliación de Dennard . Debido a todos estos cuellos de botella en la programación, parte de los sistemas multinúcleo siempre ejecutará software que no pueda cargar equipos al 100% de la potencia de cómputo. Si miras desde el otro lado, tenemos un exceso de hierro para las tareas actuales. ¿Cuánto dinero y esfuerzo estamos desperdiciando?

Los fabricantes de procesadores quieren que sobreestime los beneficios funcionales de los nuevos chips inteligentes con aún más núcleos. ¿De qué otra manera pueden recaudar dinero para cubrir los costos de producción gigantescos, sin dejar de ser rentables? El marketing está haciendo todo lo posible para que nunca te preguntes qué tareas son tan beneficiosas.

Honestamente, hay tales tareas. Es probable que los servidores en los centros de datos que procesan cientos de miles de transacciones simultáneas por segundo distribuyan la carga bastante bien entre los núcleos. Los teléfonos inteligentes o los sistemas integrados también: en ambos casos, se realizan esfuerzos significativos para minimizar el costo y el consumo de energía, lo que dificulta la puesta en servicio del exceso de energía.

¿Pero para los usuarios comunes de computadoras de escritorio y portátiles? Vagas dudas me atormentan. Es difícil entender la situación aquí, porque el aumento real de la productividad proviene de otros factores, como la transición de HDD a SSD. Tales logros se confunden fácilmente con el efecto de acelerar la CPU, si no realiza un perfil completo.

Aquí están los motivos para tales sospechas:

  1. La computación paralela seria en computadoras de escritorio / portátiles ocurre solo en la GPU.
  2. Más de dos núcleos en un procesador suelen ser inútiles. Los sistemas operativos pueden distribuir flujos de aplicaciones, pero el software típico no puede usar el paralelismo, y la mayoría de los usuarios rara vez logran lanzar simultáneamente una gran cantidad de aplicaciones diferentes que consumen muchos recursos de la CPU para cargar completamente su equipo.
  3. En consecuencia, la mayoría de los sistemas de cuatro núcleos no hacen más que generar calor.

Entre mis lectores hay muchas personas que probablemente puedan comentar razonablemente esta hipótesis. Es interesante ver lo que dicen.

ACTUALIZAR El comentarista en G + señaló un beneficio interesante de los procesadores multi-core: compilan el código muy rápidamente. El código fuente de lenguajes como C tiene buena localidad: aquí, las unidades bien separadas (archivos fuente) se compilan en archivos de objetos, que luego combina el enlazador.

Source: https://habr.com/ru/post/es434698/


All Articles