viernes, 9 de abril de 2010

CUDA [I] Introducción sobre tarjetas gráficas

[Sobre que me voy a enrollar hoy]
Hace tiempo ya, me picó el gusanillo de la programación de GPU's [1]. Es por todos conocido que las tarjetas gráficas tienen una alta capacidad de procesamiento y que se necesitan para jugar a los juegos de nueva generación. Pero, por que son tan potentes?

[Explicación]
Resulta que este tipo de hardware ha sido pensado para trabajar con mucha paralelización, es decir, muchos cálculos hechos al mismo tiempo.
Para ello, las tarjetas gráficas tienen un reloj relativamente bajo como 500MHz, o 1GHz. Y vosotros me direis: "Si ya! Pero mi PC tiene un procesador de 2.5GHz, esto es más rapido!" Y es cierto, la única diferencia es que lo más seguro es que tu procesador solo tenga un core. Y en el mejor de los casos va a tener dos o cuatro cores.

La diferencia fundamental es que las tarjetas gráficas tienen 64, 128, 256 o incluso 480 cores! Esto significa que cada uno de los 64 cores, por ejemplo, tiene una velocidad de 500MHz. Lo que una simple multiplicación (0.5GHz por 64 cores ) nos da una asombrosa velocidad de reloj de 32GHz. Eso, señores, es bastante mas que 2.5GHz. Se pueden imaginar las últimas tarjetas gráficas como la GTX 480: 480 cores a 1,401GHz = 672 GHz!!

Bien, pero no nos dejemos engañar por las cifras. 672GHz es la capacidad que puede desarrollar una tarjeta haciendo CÁLCULOS PARALELIZABLES! Osea, esto incluye toda una metodología de programación diferente a la "programación tradicional", y no todos los cálculos són paralelizables, claro! Por este motivo tenemos un procesador y una tarjeta gráfica. El procesador se encarga de los programas "unicore" ( no es una afirmación del todo cierta, ahora os lo cuento) y tiene una velociad de reloj relativamente alta, por otra parte la tarjeta gráfica se encarga de ejecutar aplicaciones que pueden ser altamente paralelizables como, [surprise surpriseee], la renderización de imágenes que es lo que se usa en los juegos. Para ello usa velocidades de reloj bajas, pero muchos cores.

Aprovechando que hablo del tema: los procesadores convencionales, a su vez, también disponen de varios cores, osea que realmente también podemos echar las mismas cuentas: Si el procesador es de 2.5GHz y tenemos 4 cores, el procesador tendrá una potencia de 10GHz para procesos paralelizables. Muy importante destacar esto último. La mayoría de programas no utilizan los diferentes cores de un procesador. Por lo que los programas pensados del modo "tradicional" trabajarían a una velocidad de reloj de 2.5GHz, mientras que los que han sido pensados para ser paralelizados trabajarían a una velocidad de 10GHz como máximo ( insisto en esto ).

[Ejemplo serializable]
Vamos a poner un ejemplo: Imaginemos que tenemos 128 números ( vamos a hacer números "redondos" 2^7 ) y queremos comprobar si son números primos o no. Nosotros programamos en un lenguaje convencional como C, por ejemplo, una función de es_primo(numero). Esta función recibe un número determinado, y devuelve verdadero o falso dependiendo si es primo o no. Bien. Pues si tenemos 128 números a comprobar lo que hará el programa será algo así:
Procesador1:
  • es_primo(1)
  • es_primo(2)
  • es_primo(3)
  • es_primo(...)
  • es_primo(127)
  • es_primo(128)
Bien! Entonces, cada llamada a la función es_primo() tardará 1 unidad de tiempo ( es un ejemplo ). Con lo que si llamamos 128 veces a la función tardaremos 128 unidades de tiempo. ok?

Imaginamos que hemos hecho el programa de modo que nosotros utilizamos una tarjeta gráfica que *casualmente* tiene 128 cores. Lo que podemos hacer es asignar a cada uno de los cores un trabajo concreto. Solo le asignamos el trabajo, todavía no lo ejecutamos! Vean:
  • Core1: es_primo(1)
  • Core2: es_primo(2)
  • Core3: es_primo(3)
  • CoreN: es_primo(N)
  • Core128: es_primo(128)
Y ahora, una vez le hemos asignado el trabajo a cada uno de los cores, les decimos a cada uno de ellos: ejecutad. Y todos, a la vez, van a hacer el cálculo. A la vez. Lo que significa que vamos a estar 1 unidad de tiempo para hacer todos los cálculos. Asombroso, verdad? Pero claro, ahora estaréis pensando. Justamente has hecho un ejemplo donde el número de cores són iguales a los números que queremos comprobar!! Vaaaale. Cierto, veamos un último ejemplo. Imaginemos que tenemos una gráfica con solo 64 cores. Seguimos queriendo calcular 128 números. Entonces asignamos dos números a cada core, de modo que queda algo así:
  • Core1: es_primo(1) es_primo(2)
  • Core2: es_primo(3) es_primo(4)
  • Core3: es_primo(5) es_primo(5)
  • CoreN: es_primo(n) es_primo(n+1)
  • Core128: es_primo(127) es_primo(128)
