Aquí es una verdadera pieza de código que he tirado fuera de un proyecto más amplio. (Lo siento, es el más corto que puedo encontrar que tuvo una aceleración notable de la captación previa). Este código realiza una transposición de datos muy grande.
Este ejemplo usa las instrucciones de captación previa de SSE, que pueden ser las mismas que las que emite GCC.
Para ejecutar este ejemplo, deberá compilar esto para x64 y tener más de 4 GB de memoria. Puede ejecutarlo con un tamaño de datos más pequeño, pero será demasiado rápido para el tiempo.
#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");
}
Cuando corro con activar ENABLE_PREFETCH, esta es la salida:
bytes = 4294967296
Starting Data Transpose... Done
Time: 0.725 seconds
Press any key to continue . . .
cuando corro con ENABLE_PREFETCH discapacitados, esta es la salida:
bytes = 4294967296
Starting Data Transpose... Done
Time: 0.822 seconds
Press any key to continue . . .
Así que hay una 13% de aceleración de la captación previa.
EDIT:
Aquí hay más resultados: algunos
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
Interesante. Desafortunadamente en las dos máquinas que probé (MacBook Pro con "Core 2 Duo" y una máquina Linux con un "Procesador AMD Opteron de Quad-Core 2376") no obtuve una diferencia significativa entre las dos versiones. Sospecho que tiene que ver con el tamaño de la caché: parece que tienes una máquina mejor que esas dos. ¿Qué piensas? –
Mi máquina es Core i7 920 @ 3.5 GHz. 8 MB de caché L3. Esta aceleración del 10% es más o menos consistente en otras 3 máquinas que he probado: Core i7 2600K a 4.6 GHz y 2 x Xeon X5482 a 3.2 GHz. Pero admito que nunca lo he probado en una computadora portátil o en una máquina AMD. – Mysticial
Acabo de editar mi respuesta con los puntos de referencia en las 4 máquinas que probé. Son todos escritorios/estaciones de trabajo Intel. Entonces esa podría ser la razón. No probé si su 3er punto se mantiene. Podría ser que sustituirlo por un acceso a la memoria podría producir el mismo resultado. – Mysticial