অন্য স্ট্যাক ওভারফ্লো প্রশ্নের উত্তর ( এটি একটি ) আমি একটি আকর্ষণীয় সাব-সমস্যায় হোঁচট খেয়েছি । 6 টি পূর্ণসংখ্যার অ্যারে বাছাই করার দ্রুততম উপায় কী?
যেহেতু প্রশ্নটি খুব নিম্ন স্তরের:
- আমরা ধরে নিতে পারি না লাইব্রেরিগুলি উপলভ্য রয়েছে (এবং কলটির নিজস্ব মূল্য আছে), কেবল সরল সি
- নির্দেশ পাইপলাইন (ক আছে খালি এড়াতে খুব উচ্চ মূল্য) আমরা সম্ভবত শাখা কমান উচিত জাম্প, এবং নিয়ন্ত্রণ প্রতিটি অন্য ধরনের ভঙ্গ প্রবাহিত (ইন ক্রম পয়েন্ট পিছনে লুকিয়ে মত
&&
বা||
)। - ঘরটি সীমাবদ্ধ এবং নিবন্ধগুলি হ্রাস করা এবং মেমরির ব্যবহার একটি সমস্যা, আদর্শভাবে জায়গাটি সাজানো সম্ভবত সেরা।
সত্যই এই প্রশ্নটি এক ধরণের গল্ফ যেখানে লক্ষ্যটি উত্সের দৈর্ঘ্যকে হ্রাস করা নয় তবে সম্পাদনের সময়। আমি এটা 'Zening' কোড হিসেবে বইয়ের নাম ব্যবহার করা কল কোড অপ্টিমাইজেশান এর জেন দ্বারা মাইকেল Abrash এবং তার চরিত্র ।
কেন আকর্ষণীয় এটি হিসাবে বিভিন্ন স্তর রয়েছে:
- উদাহরণটি সহজ এবং বোঝার জন্য সহজ এবং পরিমাপ, খুব বেশি দক্ষতার সাথে জড়িত নয়
- এটি সমস্যার জন্য ভাল একটি অ্যালগরিদম নির্বাচনের প্রভাবগুলি দেখায় তবে সংকলক এবং অন্তর্নিহিত হার্ডওয়্যারগুলির প্রভাবগুলিও দেখায়।
এখানে আমার রেফারেন্স (নিষ্পাপ, অনুকূল নয়) বাস্তবায়ন এবং আমার পরীক্ষার সেট।
#include <stdio.h>
static __inline__ int sort6(int * d){
char j, i, imin;
int tmp;
for (j = 0 ; j < 5 ; j++){
imin = j;
for (i = j + 1; i < 6 ; i++){
if (d[i] < d[imin]){
imin = i;
}
}
tmp = d[j];
d[j] = d[imin];
d[imin] = tmp;
}
}
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}
int main(int argc, char ** argv){
int i;
int d[6][5] = {
{1, 2, 3, 4, 5, 6},
{6, 5, 4, 3, 2, 1},
{100, 2, 300, 4, 500, 6},
{100, 2, 3, 4, 500, 6},
{1, 200, 3, 4, 5, 600},
{1, 1, 2, 1, 2, 1}
};
unsigned long long cycles = rdtsc();
for (i = 0; i < 6 ; i++){
sort6(d[i]);
/*
* printf("d%d : %d %d %d %d %d %d\n", i,
* d[i][0], d[i][6], d[i][7],
* d[i][8], d[i][9], d[i][10]);
*/
}
cycles = rdtsc() - cycles;
printf("Time is %d\n", (unsigned)cycles);
}
কাঁচা ফলাফল
যেহেতু ভেরিয়েন্টগুলির সংখ্যা বড় হচ্ছে, আমি সেগুলি এখানে খুঁজে পাওয়া যায় এমন একটি টেস্ট স্যুইটে সংগ্রহ করেছি । প্রকৃত পরীক্ষাগুলি কেভিন স্টকের ধন্যবাদ, উপরে দেখানো থেকে কিছুটা নিখরচায়। আপনি এটি আপনার নিজের পরিবেশে সংকলন এবং সম্পাদন করতে পারেন। আমি বিভিন্ন টার্গেট আর্কিটেকচার / সংকলকগুলির আচরণের দ্বারা বেশ আগ্রহী। (ওকে বলছি, এর উত্তরে রাখুন, আমি নতুন ফলাফলের প্রতিটি অবদানকারীকে +1 করব)।
আমি এক বছর আগে ড্যানিয়েল স্টুটজবাচের (গল্ফিংয়ের জন্য) জবাব দিয়েছিলাম কারণ তিনি তখনকার দ্রুততম সমাধানের উত্সে ছিলেন (নেটওয়ার্কগুলি বাছাই)।
লিনাক্স 64 বিট, জিসিসি 4.6.1 64 বিট, ইন্টেল কোর 2 ডুও ই 8400, -ও 2
- Qsort লাইব্রেরি ফাংশনে সরাসরি কল: 689.38
- নিষ্পাপ বাস্তবায়ন (সন্নিবেশ সাজানোর): 285.70
- সন্নিবেশ বাছাই (ড্যানিয়েল স্টুটজবাচ): 142.12
- সন্নিবেশ বাছাই করুন তালিকাভুক্ত: 125.47
- র্যাঙ্ক অর্ডার: 102.26
- রেজিস্টারগুলির সাথে ক্রম ক্রম: 58.03
- নেটওয়ার্ক বাছাই করা (ড্যানিয়েল স্টুটজবাচ): 111.68
- নেটওয়ার্ক বাছাই (পল আর): 66.36
- দ্রুত অদলবদুর সাথে নেটওয়ার্ক 12 বাছাই করা হচ্ছে: 58.86
- বাছাই করা নেটওয়ার্কগুলি 12 পুনরায় সাজানো সোয়াপ: 53.74
- নেটওয়ার্কগুলি বাছাই করা 12 সরল সোয়্যাপটি পুনরায় সাজানো হয়েছে: 31.54
- পুনরায় সাজানো বাছাইকরণ নেটওয়ার্ক ডাব্লু / ফাস্ট অদলবদল: 31.54
- পুনরায় সাজানো বাছাইকরণ নেটওয়ার্ক ডাব্লু / দ্রুত স্যুইপ ভি 2: 33.63
- ইনলাইনড বুদ্বুদ সাজান (পাওলো বনজিনি): 48.85
- নিবন্ধভুক্ত সারণি বাছাই করুন (পাওলো বনজিনি): 75.30
লিনাক্স 64 বিট, জিসিসি 4.6.1 64 বিট, ইন্টেল কোর 2 ডুও ই 8400, -ও 1
- Qsort লাইব্রেরি ফাংশনে সরাসরি কল: 705.93
- নিষ্পাপ বাস্তবায়ন (সন্নিবেশ সাজানোর): 135.60
- সন্নিবেশ সাজান (ড্যানিয়েল স্টুটজবাচ): 142.11
- সন্নিবেশ বাছাই করুন তালিকাভুক্ত: 126.75
- র্যাঙ্ক অর্ডার: 46.42
- রেজিস্টারগুলির সাথে রেঙ্ক অর্ডার: 43.58
- নেটওয়ার্ক বাছাই করা (ড্যানিয়েল স্টুটজবাচ): 115.57
- নেটওয়ার্ক বাছাই (পল আর): .4৪.৪৪
- দ্রুত অদলবদুর সাথে নেটওয়ার্ক 12 বাছাই করা হচ্ছে: 61.98
- বাছাই করা নেটওয়ার্কগুলি 12 পুনরায় সাজানো সোয়াপ: 54.67
- নেটওয়ার্কগুলি বাছাই করা 12 সরল সোয়্যাপটি পুনরায় সাজানো হয়েছে: 31.54
- পুনরায় সাজানো বাছাইকরণ নেটওয়ার্ক ডাব্লু / ফাস্ট অদলবদল: 31.24
- পুনঃক্রমিত বাছাই নেটওয়ার্ক W / দ্রুত অদলবদল V2: 33.07
- ইনলাইন করা বুদ্বুদ সাজান (পাওলো বনজিনি): 45.79
- নিবন্ধভুক্ত সারণি বাছাই করুন (পাওলো বনজিনি): 80.15
আমি -O1 এবং -O2 ফলাফল উভয়ই অন্তর্ভুক্ত করেছি কারণ আশ্চর্যজনকভাবে বেশ কয়েকটি প্রোগ্রামের জন্য O2 O1 এর চেয়ে কম দক্ষ। আমি অবাক হই কোন নির্দিষ্ট অপ্টিমাইজেশনের এই প্রভাব আছে?
প্রস্তাবিত সমাধান সম্পর্কে মন্তব্য
সন্নিবেশ সাজান (ড্যানিয়েল স্টুটজবাচ)
যেমনটি প্রত্যাশিত ক্ষুদ্রাক্রমে শাখাগুলি হ'ল এটি অবশ্যই একটি ভাল ধারণা।
নেটওয়ার্ক বাছাই করা (ড্যানিয়েল স্টুটজবাচ)
সন্নিবেশ সাজানোর চেয়ে ভাল। আমি ভাবলাম যে যদি বাহ্যিক লুপটি এড়ানো থেকে প্রধান প্রভাবটি না পাওয়া যায়। আমি এটি পরীক্ষার জন্য অনিবন্ধিত সন্নিবেশ সাজানোর মাধ্যমে চেষ্টা করেছি এবং প্রকৃতপক্ষে আমরা প্রায় একই পরিসংখ্যান পাই (কোডটি এখানে )।
নেটওয়ার্ক বাছাই (পল আর)
এখন পর্যন্ত সেরা। আমি পরীক্ষার জন্য আসল কোডটি এখানে ব্যবহার করেছি । অন্যান্য সাজানোর নেটওয়ার্ক বাস্তবায়নের তুলনায় এটি কেন প্রায় দ্বিগুণ দ্রুত তা এখনও জানেন না। প্যারামিটার পাস হচ্ছে? দ্রুত সর্বোচ্চ?
নেটওয়ার্কগুলি 12 অদলবদল দ্রুত অদলবদলের সাথে বাছাই করা হচ্ছে
ড্যানিয়েল স্টুটজবাচের পরামর্শ অনুসারে, আমি শাখাবিহীন দ্রুত অদলবদলের (কোডটি এখানে ) তার সাথে 12 টি সোয়াপ বাছাই করার নেটওয়ার্কটি একত্রিত করেছি । এটি প্রকৃতপক্ষে দ্রুততর, 1 টি কম স্বাপের সাহায্যে আশা করা যায় এমন একটি ছোট মার্জিন (প্রায় 5%) দিয়ে সেরা এখন পর্যন্ত
এটি লক্ষণীয়ও আকর্ষণীয় যে পিপিসি আর্কিটেকচারে যদি ব্রাঞ্চহীন অদলবদলকে সহজ (4 গুণ) কম কার্যকর বলে মনে হয়।
লাইব্রেরি Qsort কল করা হচ্ছে
অন্য একটি রেফারেন্স পয়েন্ট দেওয়ার জন্য আমি কেবল লাইব্রেরি Qsort কল করার পরামর্শ হিসাবে চেষ্টা করেছি (কোডটি এখানে রয়েছে )। প্রত্যাশিত হিসাবে এটি অনেক ধীর: 10 থেকে 30 গুণ ধীর গতির ... নতুন পরীক্ষার স্যুইটের সাথে এটি স্পষ্ট হয়ে উঠলে, প্রধান সমস্যাটি প্রথম কলের পরে গ্রন্থাগারের প্রাথমিক লোড বলে মনে হয়, এবং এটি অন্যের সাথে এতটা খারাপভাবে তুলনা করে না ares সংস্করণ। এটি আমার লিনাক্সের চেয়ে ধীরে ধীরে 3 থেকে 20 গুণ কম। অন্যের দ্বারা পরীক্ষার জন্য ব্যবহৃত কিছু আর্কিটেকচারে এটি আরও দ্রুত বলে মনে হয় (গ্রন্থাগার কিউসোর্ট আরও জটিল এপিআই ব্যবহার করায় আমি এটি দেখে আশ্চর্য হয়েছি)।
পদক্রম
রেক্স কের আরও একটি সম্পূর্ণ ভিন্ন পদ্ধতি প্রস্তাব করেছিলেন: অ্যারে গণনা প্রতিটি আইটেমের জন্য সরাসরি এটির চূড়ান্ত অবস্থান। এটি দক্ষ কারণ গণনা র্যাঙ্ক অর্ডার শাখার প্রয়োজন নেই। এই পদ্ধতির অসুবিধাটি হ'ল এটি অ্যারের মেমরির পরিমাণের তিনগুণ বেশি নেয় (র্যাঙ্ক ক্রম সংরক্ষণের জন্য অ্যারে এবং ভেরিয়েবলগুলির একটি অনুলিপি)। পারফরম্যান্স ফলাফল খুব অবাক (এবং আকর্ষণীয়)। 32 বিট ওএস এবং ইন্টেল কোর 2 কোয়াড ই 8300 সহ আমার রেফারেন্স আর্কিটেকচারে, চক্র গণনাটি 1000 এর নীচে ছিল (ব্রাঞ্চিং সোয়াপযুক্ত নেটওয়ার্কগুলি বাছাই করার মতো)। কিন্তু যখন আমার its৪ বিট বাক্সে সংকলিত এবং কার্যকর করা হয়েছে (ইন্টেল কোর 2 ডুও) এটি আরও ভাল পারফর্ম করেছে: এটি এখন পর্যন্ত দ্রুততম হয়ে উঠেছে। অবশেষে আমি আসল কারণটি খুঁজে পেলাম। আমার 32 বিট বাক্সে জিসিসি 4.4.1 এবং আমার 64 বিট বাক্স জিসিসি 4.4 ব্যবহার করে।
আপডেট :
উপরের প্রকাশিত পরিসংখ্যানগুলি দেখায় যে এই প্রভাবটি এখনও সিসিসি পরবর্তী সংস্করণগুলির দ্বারা বর্ধিত হয়েছিল এবং রেঙ্ক অর্ডার অন্য কোনও বিকল্পের তুলনায় ধারাবাহিকভাবে দ্বিগুণ দ্রুত হয়ে ওঠে।
পুনরায় সাজানো অদলবদুর সাথে নেটওয়ার্কগুলি বাছাই করা 12
জিসিসি ৪.৪.৩ সহ রেক্স কের প্রস্তাবটির আশ্চর্য দক্ষতা আমাকে অবাক করে তুলেছে: ব্রাচলেস বাছাই করা নেটওয়ার্কগুলির চেয়ে 3 গুণ বেশি মেমরির ব্যবহার সহ একটি প্রোগ্রাম কীভাবে দ্রুত হতে পারে? আমার অনুমানটি ছিল যে এটি লেখার পরে পড়ার ধরণের নির্ভরতা কম ছিল, যার ফলে x86 এর সুপারক্যালার নির্দেশিকার সময়সূচীর আরও ভাল ব্যবহার করা যাবে। এটি আমাকে একটি ধারণা দিয়েছে: লেখার নির্ভরতার পরে পড়া কমিয়ে আনার জন্য অদলবদল পুনরায় অর্ডার করুন। আরও সহজভাবে বলতে গেলে: যখন আপনাকে SWAP(1, 2); SWAP(0, 2);
দ্বিতীয় স্বরূপ সম্পাদন করার আগে প্রথম অদলবদলটি শেষ হওয়ার জন্য অপেক্ষা করতে হবে কারণ একটি সাধারণ মেমরি কোষ উভয়ই অ্যাক্সেস করতে পারে। আপনি যখন SWAP(1, 2); SWAP(4, 5);
প্রসেসর সমান্তরাল উভয় চালিত করতে পারেন। আমি এটি চেষ্টা করেছি এবং এটি প্রত্যাশার মতো কাজ করে, বাছাই করার নেটওয়ার্কগুলি প্রায় 10% দ্রুত চলছে।
সরল সোয়াপ দিয়ে নেটওয়ার্কগুলি বাছাই করা 12
মূল পোস্টের এক বছর পর স্টেইনার এইচ। গাউনসন পরামর্শ দিয়েছিলেন, আমাদের কম্পাইলারকে আউটসামার্ট করার এবং অদলবদলের কোডটি সহজ রাখার চেষ্টা করা উচিত নয়। ফলস্বরূপ কোডটি প্রায় 40% দ্রুত হওয়ায় এটি সত্যিই একটি ভাল ধারণা! তিনি x86 ইনলাইন এসেম্বলি কোড ব্যবহার করে হাতে কলমে অপ্টিমাইজড একটি সোয়াপ প্রস্তাব করেছিলেন যা এখনও আরও কিছু চক্র এড়াতে পারে। সবচেয়ে আশ্চর্যজনক (এটি প্রোগ্রামারের মনোবিজ্ঞানের খণ্ডগুলি বলে) এটি যে এক বছর আগে ব্যবহৃত কেউই সেই অদলবদলের সংস্করণটি ব্যবহার করে নি। আমি পরীক্ষার জন্য যে কোডটি ব্যবহার করতাম তা এখানে । অন্যরা সি-র দ্রুত স্বাপটি লেখার জন্য অন্যান্য উপায়ের পরামর্শ দিয়েছিল, তবে এটি শালীন সংকলক সহ সাধারণের মতোই পারফরম্যান্স দেয়।
"সেরা" কোডটি এখন অনুসরণ হিসাবে রয়েছে:
static inline void sort6_sorting_network_simple_swap(int * d){
#define min(x, y) (x<y?x:y)
#define max(x, y) (x<y?y:x)
#define SWAP(x,y) { const int a = min(d[x], d[y]); \
const int b = max(d[x], d[y]); \
d[x] = a; d[y] = b; }
SWAP(1, 2);
SWAP(4, 5);
SWAP(0, 2);
SWAP(3, 5);
SWAP(0, 1);
SWAP(3, 4);
SWAP(1, 4);
SWAP(0, 3);
SWAP(2, 5);
SWAP(1, 3);
SWAP(2, 4);
SWAP(2, 3);
#undef SWAP
#undef min
#undef max
}
যদি আমরা আমাদের পরীক্ষার সেটটিকে বিশ্বাস করি (এবং, হ্যাঁ এটি বেশ দুর্বল, তবে এটির সুবিধায় স্বল্প, সহজ এবং আমরা কী পরিমাপ করছি তা বোঝা সহজ), এক ধরণের ফলাফলের কোডের চক্রের গড় সংখ্যা 40 টি চক্রের নীচে ( 6 পরীক্ষা কার্যকর করা হয়)। যা প্রতিটি স্বাপকে গড়ে ৪ টি চক্র রাখে। আমি আশ্চর্যরূপে দ্রুত কল। অন্য কোন উন্নতি সম্ভব?
__asm__ volatile (".byte 0x0f, 0x31; shlq $32, %%rdx; orq %%rdx, %0" : "=a" (x) : : "rdx");
কারণ rdtsc উত্তরটি EDX: EAX এ রাখে যখন GCC এটি একক 64৪-বিট রেজিস্টারে প্রত্যাশা করে। আপনি -O3 এ সংকলন করে বাগটি দেখতে পাচ্ছেন। আরও দ্রুত সুইপ সম্পর্কে পল আর-এর কাছে আমার মন্তব্য নীচে দেখুন।
CMP EAX, EBX; SBB EAX, EAX
0 বা 0xFFFFFFFF রাখবে । "orrowণ সহ বিয়োগ", ("বহন সহ যুক্ত করুন") এর সমকক্ষ ; আপনি যে স্ট্যাটাস বিটটি উল্লেখ করেছেন সেটি হ'ল ক্যারি বিট। তারপরে আবারও, আমার মনে আছে এবং পেন্টিয়াম 4 বনাম এবং এর ভয়াবহ লেটেন্সি ও থ্রুপুট ছিল এবং কোর সিপিইউতে এখনও দ্বিগুণ ধীর ছিল। 80386 সাল থেকে শর্তসাপেক্ষে দোকান এবং শর্তসাপেক্ষে স্থানান্তর নির্দেশাবলীও রয়েছে তবে সেগুলিও ধীর গতির। EAX
EAX
EBX
SBB
ADC
ADC
SBB
ADD
SUB
SETcc
CMOVcc
x-y
এবংx+y
ভূগর্ভস্থ বা প্রবাহের কারণ হবে না?