f# - Programación funcional y arquitectura multinúcleo
functional-programming multicore (9)
He leído en alguna parte que la programación funcional es adecuada para aprovechar la tendencia multi-core en la informática ... Realmente no entendí la idea. ¿Está relacionado con el cálculo lambda y la arquitectura von Neumann?
El argumento detrás de la creencia que citó es que la programación puramente funcional controla los efectos secundarios, lo que hace que sea mucho más fácil y seguro introducir el paralelismo y, por lo tanto, que los lenguajes de programación puramente funcionales deberían ser ventajosos en el contexto de las computadoras multinúcleo.
Desafortunadamente, esta creencia fue refutada por mucho tiempo por varias razones:
El rendimiento absoluto de las estructuras de datos puramente funcionales es pobre . Así que la programación puramente funcional es un gran paso inicial en la dirección incorrecta en el contexto del rendimiento (que es el único propósito de la programación paralela).
Las estructuras de datos puramente funcionales se escalan mal porque hacen hincapié en los recursos compartidos, incluido el asignador / GC y el ancho de banda de la memoria principal. Por lo tanto, los programas puramente paralelizados a menudo obtienen reducciones de velocidad a medida que aumenta el número de núcleos.
La programación puramente funcional hace que el rendimiento sea impredecible. Así que los programas puramente funcionales a menudo ven la degradación del rendimiento cuando se paralelizan porque la granularidad es efectivamente aleatoria.
Por ejemplo, el quicksort bastardo de dos líneas, a menudo citado por la comunidad Haskell, generalmente funciona miles de veces más lento que un quicksort real escrito en un lenguaje más convencional como F #. Además, aunque puedes paralelizar fácilmente el elegante programa Haskell, es poco probable que veas una mejora en el rendimiento porque toda la copia innecesaria hace que un solo núcleo sature todo el ancho de banda de la memoria principal de una máquina multinúcleo, lo que hace que el paralelismo carezca de valor. De hecho, nadie ha logrado escribir ningún tipo de paralelismo genérico en Haskell que sea competitivo. Los géneros de vanguardia proporcionados por la biblioteca estándar de Haskell son, por lo general, cientos de veces más lentos que las alternativas convencionales.
Sin embargo, la definición más común de programación funcional como un estilo que enfatiza el uso de funciones de primera clase realmente resulta ser muy útil en el contexto de la programación multinúcleo porque este paradigma es ideal para factorizar programas paralelos. Por ejemplo, vea la nueva función Parallel.For
orden superior desde el System.Threading.Tasks
nombres System.Threading.Tasks
en .NET 4.
He leído en alguna parte que la programación funcional es adecuada para aprovechar la tendencia multi-core en la informática. Realmente no entiendo la idea. ¿Está relacionado con el cálculo lambda y la arquitectura von Neumann?
Cuando no hay efectos secundarios, el orden de evaluación no importa. Entonces es posible evaluar expresiones en paralelo.
El argumento básico es que es difícil paralelizar automáticamente idiomas como C / C ++ / etc porque las funciones pueden establecer variables globales. Considere dos llamadas a funciones:
a = foo(b, c);
d = bar(e, f);
Aunque foo y bar no tienen argumentos en común y uno no depende del código de retorno del otro, de todos modos pueden tener dependencias porque foo podría establecer una variable global (u otro efecto secundario) de la que depende la barra.
Los lenguajes funcionales garantizan que foo y barra son independientes: no hay elementos globales ni efectos secundarios. Por lo tanto, foo y bar se pueden ejecutar de forma segura en diferentes núcleos, de forma automática, sin intervención del programador.
El libro Programming Erlang: Software for a Concurrent World de Joe Armstrong (el creador de Erlang ) habla bastante sobre el uso de Erlang para sistemas multinúcleo (/ multiprocesador). Como dice el artículo de wikipedia:
Crear y gestionar procesos es trivial en Erlang, mientras que los hilos se consideran un tema complicado y propenso a errores en la mayoría de los lenguajes. Aunque toda la concurrencia es explícita en Erlang, los procesos se comunican mediante el envío de mensajes en lugar de las variables compartidas, lo que elimina la necesidad de bloqueos.
Esta es una pequeña pregunta vaga. Una ventaja de las CPU multinúcleo es que puede ejecutar un programa funcional y dejar que se desconecte en serie sin preocuparse por afectar el funcionamiento de la informática que tiene que ver con otras funciones que la máquina está llevando a cabo.
La diferencia entre un servidor multi-U y una CPU de varios núcleos en un servidor o PC es el ahorro de velocidad que se obtiene al tenerlo en el mismo BUS, lo que permite una comunicación mejor y más rápida con los núcleos.
editar: Probablemente califique esta publicación diciendo que en la mayoría de las secuencias de comandos que hago, con o sin núcleos múltiples, rara vez veo un problema para obtener mis datos a través de la paralelización, como ejecutar múltiples scripts pequeños a la vez en mi script, así No me ralentizan cosas como esperar a que se carguen las URL y qué no.
edición doble: Además, muchos lenguajes de programación funcionales han tenido variantes paralelas durante décadas. Éstos utilizan mejor el cálculo en paralelo con alguna mejora de velocidad, pero nunca lograron alcanzarlo.
La programación funcional minimiza o elimina los efectos secundarios y, por lo tanto, es más adecuada para la programación distribuida. es decir, procesamiento multinúcleo.
En otras palabras, muchas piezas del rompecabezas se pueden resolver de forma independiente en núcleos separados al mismo tiempo sin tener que preocuparse de que una operación afecte a otra casi tanto como lo haría en otros estilos de programación.
Omitiendo cualquier término técnico / científico, la razón es porque el programa funcional no comparte datos. Los datos se copian y transfieren entre funciones, por lo que no hay datos compartidos en la aplicación.
Y los datos compartidos son lo que causa la mitad de los dolores de cabeza con multihilo.
Todas las respuestas anteriores van a la idea clave de que "ningún almacenamiento mutable compartido" es un habilitador clave para ejecutar partes de un programa en paralelo. Realmente no resuelve el problema igualmente difícil de encontrar cosas para ejecutar en paralelo. Pero las típicas expresiones más claras de la funcionalidad en los lenguajes funcionales hacen que teóricamente sea más fácil extraer el paralelismo de una expresión secuencial.
En la práctica, creo que la propiedad "sin almacenamiento mutable compartido" de los lenguajes basados en la recolección de basura y la semántica de copia en el cambio hace que sea más fácil agregar subprocesos. El mejor ejemplo es probablemente Erlang, que combina semántica casi funcional con hilos explícitos.
Una de las cosas más difíciles de tratar con el procesamiento paralelo es bloquear las estructuras de datos para evitar la corrupción. Si dos subprocesos mudaran una estructura de datos a la vez sin tenerlo bloqueado a la perfección, se podría producir cualquier cosa, desde datos no válidos hasta un punto muerto.
Por el contrario, los lenguajes de programación funcionales tienden a enfatizar los datos inmutables. Cualquier estado se mantiene separado de la lógica, y una vez que se crea una estructura de datos, no se puede modificar. La necesidad de bloqueo se reduce en gran medida.
Otro beneficio es que algunos procesos que se paralelizan muy fácilmente, como iteración, se abstraen a funciones. En C ++, es posible que tenga un bucle for que ejecuta algunos procesos de datos sobre cada elemento de una lista. Pero el compilador no tiene manera de saber si esas operaciones se pueden ejecutar de forma segura en paralelo, tal vez el resultado de una depende del anterior. Cuando se usa una función como map()
o reduce()
, el compilador puede saber que no hay dependencia entre las llamadas. Por lo tanto, se pueden procesar múltiples elementos al mismo tiempo.