लुचियन एक स्पष्टीकरण देता है कि यह व्यवहार क्यों होता है, लेकिन मुझे लगा कि इस समस्या का एक संभव समाधान दिखाना एक अच्छा विचार होगा और साथ ही कैश विस्मृत एल्गोरिदम के बारे में थोड़ा दिखाना होगा।
आपका एल्गोरिथ्म मूल रूप से करता है:
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
A[j][i] = A[i][j];
जो एक आधुनिक सीपीयू के लिए सिर्फ भयानक है। एक उपाय यह है कि आप अपने कैश सिस्टम के बारे में जानकारी जानें और उन समस्याओं से बचने के लिए एल्गोरिथ्म को ट्विक करें। जब तक आप उन विवरणों को जानते हैं तब तक महान काम करता है .. विशेष रूप से पोर्टेबल नहीं।
क्या हम इससे बेहतर कर सकते हैं? हाँ हम कर सकते हैं: इस समस्या के लिए एक सामान्य दृष्टिकोण कैश अनजान एल्गोरिदम है कि जैसा कि नाम कहता है कि विशिष्ट कैश आकार पर निर्भर होने से बचा जाता है] [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];
}
}
थोड़ा और अधिक जटिल है, लेकिन एक छोटा परीक्षण मेरे प्राचीन e8400 पर काफी दिलचस्प दिखाता है VS2010 x64 रिलीज के लिए, परीक्षण के लिए 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 के बजाय (1 पुनरावर्ती एल्गोरिदम के लिए सामान्य अनुकूलन) के बजाय कर रहे हैं। अगर हम LEAFSIZE = 1 सेट करते हैं, तो कैश का मेरे लिए कोई प्रभाव नहीं है [ 8193: 1214.06; 8192: 1171.62ms, 8191: 1351.07ms
- कि त्रुटि के मार्जिन के अंदर है, उतार चढ़ाव 100ms क्षेत्र में हैं; यह "बेंचमार्क" कुछ ऐसा नहीं है जिसे मैं पूरी तरह से सही मान चाहता हूं, तो मैं बहुत सहज हो जाऊंगा])
[१] इस सामान के लिए स्रोत: यदि आप लीसेरसन के साथ काम कर रहे हैं और इस पर सह से कोई व्याख्यान नहीं पा सकते हैं .. तो मैं उनके कागजों को एक अच्छा प्रारंभिक बिंदु मानता हूं। उन एल्गोरिदम को अभी भी बहुत कम वर्णित किया गया है - सीएलआर के पास उनके बारे में एक ही फुटनोट है। फिर भी यह लोगों को आश्चर्यचकित करने का एक शानदार तरीका है।
संपादित करें (ध्यान दें: मैं वह नहीं हूं जिसने यह उत्तर पोस्ट किया है; मैं बस इसे जोड़ना चाहता था):
यहां उपरोक्त कोड का पूर्ण 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];
}
}
}
}