Luchian giải thích về lý do tại sao hành vi này xảy ra, nhưng tôi nghĩ đó là một ý tưởng hay để hiển thị một giải pháp có thể cho vấn đề này và đồng thời hiển thị một chút về thuật toán không rõ ràng của bộ nhớ cache.
thuật toán của bạn cơ bản nào:
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
A[j][i] = A[i][j];
mà chỉ là khủng khiếp cho một CPU hiện đại. Một giải pháp là biết chi tiết về hệ thống bộ nhớ cache của bạn và tinh chỉnh thuật toán để tránh những vấn đề đó. Hoạt động tuyệt vời miễn là bạn biết những chi tiết đó .. không đặc biệt là di động.
Chúng ta có thể làm tốt hơn thế không? Vâng, chúng tôi có thể: Một cách tiếp cận chung cho vấn đề này là cache oblivious algorithms mà như tên gọi của mình tránh bị lệ thuộc vào kích thước bộ nhớ cache cụ thể [1]
Các giải pháp sẽ trông như thế này:
void recursiveTranspose(int i0, int i1, int j0, int j1) {
int di = i1 - i0, dj = j1 - j0;
const int LEAFSIZE = 32; // well ok caching still affects this one here
if (di >= dj && di > LEAFSIZE) {
int im = (i0 + i1)/2;
recursiveTranspose(i0, im, j0, j1);
recursiveTranspose(im, i1, j0, j1);
} else if (dj > LEAFSIZE) {
int jm = (j0 + j1)/2;
recursiveTranspose(i0, i1, j0, jm);
recursiveTranspose(i0, i1, jm, j1);
} else {
for (int i = i0; i < i1; i++)
for (int j = j0; j < j1; j++)
mat[j][i] = mat[i][j];
}
}
Hơi phức tạp hơn, nhưng một bài kiểm tra ngắn cho thấy một cái gì đó khá thú vị trên E8400 xưa của tôi với VS2010 x64 phát hành, testcode cho MATSIZE 8192
int main() {
LARGE_INTEGER start, end, freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
recursiveTranspose(0, MATSIZE, 0, MATSIZE);
QueryPerformanceCounter(&end);
printf("recursive: %.2fms\n", (end.QuadPart - start.QuadPart)/(double(freq.QuadPart)/1000));
QueryPerformanceCounter(&start);
transpose();
QueryPerformanceCounter(&end);
printf("iterative: %.2fms\n", (end.QuadPart - start.QuadPart)/(double(freq.QuadPart)/1000));
return 0;
}
results:
recursive: 480.58ms
iterative: 3678.46ms
Edit: về ảnh hưởng của kích thước: Nó được nhiều ít phát âm mặc dù vẫn còn đáng chú ý ở mức độ nào, đó là bởi vì chúng tôi đang sử dụng giải pháp lặp như nút lá thay vì đệ quy xuống 1 (tối ưu hóa thông thường cho các thuật toán đệ quy). Nếu chúng tôi đặt LEAFSIZE = 1, bộ nhớ cache không ảnh hưởng đến tôi [8193: 1214.06; 8192: 1171.62ms, 8191: 1351.07ms
- đó là bên trong lề lỗi, các biến động nằm trong khu vực 100ms; "điểm chuẩn" này không phải là điều mà tôi muốn quá thoải mái nếu chúng tôi muốn các giá trị hoàn toàn chính xác])
[1] Nguồn cho nội dung này: Tốt nếu bạn không thể thuyết trình từ một người đã làm việc với Leiserson và đồng ý điều này .. Tôi cho rằng giấy tờ của họ là một điểm khởi đầu tốt. Những thuật toán này vẫn còn khá hiếm khi được mô tả - CLR có một chú thích duy nhất về chúng. Tuy nhiên đó là một cách tuyệt vời để làm mọi người ngạc nhiên.
Sửa (lưu ý: Tôi không phải là một trong những người gửi câu trả lời này, tôi chỉ muốn thêm này):
Dưới đây là một C++ phiên bản hoàn chỉnh của đoạn code trên:
template<class InIt, class OutIt>
void transpose(InIt const input, OutIt const output,
size_t const rows, size_t const columns,
size_t const r1 = 0, size_t const c1 = 0,
size_t r2 = ~(size_t) 0, size_t c2 = ~(size_t) 0,
size_t const leaf = 0x20)
{
if (!~c2) { c2 = columns - c1; }
if (!~r2) { r2 = rows - r1; }
size_t const di = r2 - r1, dj = c2 - c1;
if (di >= dj && di > leaf)
{
transpose(input, output, rows, columns, r1, c1, (r1 + r2)/2, c2);
transpose(input, output, rows, columns, (r1 + r2)/2, c1, r2, c2);
}
else if (dj > leaf)
{
transpose(input, output, rows, columns, r1, c1, r2, (c1 + c2)/2);
transpose(input, output, rows, columns, r1, (c1 + c2)/2, r2, c2);
}
else
{
for (ptrdiff_t i1 = (ptrdiff_t) r1, i2 = (ptrdiff_t) (i1 * columns);
i1 < (ptrdiff_t) r2; ++i1, i2 += (ptrdiff_t) columns)
{
for (ptrdiff_t j1 = (ptrdiff_t) c1, j2 = (ptrdiff_t) (j1 * rows);
j1 < (ptrdiff_t) c2; ++j1, j2 += (ptrdiff_t) rows)
{
output[j2 + i1] = input[i2 + j1];
}
}
}
}
Mã của bạn trông bộ nhớ cache không thân thiện với tôi. – CodesInChaos
@CodeInChaos và nó được. –
Có khá nhiều vấn đề tương tự như câu hỏi này: http://stackoverflow.com/questions/7905760/matrix-multiplication-small-difference-in-matrix-size-large-difference-in-timi – Mysticial