Dưới đây là một mảnh thực tế của mã mà tôi đã rút khỏi một dự án lớn hơn. (Xin lỗi, đó là đoạn ngắn nhất tôi có thể thấy có tốc độ đáng chú ý khi tìm nạp trước.) Mã này thực hiện chuyển dữ liệu rất lớn.
Ví dụ này sử dụng hướng dẫn tìm nạp trước SSE, có thể giống như hướng dẫn mà GCC phát ra.
Để chạy ví dụ này, bạn sẽ cần phải biên dịch mã này cho x64 và có nhiều hơn 4GB bộ nhớ. Bạn có thể chạy nó với một dữ liệu nhỏ hơn, nhưng nó sẽ là quá nhanh để thời gian.
#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");
}
Khi tôi chạy nó với ENABLE_PREFETCH bật, đây là kết quả:
bytes = 4294967296
Starting Data Transpose... Done
Time: 0.725 seconds
Press any key to continue . . .
Khi tôi chạy nó với ENABLE_PREFETCH tàn tật, đây là kết quả:
bytes = 4294967296
Starting Data Transpose... Done
Time: 0.822 seconds
Press any key to continue . . .
Vì vậy, có một Tăng tốc 13% từ tìm nạp trước.
EDIT:
Dưới đây là một số kết quả hơn:
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
Thú vị. Thật không may trên hai máy tôi đã thử nghiệm (Macbook Pro với "Core 2 Duo" và một máy Linux với một "Quad-Core AMD Opteron Processor 2376") Tôi đã không nhận được một sự khác biệt đáng kể giữa hai phiên bản. Tôi nghi ngờ nó đã làm với kích thước bộ nhớ cache - có vẻ bạn có một máy tốt hơn so với hai. Bạn nghĩ sao? –
Máy của tôi là Core i7 920 @ 3.5 GHz. Bộ nhớ cache L3 8MB. Tốc độ tăng tốc 10% này ít nhiều phù hợp trên 3 máy khác mà tôi đã thử nghiệm: Core i7 2600K @ 4,6 GHz và 2 x Xeon X5482 @ 3,2 GHz. Nhưng tôi sẽ thừa nhận rằng tôi chưa bao giờ thử nghiệm nó trên máy tính xách tay hay máy AMD. – Mysticial
Tôi chỉ chỉnh sửa câu trả lời của mình với điểm chuẩn trên tất cả 4 máy mà tôi đã thử nghiệm. Chúng là tất cả các máy tính để bàn/máy trạm của Intel. Vì vậy, đó có thể là lý do. Tôi đã không kiểm tra xem điểm thứ 3 của bạn có giữ được hay không. Nó có thể được thay thế bằng truy cập bộ nhớ có thể tạo ra kết quả tương tự. – Mysticial