Prestazioni

CUDA, il nuovo linguaggio di programmazione per le GPU Nvidia, promette grandi risultati e mostra le schede video sotto una nuova luce. Cerchiamo di capire un po' di più cosa ci riserva CUDA.

Avatar di Andrea Ferrario

a cura di Andrea Ferrario

Editor in Chief

Prestazioni

Abbiamo comunque deciso di misurare il tempo necessario per il processo per vedere se esiste un vantaggio nell'usare CUDA anche con un'implementazione cruda, o se è necessaria molta pratica per trarre veramente i benefici dall'uso della GPU. Abbiamo usato un notebook dotato di processore Core 2 Duo T5450 e una GeForce 8600M GT, con Windows Vista. Questo sistema è ben lungi dall'essere un supercomputer, ma i risultati ottenuti sono stati interessanti, anche perché il nostro test non è molto favorevole per la GPU. Nvidia ha certamente tutto l'interesse a mostrare un'enorme accelerazione usando un sistema equipaggiato con una GPU potentissima e un bandwidth mastodontico, ma la realtà dei sistemi in circolazione è ben diversa, come sappiamo.

Questi sono i risultati ottenuti processando un'immagine da 2048x2048:

  • CPU 1 thread: 1419 ms
  • CPU 2 threads: 749 ms
  • CPU 4 threads: 593 ms
  • GPU (8600M GT) blocks of 256 pixels: 109 ms
  • GPU (8600M GT) blocks of 128 pixels: 94 ms
  • GPU (8800 GTX) blocks of 128 pixels / 256 pixels: 31 ms

Abbiamo modificato l'implementazione CPU in maniera tale da renderla thread-ottimizzata. Come abbiamo detto, il codice è ideale per questa situazione: tutto quello che dovrete fare è dividere l'immagine iniziale in tante zone, come se fossero dei thread. Da notare che avremo un'accelerazione lineare passando da uno a due thread sulla nostra CPU dual-core, che mostra la natura altamente parallela del nostro programma di test. Abbastanza inaspettatamente, la versione four-thread si è dimostrata più veloce, dove noi non ci aspettavamo differenze, o meglio, ci aspettavamo una perdita di efficienza a causa del lavoro aggiuntivo necessario per creare i thread addizionali.

Cosa spiegano questi risultati? Difficile da dire, ma pensiamo che il thread scheduler di Windows abbia qualche responsabilità. In ogni caso i risultati rimangono riproducibili. Con una texture di dimensioni inferiori (512x512), il guadagno offerto dalla divisione dei thread è meno marcato (circa il 35%, anziché il 100%), e il comportamento con la versione four-threaded è più logico, poiché non mostra un guadagno prestazionale rispetto alla versione two-thread. La GPU è sempre più veloce, ma in maniera meno marcata.

La seconda osservazione è che anche una GPU "lenta" è circa sei volte più veloce di una CPU di buon livello. Considerando che si tratta di un primo programma, il risultato è molto incoraggiante. I risultati, inoltre, possono ancora migliorare, ricorrendo a blocchi più piccoli. La spiegazione è semplice - il nostro programma usa 14 registri per thread, con blocchi da 256 thread, questo significa 3584 registri per blocco; nel nostro caso abbiamo tre blocchi o 10572 registri. Ma un multiprocessore ha solo 8192 registri, quindi può tenere solo due blocchi attivi. Diversamente, con blocchi da 128 pixel, abbiamo bisogno di 1792 registri per blocco; 8192 diviso 1792, arrotondando all'intero più vicino, si ottengono quattro blocchi. In pratica, il numero di thread è lo stesso (512 per multiprocessore, dove teoricamente il massimo è 768), ma avendo più blocchi, la GPU può essere più flessibile con gli accessi in memoria, e, quando si esegue un'operazione a lunga latenza, può lanciare le istruzioni su un altro blocco, mentre aspetta che arrivi il risultato dell'istruzione precedente. Quattro blocchi riescono a mascherare la latenza, specialmente se il programma effettua diversi accessi alla memoria.