Luchian給出解釋爲什麼這種行爲發生,但我認爲這會是一個不錯的主意,以顯示一種可能的解決了這個問題,並在同一時間出示了一下有關緩存忘卻的算法。
你的算法基本上沒有:
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
A[j][i] = A[i][j];
這太可怕了現代CPU。一個解決方案是知道你的緩存系統的細節,並調整算法以避免這些問題。只要你知道那些細節,工作很棒..不是特別便攜。
我們可以做得更好嗎?是的,我們可以:這個問題的一般方法是cache oblivious algorithms,作爲它的名字說避免依賴於特定的緩存大小[1]
該解決方案是這樣的:
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];
}
}
稍微複雜一些,但一個簡短的測試顯示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
的東西在我的古E8400與VS2010發佈的x64挺有意思的,testcode編輯:關於大小的影響:它是那麼明顯,雖然仍明顯在一定程度上,這是因爲我們將迭代解決方案用作葉節點,而不是遞歸到1(通常遞歸算法的優化)。如果我們設置LEAFSIZE = 1,緩存對我沒有影響[8193: 1214.06; 8192: 1171.62ms, 8191: 1351.07ms
- 這是在誤差範圍內,波動在100ms區域;如果我們想要完全準確的數值,這個「基準」並不是我會感到太舒服的原因])
[1]這個東西的來源:好吧,如果你不能從一個合作過的人Leiserson和co ..我認爲他們的論文是一個很好的起點。這些算法仍然很少被描述 - CLR有一個關於它們的腳註。儘管如此,這仍然是給人們驚喜的好方法。
編輯(注:我不是誰張貼了這個答案的一個;我只是想補充這一點):
這裏是上面代碼的完整C++版本:
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];
}
}
}
}
您的代碼看起來對我不友好。 – CodesInChaos 2012-07-10 13:02:26
@CodeInChaos,它是。 – 2012-07-10 13:02:56
這是幾乎相同的問題,這個問題:http://stackoverflow.com/questions/7905760/matrix-multiplication-small-difference-in-matrix-size-large-difference-in-timi – Mysticial 2012-07-10 13:30:51