lunes, 23 de noviembre de 2015

Harder, better, faster, SIMD

Los sistemas computacionales son sin duda alguna indispensables para múltiples actividades cotidianas. Podemos realizar actividades de oficina, buscar puntos específicos en la ciudad, ver fotos, editar videos, reproducir música, y muchas otras cosas más. Sin embargo, muchas de las aplicaciones más recientes no existirían de no haber sido por el desarrollo de los lenguajes de programación, y de las computadoras mismas.

Todos los lenguajes de programación tienen su origen en un lenguaje final: el binario. El binario es el verdadero dialecto computacional. Pero este no es un lenguaje que sea fácil de programar, y mucho menos de leer. Dado esto, se implementó una tecnología que permitiera a un usuario con  conocimientos suficientes acerca del hardware, para poderlo programar: el ensamblador.

Una de las variantes del ensamblador, es la implementación tipo SIMD (Simple Instruction, Multiple Data). Este tipo de implementaciones en ensamblador permiten agilizar el procesamiento de instrucciones que se repiten (como en un ciclo for en C), de tal forma que se aprovechan los componentes del procesador (registros y memoria caché, principalmente). Sin embargo, el uso de este tipo de implementaciones tiene ciertas restricciones:

·       1.  No es fácil escribir un programa en ensamblador si no se tiene una idea de qué se va a hacer.
·        2.  Al nivel de programación que tenemos hoy en día (por la complejidad de los lenguajes para su fácil uso), si no sirve en un lenguaje de nivel superior a ensamblador, no tiene caso hacerlo en ensamblador.
·         3. Dependiendo de la arquitectura del procesador, se deben considerar el número de bits y bytes a procesar en los registros.
·         4. El total de bytes a procesar debe ser múltiplo del total de registros a utilizar, siendo también congruente con el número de bits de éstos.


Este tipo de implementaciones resulta interesante, ya que una aplicación puramente en C, puede alcanzar speed ups de más de 20 unidades, cuando la implementación del código paralelizable es una extensión SIMD bien realizada.

lunes, 2 de noviembre de 2015

Nadie dijo que fuera fácil, pero alguien tiene que hacerlo

El cómputo paralelo, como hemos estado discutiendo a lo largo del semestre, es una de las tendencias con mayor importancia en esta década. Existen múltiples plataformas de hardware que soportan el cómputo paralelo. Estas plataformas están en uso desde el año 2004. Sin embargo, el software generado en años anteriores (incluso el actual) no está pensado para trabajar de forma paralela.

El problema radica principalmente en que el paralelismo, a pesar de que es posible enseñarlo en las escuelas con piezas de hardware económicas (laptops, tarjetas multinúcleo, GPU’s, entre otros), no se le ha dado la importancia que requiere para su desarrollo.

Una de los grandes campos de paralelismo a nivel mundial (y comercial) es el mercado de los videojuegos. Cada día, vemos cómo empresas como Microsoft, Sony, entre otros, compiten arduamente por ofrecer a los fans una plataforma que les brinde mejor desempeño y videojuegos cada vez más específicos para satisfacer las expectativas de los mismos jugadores. Este mercado no sería tan fuerte, de no ser por la gran capacidad de cómputo paralelo que se realiza a nivel de hardware y a nivel de software en las consolas.

Los problemas crecen conforme las ambiciones son mayores. Dado a que gran cantidad de segmentos de código han sido elaborados de forma secuencial, pensando en que el desarrollo de procesadores más rápidos radicaba en la frecuencia de reloj,  la forma más “rápida” de paralelizar el código es ver qué segmentos son candidatos a paralelizado. Posteriormente, se deben hacer pruebas para verificar que no haya bugs. Esta etapa, sin duda alguna, es la más difícil y tediosa de todo el proceso.

A veces, nos preguntamos a nosotros mismos por qué no reescribir el código, tomando en cuenta la capacidad de paralelismo que se tiene. Sin embargo, abrimos un archivo que tiene millones de líneas de código (como el de un kernel, o un sistema operativo completo), y vemos que es muy mala idea. Especialmente, en los códigos que corren en servidores bancarios, o gubernamentales.
Para estos casos, lo mejor que se ha logrado es el desarrollo de módulos que, con simples instrucciones, se paralelizan ciertas partes del código. Un ejemplo de estos módulos es OpenMP, que con directivas a compilador (pragmas), se pueden paralelizar segmentos de código, ciclos for, entre otras cosas.


El paralelismo es un gran reto, tanto a nivel de desempeño del hardware, como el desarrollo del software especializado. Y, dada la importancia que esto tiene, es imperioso que se acepte el reto, y comencemos a ser más eficientes a la hora de programar, tomando en cuenta el paralelismo como herramienta fundamental.