El tema de los multinúcleos (Parte 2: Paralelización)

Como ya habíamos visto, la prostitución de los núcleos en los procesadores en serie más que convertirse en una real solución al cómputo cotidiano, se ha convertido en una herramienta de marketing (que lleva, incluso, a la generación de núcleos virtuales para, según, mejorar el rendimiento de la computadora, cosa que no es del todo cierta, y que solo se ve beneficiada por el uso de aceleradores intrínsecos de reloj, como TurboCore o TurboBoost). Sin embargo, la paralelización es un método muy utilizado en el ámbito del servidor, aunque rara vez se utiliza en el ámbito del cómputo personal. Y aquí es donde entra mi corazón de programador.

Para ponerlo en contexto, hablemos un poco de la Programación Orientada a Objetos (OOP, por sus siglas en inglés). La OOP es un paradigma que establece un tripode (encapsulamiento, herencia y polimorfismo) y características específicas de los objetos (estados, propiedades y métodos, y opcionalmente eventos en el caso de este tipo de programación) para un entorno completo que hoy (nos guste o no) es la forma estándar de programar. Hay objetos visuales o integrales de la plataforma de desarrollo (componentes) y hay objetos construidos con una finalidad en particular (objetos per se, o componentes de usuario), etcétera. Hay tratados enormes que hablan de la OOP (hay algunas referencias simples en http://es.wikipedia.org/wiki/Programaci%C3%B3n_Orientada_a_Objetos), por lo que no utilizaré tiempo para hablar de ello en este valioso espacio.

La pregunta es, ¿en qué se pueden generar objetos? ¿Será posible generar objetos en, digamos Ensamblador? La respuesta es: Sí. Al ser un paradigma, se pueden manejar objetos en cualquier cosa. Lo que venga a la mente. Ahora bien, ¿dónde será más fácil manejar objetos? ¿En Ensamblador, o en .NET o Java o C++? Decididamente, en alguno de los tres últimos (y varios otros lenguajes que existen orientados a objetos). ¿Por qué? Simplemente porque ya cuentan con toda la infraestructura para no tener que programar el comportamiento del encapsulamiento, de la herencia, del polimorfismo, de la entidad, del estado, del método, del atributo (propiedad), etcétera. Todo eso ya está hecho, y se facilita enormemente el proceso de programación. Así, aunque se puede usar cualquier lenguaje de programación para hacer OOP, lo mejor es usar un lenguaje de programación OOP.

Ya puestos en contexto con lo anterior, la programación en paralelo trae consigo el proceso de distribución de subprocesos (threads) para que sean procesados al mismo tiempo por diferentes entidades. Si hablamos de la paralelización en el mismo supermercado que utilizamos en la primera parte de esta serie, sería (de forma simplista) la entrada al supermercado (donde varios clientes al mismo tiempo acceden a él), o, incluso, el que varios clientes sean atendidos por varios empleados en los distintos departamentos del supermercado al mismo tiempo. La entrada y la atención de los clientes se realiza de manera paralela, pero el cobro es de manera serial.

Tal vez, lo único del famoso supermercado que no se semeja a la forma en que el procesador serial trabaja, es que cada thread o subproceso (cliente) decide por sí mismo a qué caja ir. En el procesamiento serial, eso no sucede. En el procesamiento serial el efecto es más como la unifila, donde hay una persona o un aparato que nos dice a qué caja pasar (si está disponible). Esa persona o aparato sería el núcleo del sistema operativo (conciente de que hay multicajas) que, conforme se entera que un núcleo tiene tiempo disponible para atender a alguna petición, le envía el thread (cliente) para que lo procese lo más ágilmente posible. Pero el thread, mientras, no se mueve del lugar en el que está, hasta que recibe la señal de que pase a la caja disponible (lo cual significa que si el kernel del sistema operativo nunca se entera que un núcleo [caja] está disponible, el thread [cliente] no se moverá del frente de la cola [fila] hasta que el kernel se dé cuenta de que ese núcleo [caja] está disponible).

Lo mejor, es que los subprocesos o threads se distribuyan más libremente y puedan ejecutar su funcionalidad en los núcleos [empleados o cajas] que estén disponibles (pero ello debe ser de forma muy inteligente para no saturar núcleos [empleados o cajas] y dejar otros sin hacer nada). Hay dos formas de hacerlo: que cada subproceso sea acompañado de un "alguien" del supermercado para llevarle por los pasillos y con los empleados que correspondan (lo cual, entorpece el flujo del subproceso), o que el propio subproceso decida lo mejor posible cómo moverse por el supermercado y con quién ir. El primero es provocado por intentar programar en paralelo con un procesador serial. El segundo es la programación paralela en un procesador en paralelo.

Si se intenta programar en paralelo con un procesador serial, el desarrollador tiene TODA LA RESPONSABILIDAD de controlar el comportamiento del thread que está paralelizando, y mientras más threads intente paralelizar, mayor posibilidad de fallas (y mayores esquemas de control) tiene que desarrollar (para evitar deadlocks, que una instrucción salga antes que otra que se esperaba que saliera antes, que controle el orden, etcétera). Programar en paralelo con procesadores seriales es una pesadilla tal que por ello los desarrolladores preferimos programar con un solo thread, y, con ello, reducir el tiempo de desarrollo y, por ende, el precio del programa. Un programa en paralelo sobre tecnología serial es sumamente tardado de programar, sumamente complejo y, por ende, sumamente caro. Si, de por sí, nos negamos a pagar 1,500 pesos por un Office en la actualidad, difícilmente pagaríamos 15,000 pesos (más o menos) si el Office estuviera paralelizado.

Lo contrario pasa en los servidores, donde la paralelización es vital y, por ende, explica el precio que tienen algunas aplicaciones de servidor (como bases de datos, programas de análisis, de HPC, de number crunching, etcétera) que pueden costar decenas, y, a veces, cientos de miles de pesos. La paralelización en los servidores justifica el uso de multinúcleos en ellos, y buscar altas densidades de núcleos con esa misma finalidad (AMD tiene el procesador AMD Opteron dodecacore [12 núcleos], y el próximo año tendrá el hexadecadore [16 núcleos]).

Regresando a las PC, los procesadores en paralelo o GPGPU son la mejor forma de hacer programación en paralelo. La buena noticia para los desarrolladores es que ya existen estándares abiertos (como OpenCL 1.1 y DirectCompute 11 [Windows]) para realizar programación en paralelo de una forma más sencilla. Así, ya no hay que ceñirse a cosas como AMD Accelerated Parallel Processing o el CUDA para realizar esa programación y limitar la paralelización a solo un tipo de procesador en paralelo. La programación en paralelo es el futuro, sí, pero con procesadores en paralelo.

Así, ahora las GPGPU (Unidad de Procesamiento Gráfico de Propósito General, por sus siglas en inglés) forman una parte importantísima de los modernos esquemas de cómputo, pues no solo son un medio para presentar gráficos en una computadora, sino que también coadyuvan decididamente a la aceleración de procesos. Modernos sistemas operativos como Windows 7 y GNU/Linux cuentan en la actualidad con esquemas para acelerar su funcionamiento automáticamente con cómputo serial-paralelo que son los que ya se habían mencionado: DirectCompute (desde Windows Vista) y OpenCL (DirectCompute es una forma de explicar el por qué en una computadora con solo el procesador serial el rendimiento final de Windows Vista no era el mejor, y lo mismo pasa con Windows 7). Y están apareciendo cada vez más programas que aprovechan este tipo de tecnologías o tecnologías similares: Microsoft Office 2010, Internet Explorer 9, Mozilla Firefox (con OpenGL ES 2.0: http://www.khronos.org/opengles/), juegos y varios etcéteras.

Así, el cómputo serial-paralelo ya es la norma en la actualidad. Es importante asegurarse que la computadora cuente con cómputo serial-paralelo de manera que se aproveche por completo la potencia de los nuevos sistemas operativos y aplicaciones (es decir, que se cuente con una CPU (procesadores de, al menos, dos núcleos: AMD Athlon, AMD Phenom, AMD Turion, Core ix) y una GPGPU (AMD/ATI Radeon, AMD/ATI FirePro, GeForce o Quadro), y una buena cantidad de memoria RAM (4GB DDR3 1333, recomendado) para que la experiencia de cómputo sea completa. Ya en otro Blog hablé de ReadyBoost que también podría ser muy útil para agilizar el cómputo actual.

Hay una buena noticia en cuanto a cómputo serial-paralelo: AMD está preparando la presentación del primer procesador serial-paralelo del mundo, que da muerte a la CPU tal como ahora la conocemos. Esperaremos noticias de AMD para dar mayores detalles de este nuevo procesador. ¡Nos seguimos leyendo!

Comentarios

Entradas más populares de este blog

Toshiba Satellite T215-SP1004M

Consecuencias de la falta de mantenimiento en el equipo de cómputo

Normalización de bases de datos (Parte 6 y última): Quinta y sexta formas de normalización (5NF) (6NF)