De este modo, cuando el Core1 acabe de ejecutar "es_primo(1)", empezará a ejecutar "es_primo(2)". Cuando demos la orden a todos los cores de que empiezen su trabajo, tardarán 2 unidades de tiempo en realizar el trabajo, ya que cada uno tiene que hacer dos cálculos, pero todos harán sus dos cálculos a la vez. Interesante, verdad?

**Nota: en el ejemplo se considera que el coste de comprobar es_primo(1) es igual a es_primo(128), aunque en realidad no tiene el mismo coste computacional. Es solo para que el ejemplo se entienda.

[Ejemplo no serializable]
Calcular los decimales del número PI. PI no puede calcularse en "partes". No le puedo decir al Core1, cálculame los decimales de PI de 1 a 100 y al Core2 que me calcule los decimales de 101 hasta 200 ya que para calcular los decimales de 101 al 200 se necesita haber encontrado los decimales de 1 a 100. Este cálculo lo hará mejor el procesador, ya que trabaja a una velocidad de reloj más alta.

[Tecnología]
Pues al tener una gráfica de nvidia ( 9600GT ), me ha apetecido probar de programarla. Para ello hay un lenguaje de programación, CUDA. Este lenguaje, que es muy parecido a C, nos permite programar y compilar los programas para que se ejecuten el la/las gráficas que tengamos instaladas. Para ello usaré ubuntu 9.04 ( ya explicaré por qué ) los drivers 195 de nVidia y el SDK que dan en su pagina web.

[Resumiendo...]
CUDA mola. Aprendamos a programar. En la próxima entrega os explico como instalar todo el SDK para programar en CUDA (ardua tarea). Espero no haberles aburrido mucho!

7 comentarios:

  1. Hola solo para aclarar algo.
    tengo entendido que un funcionamiento en forma serial, es UNO DESPUES DE OTRO. mientras que uno en forma paralela son varios al mismo tiempo.

    Cuando hablas especificamente de la arquitectura de un computador (digase computador como aquello que computa) podemos hablar de ejecucion de procesos en paralelo o de cauce segmentado. (Tanenbaum ó Stallings)

    Todo ello por la manera en que comienza tu escrito. (saludos)

    "ha sido pensado para trabajar con mucha serialización, es decir, muchos cálculos hechos al mismo tiempo."

    ResponderEliminar
  2. Muchas gracias por la correción. Si, tienes toda la razón. Seguramente quise decir "ha sido pensado para trabajar con muchos procesos serializados a la vez" ( osea, de manera paralela, claro... ).

    Tendré que revisar la entrada, ya que ahora veo un par de errores mas. Muchas gracias.

    Saludos!

    ResponderEliminar
  3. Para eso estamos!
    Saludos

    ResponderEliminar
  4. HOLA BUENAS NOCHES ALGUIEN QUE ME PUEDA AYUDAR ESTOY PROGRAMANDO EN CUDA PERO ME GENERA UN ERROR AL MOMENTO DE LEER LA IMAGEN EL ERROR ES

    cutLoadPPM():Failed to open file:lena.ppm
    cut error in file 'convolucion.cu'

    ResponderEliminar
  5. Mucho me temo, Anónimo, estuve haciendo pruebas con cuda. Pero tan solo me quedé en la puerta. Siento no poder ayudarte esta vez.

    Un saludo, Jan.

    P.D: No hace falta que grites ;)

    ResponderEliminar
  6. Hola, por favor dame tu opinion conrelacion a estas dos tarjetas no se por cual decidirme:
    ENGTX560 DCII TOP/2DI/1GD5
    ASUS GTX 560 DirectCU II TOP overclocked graphics card for superb 3D Vision™ gaming
    o
    ZOTAC nVidia GeForce GTX560 2GB DDR5 2DVI/HDMI/Displayport PCI-Express Video Card [ZT-50709-10M]

    ResponderEliminar
  7. Anónimo, es una pena pero no puedo ayudarte a decidir entre estas tarjetas gràficas.
    Me temo que hace mucho que estoy desactualizado a lo que tarjetas gráficas se refiere (si es que alguna vez lo estuve) y este post simplemente fué una prueba que hice hace dos años para jugar un poco con la programación en CUDA. Nada mas.

    Lo siento, tendrás que pedir opinión en algún foro especializado ;)

    Un saludo y suerte!

    ResponderEliminar