Ecco un vero pezzo di codice che ho tirato fuori di un progetto più ampio. (Siamo spiacenti, è il più breve che riesco a trovare con un notevole aumento della velocità di precaricamento.) Questo codice esegue una trasposizione dei dati molto ampia.
Questo esempio utilizza le istruzioni di precaricamento SSE, che possono essere uguali a quelle emesse da GCC.
Per eseguire questo esempio, è necessario compilarlo per x64 e avere più di 4 GB di memoria. È possibile eseguirlo con un datasize più piccolo, ma sarà troppo veloce per volta.
#include <iostream>
using std::cout;
using std::endl;
#include <emmintrin.h>
#include <malloc.h>
#include <time.h>
#include <string.h>
#define ENABLE_PREFETCH
#define f_vector __m128d
#define i_ptr size_t
inline void swap_block(f_vector *A,f_vector *B,i_ptr L){
// To be super-optimized later.
f_vector *stop = A + L;
do{
f_vector tmpA = *A;
f_vector tmpB = *B;
*A++ = tmpB;
*B++ = tmpA;
}while (A < stop);
}
void transpose_even(f_vector *T,i_ptr block,i_ptr x){
// Transposes T.
// T contains x columns and x rows.
// Each unit is of size (block * sizeof(f_vector)) bytes.
//Conditions:
// - 0 < block
// - 1 < x
i_ptr row_size = block * x;
i_ptr iter_size = row_size + block;
// End of entire matrix.
f_vector *stop_T = T + row_size * x;
f_vector *end = stop_T - row_size;
// Iterate each row.
f_vector *y_iter = T;
do{
// Iterate each column.
f_vector *ptr_x = y_iter + block;
f_vector *ptr_y = y_iter + row_size;
do{
#ifdef ENABLE_PREFETCH
_mm_prefetch((char*)(ptr_y + row_size),_MM_HINT_T0);
#endif
swap_block(ptr_x,ptr_y,block);
ptr_x += block;
ptr_y += row_size;
}while (ptr_y < stop_T);
y_iter += iter_size;
}while (y_iter < end);
}
int main(){
i_ptr dimension = 4096;
i_ptr block = 16;
i_ptr words = block * dimension * dimension;
i_ptr bytes = words * sizeof(f_vector);
cout << "bytes = " << bytes << endl;
// system("pause");
f_vector *T = (f_vector*)_mm_malloc(bytes,16);
if (T == NULL){
cout << "Memory Allocation Failure" << endl;
system("pause");
exit(1);
}
memset(T,0,bytes);
// Perform in-place data transpose
cout << "Starting Data Transpose... ";
clock_t start = clock();
transpose_even(T,block,dimension);
clock_t end = clock();
cout << "Done" << endl;
cout << "Time: " << (double)(end - start)/CLOCKS_PER_SEC << " seconds" << endl;
_mm_free(T);
system("pause");
}
Quando eseguo con ENABLE_PREFETCH abilitato, questo è l'output:
bytes = 4294967296
Starting Data Transpose... Done
Time: 0.725 seconds
Press any key to continue . . .
quando l'eseguo con ENABLE_PREFETCH disattivato, questo è l'output:
bytes = 4294967296
Starting Data Transpose... Done
Time: 0.822 seconds
Press any key to continue . . .
Quindi c'è una 13% di accelerazione dal prefetching.
EDIT:
Ecco altri risultati:
Operating System: Windows 7 Professional/Ultimate
Compiler: Visual Studio 2010 SP1
Compile Mode: x64 Release
Intel Core i7 860 @ 2.8 GHz, 8 GB DDR3 @ 1333 MHz
Prefetch : 0.868
No Prefetch: 0.960
Intel Core i7 920 @ 3.5 GHz, 12 GB DDR3 @ 1333 MHz
Prefetch : 0.725
No Prefetch: 0.822
Intel Core i7 2600K @ 4.6 GHz, 16 GB DDR3 @ 1333 MHz
Prefetch : 0.718
No Prefetch: 0.796
2 x Intel Xeon X5482 @ 3.2 GHz, 64 GB DDR2 @ 800 MHz
Prefetch : 2.273
No Prefetch: 2.666
Interessante. Sfortunatamente sulle due macchine che ho provato (Macbook Pro con "Core 2 Duo" e una macchina Linux con un processore quad-core AMD Opteron 2376) non ho avuto una differenza significativa tra le due versioni. Sospetto che abbia a che fare con la dimensione della cache: sembra che tu abbia una macchina migliore di quelle due. Cosa ne pensi? –
La mia macchina è un Core i7 920 @ 3,5 GHz. 8 MB di cache L3. Questo 10% di accelerazione è più o meno coerente su altre 3 macchine che ho testato: Core i7 2600K a 4,6 GHz e 2 x Xeon X5482 a 3,2 GHz. Ma ammetto che non l'ho mai testato su un laptop o su una macchina AMD. – Mysticial
Ho appena modificato la mia risposta con i benchmark su tutte e 4 le macchine che ho testato. Sono tutti desktop/workstation Intel. Quindi questa potrebbe essere la ragione. Non ho testato se il tuo terzo punto è valido. Potrebbe essere che sostituirlo con un accesso alla memoria potrebbe produrre lo stesso risultato. – Mysticial