2010-01-02 20 views
16

Ho scritto diverse funzioni di copia alla ricerca di una buona strategia di memoria su PowerPC. L'utilizzo dei registri Altivec o fp con hint cache (dcb *) raddoppia le prestazioni su un semplice ciclo di copia byte per i dati di grandi dimensioni. Inizialmente soddisfatto, ho inserito una normale memepia per vedere come si confronta ... 10 volte più veloce del mio meglio! Non ho intenzione di riscrivere memcpy, ma spero di imparare da esso e accelerare diversi filtri di immagini semplici che passano la maggior parte del loro tempo spostando i pixel da e verso la memoria.Cosa rende memcpy di Apple PowerPC così veloce?

L'analisi degli squali rivela che il loro ciclo interno utilizza dcbt per il prefetch, con 4 letture vettoriali, quindi 4 scritture vettoriali. Dopo aver modificato la mia funzione migliore per trasportare 64 byte per iterazione, il vantaggio prestazionale di memcpy è ancora imbarazzante. Sto usando dcbz per liberare larghezza di banda, Apple non usa nulla, ma entrambi i codici tendono ad esitare nei negozi.

 
prefetch 
    dcbt future 
    dcbt distant future 
load stuff 
    lvx image 
    lvx image + 16 
    lvx image + 32 
    lvx image + 48 
    image += 64 
prepare to store 
    dcbz filtered 
    dcbz filtered + 32 
store stuff 
    stvxl filtered 
    stvxl filtered + 16 
    stvxl filtered + 32 
    stvxl filtered + 48 
    filtered += 64 
repeat 

Qualcuno ha qualche idea sul perché codice molto simile ha un divario di prestazioni così drammatico? Mi piacerebbe marinare i filtri delle immagini reali in qualunque cosa stia usando la memcpia salsa segreta!

Ulteriori informazioni: Tutti i dati sono allineati con vettori. Sto facendo copie filtrate dell'immagine, non sostituendo l'originale. Il codice funziona su PowerPC G4, G5 e Cell PPU. La versione di SPU Cell è già incredibilmente veloce.

risposta

0

Forse è a causa della cache della CPU. Prova a eseguire CacheGrind:

Cachegrind è un profilo di cache. Lo strumento esegue una simulazione dettagliata delle cache I1, D1 e L2 nella CPU e così può individuare con precisione le fonti di errori di cache nel codice. Lo standard identifica il numero di mancate cache, i riferimenti di memoria e le istruzioni eseguite per ogni riga del codice sorgente, con riepilogo per programmi, per modulo e per interi programmi. È utile con programmi scritti in qualsiasi lingua. Cachegrind esegue programmi su 20--100x più lenti del normale.

+0

CacheGrind non funziona assolutamente su PPC/Darwin. –

+0

@Nick, sei sicuro? http://en.wikipedia.org/wiki/Valgrind "A partire dalla versione 3.4.0, Valgrind supporta Linux su x86, x86-64 e PowerPC" –

+1

@Andreas: funziona su * linux *, ma sicuramente non su Darwin. L'unico supportato (ea malapena) Darwin è x86. –

2

Non so esattamente cosa stai facendo, dal momento che non riesco a vedere il tuo codice, ma la salsa segreta di Apple è here.

+0

Potrei vedere lo smontaggio in Shark, quindi so cosa stanno facendo nel ciclo di copia. Mi chiedo solo cosa c'è prima di quel ciclo che sembra dargli un effetto overdrive. Quel codice dovrebbe aiutare, quindi grazie per il link! –

+0

@Invisible Cow: Sì, speravo solo che potesse fornire un po 'più di contesto (e commenti) che potesse essere perspicace. –

+0

Aggiunto un po 'di codice alla domanda, per G4 e le sue linee di caching a 32 byte. –

7

L'analisi degli squali rivela che il loro ciclo interno utilizza dcbt per il prefetch, con 4 letture vettoriali, quindi 4 scritture vettoriali. Dopo tweaking del mio meglio la funzione di trasportare anche 64 byte per l'iterazione

I può essere affermare l'ovvio, ma dal momento che non menziona il seguente affatto nella sua interrogazione, può valere la pena segnalarlo:

Scommetto che la scelta di Apple di 4 letture di vettori seguita da 4 scritture vettoriali ha a che fare tanto con lo G5's pipeline and its management of out-of-order instruction execution in "dispatch groups" che con una magica dimensione di linea di 64 byte. Hai notato che la linea salta nel link bcopy.s di Nick Bastin? Ciò significa che lo sviluppatore ha pensato a come il flusso di istruzioni sarebbe stato consumato dal G5. Se si desidera riprodurre la stessa performance, non è sufficiente leggere i dati 64 byte alla volta, è necessario assicurarsi che i gruppi di istruzioni siano ben riempiti (in pratica, ricordo che le istruzioni possono essere raggruppate fino a cinque indipendenti, con le prime quattro sono istruzioni non di salto e la quinta può essere solo un salto, i dettagli sono più complicati).

EDIT: si può anche essere interessati dal paragrafo seguente sulla stessa pagina:

L'istruzione dcbz ancora zeri allineati 32 segmenti byte di memoria come per il G4 e G3. Tuttavia, dal momento che non è una cacheline completa su un G5, non avrà i benefici sulle prestazioni che probabilmente speravi. C'è un'istruzione dcbzl recentemente introdotta per il G5 che zera una cacheline da 128 byte completa.

+0

Non avevo pensato a gruppi di spedizione. L'intero "zuppa di istruzioni" del G5 mi ha sempre perplesso e preferisco di gran lunga lavorare con la cella, semplicemente perché il suo modello di esecuzione si adatta alla mia testa. Per quanto riguarda la modifica, il codice differisce già per le linee di memoria più grandi. –

0

Ancora non una risposta, ma hai verificato che memcpy stia effettivamente spostando i dati? Forse è stato appena rimappato copy-on-write. Si vedrà comunque il loop memcpy interno in Shark come parte della prima e dell'ultima pagina sono stati copiati.

0

Come menzionato in un'altra risposta, "dcbz", come definito da Apple sul G5, funziona solo su 32 byte, quindi si perderanno le prestazioni con questa istruzione su un G5 con 128 linee di caching a byte. È necessario utilizzare "dcbzl" per impedire che la cache di destinazione venga prelevata dalla memoria (riducendo in modo efficace la larghezza di banda della memoria di lettura utile della metà).

+1

E non dimenticare - dovresti usare solo 1 "dcbzl" per 128 byte di linea. Sembra che il tuo codice stia facendo un "dcbz" ogni 32 byte. – JanePhanie