Luchian 은이 동작이 발생 하는 이유에 대한 설명을 제공 하지만이 문제에 대한 하나의 가능한 솔루션을 표시하는 동시에 캐시 무의식 알고리즘에 대한 정보를 제공하는 것이 좋습니다.
알고리즘은 기본적으로 다음을 수행합니다.
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
A[j][i] = A[i][j];
이것은 현대 CPU에 끔찍한 일입니다. 한 가지 해결책은 캐시 시스템에 대한 세부 사항을 알고 이러한 문제점을 피하기 위해 알고리즘을 조정하는 것입니다. 당신이 그 세부 사항을 알고있는 한 잘 작동합니다.
그보다 더 잘할 수 있습니까? 예, 우리는 할 수 있습니다 :이 문제에 대한 일반적인 접근 방식 은 이름에서 알 수 있듯이 특정 캐시 크기에 의존 하지 않는 캐시 인식 알고리즘 입니다 [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];
}
}
약간 더 복잡하지만 짧은 테스트를 통해 VS2010 x64 릴리스가있는 고대 e8400에서 매우 흥미로운 것을 보여줍니다. 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
편집 : 크기의 영향에 관하여 : 어느 정도 눈에 띄게 보이지만 반복 발음을 1로 재귀 대신 리프 노드로 사용하기 때문에 (재귀 알고리즘의 일반적인 최적화) 훨씬 덜 두드러집니다. LEAFSIZE = 1로 설정하면, 캐시는 나에게 영향을 미치지 않습니다 8193: 1214.06; 8192: 1171.62ms, 8191: 1351.07ms
. 이 "벤치 마크"는 우리가 완전히 정확한 값을 원한다면 너무 편한 것이 아닙니다.])
[1] 이것에 대한 출처 : 글쎄, 만약 당신이 Leiserson과 공동으로 일한 누군가로부터 강의를받을 수 없다면 .. 나는 그들의 논문이 좋은 출발점이라고 가정합니다. 이러한 알고리즘은 여전히 거의 설명되지 않습니다. 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];
}
}
}
}