লুচিয়ান এই আচরণটি কেন ঘটে তার একটি ব্যাখ্যা দেয় তবে আমি ভেবেছিলাম যে এই সমস্যার একটি সম্ভাব্য সমাধান দেখানো এবং একই সাথে ক্যাশে বিস্মৃত অ্যালগরিদমগুলি সম্পর্কে কিছুটা দেখানো ভাল ধারণা হবে।
আপনার অ্যালগরিদম মূলত:
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 তে ভিএস 2010 x 64 রিলিজ, টেস্টকোড সহ আকর্ষণীয় কিছু দেখায় 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
- এটি ত্রুটির প্রান্তরে রয়েছে, ওঠানামাটি 100 মিমি অঞ্চলে হয়; এই "বেঞ্চমার্ক" এমন কিছু নয় যা আমরা সম্পূর্ণ নির্ভুল মান চাইলে আমি খুব স্বাচ্ছন্দ্য বোধ করি])
[1] এই স্টাফের উত্স: আচ্ছা আপনি যদি লেসারসন এবং এই সহকারীর সাথে কাজ করে এমন কারও কাছ থেকে বক্তৃতা না পান তবে .. আমি তাদের কাগজপত্রকে একটি ভাল সূচনা পয়েন্ট বলে ধরে নিই। এই অ্যালগরিদমগুলি এখনও খুব কমই বর্ণিত হয়েছে - সিএলআর তাদের সম্পর্কে একটি একক পাদটীকা আছে। তবুও এটি মানুষকে অবাক করার এক দুর্দান্ত উপায়।
সম্পাদনা (দ্রষ্টব্য: আমি এই উত্তরটি পোস্ট করিনি এমন একজন নই; আমি কেবল এটি যুক্ত করতে চেয়েছিলাম):
উপরের কোডটির একটি সম্পূর্ণ সি ++ সংস্করণ এখানে:
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];
}
}
}
}