আমি ২০০৯ সালে প্রথম লক্ষ্য করেছি যে জিসিসি (কমপক্ষে আমার প্রকল্পগুলিতে এবং আমার মেশিনে) আমি গতির ( বা ) পরিবর্তে আকারের ( -Os
) এর জন্য অপ্টিমাইজ করলে খুব দ্রুততর কোড তৈরি করার প্রবণতা রয়েছে এবং কেন আমি তখন থেকেই ভাবছিলাম।-O2
-O3
আমি এই বিস্ময়কর আচরণটি দেখায় এবং এখানে পোস্ট করার জন্য যথেষ্ট ছোট এটি কোড (বরং নির্বোধ) কোড তৈরি করতে পরিচালিত করেছি।
const int LOOP_BOUND = 200000000;
__attribute__((noinline))
static int add(const int& x, const int& y) {
return x + y;
}
__attribute__((noinline))
static int work(int xval, int yval) {
int sum(0);
for (int i=0; i<LOOP_BOUND; ++i) {
int x(xval+sum);
int y(yval+sum);
int z = add(x, y);
sum += z;
}
return sum;
}
int main(int , char* argv[]) {
int result = work(*argv[1], *argv[2]);
return result;
}
যদি আমি এটি দিয়ে সংকলন করি -Os
তবে এই প্রোগ্রামটি কার্যকর করতে 0.38 s লাগবে এবং 0.44 গুলি যদি এটি সংকলিত হয় -O2
বা এর সাথে হয় -O3
। এই সময়গুলি ধারাবাহিকভাবে এবং কার্যত কোনও গোলমাল ছাড়াই প্রাপ্ত হয় (জিসিসি 4.7.2, x86_64 জিএনইউ / লিনাক্স, ইন্টেল কোর i5-3320M)।
(আপডেট: আমি সমস্ত অ্যাসেম্বলি কোড গিটহাবে স্থানান্তরিত করেছি : fno-align-*
পতাকাগুলি একই প্রভাব হওয়ায় তারা পোস্টটি স্ফীত করে দিয়েছে এবং সম্ভবত প্রশ্নগুলিতে খুব সামান্য মূল্য যুক্ত করেছে ))
এখানে -Os
এবং এর সাথে উত্পন্ন সমাবেশ -O2
।
দুর্ভাগ্যক্রমে, সমাবেশ সম্পর্কে আমার বোঝাপড়া খুব সীমিত, তাই আমি এর পরে যা করেছি তা সঠিক ছিল কিনা তা আমার কোনও ধারণা নেই: আমি সমাবেশটি ধরেছিলাম -O2
এবং লাইনগুলি -Os
বাদ দিয়ে এর সমস্ত পার্থক্যকে সমাবেশে .p2align
মিশেছি, ফলাফলটি এখানে । এই কোডটি এখনও 0.38 সেকেন্ডে চলে এবং কেবল পার্থক্যটি .p2align
স্টাফ।
যদি আমি সঠিকভাবে অনুমান করি তবে এগুলি স্ট্যাক সারিবদ্ধকরণের জন্য প্যাডিংস। মতে কেন NOPs সঙ্গে জিসিসি প্যাড কার্যাবলী করে? এই কোডটি দ্রুত চালিত হবে এই আশায় করা হয়েছে, তবে দৃশ্যত এই অপ্টিমাইজেশনটি আমার ক্ষেত্রে ব্যাকফায়ার হয়েছে।
এই প্যাডিং কি এই ক্ষেত্রে দোষী? কেন এবং কিভাবে?
গোলমাল এটি বেশ কার্যকর করে সময় মাইক্রো-অপ্টিমাইজেশানকে অসম্ভব করে তোলে।
আমি কীভাবে নিশ্চিত করতে পারি যে আমি সি বা সি ++ উত্স কোডে মাইক্রো-অপ্টিমাইজেশানগুলি (স্ট্যাক সারিবদ্ধকরণের সাথে সম্পর্কযুক্ত) করি না এমন দুর্ঘটনাক্রমে ভাগ্যবান / দুর্ভাগ্যযুক্ত সারিবদ্ধতাগুলি হস্তক্ষেপ করছে না?
হালনাগাদ:
পাস্কেল কুয়াকের উত্তর অনুসরণ করে আমি সামঞ্জস্যের সাথে কিছুটা টিনক করেছিলাম। গিসি তে পাস -O2 -fno-align-functions -fno-align-loops
করার মাধ্যমে, সমস্ত .p2align
বিধানসভা থেকে চলে যায় এবং 0.38 এর দশকে উত্পাদিত এক্সিকিউটেবল রান হয়। সিসি ডকুমেন্টেশন অনুসারে :
-Os সমস্ত -O2 অনুকূলিতকরণ সক্ষম করে [তবে] -ও নিম্নলিখিত অপ্টিমাইজেশন পতাকাগুলি অক্ষম করে:
-falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays
সুতরাং, এটি বেশিরভাগই মনে হয় (ভুল) প্রান্তিককরণের মতো সমস্যা।
আমি এখনও সম্পর্কে সন্দিহান আছি -march=native
হিসাবে প্রস্তাব Marat দুখান এর উত্তর । আমি নিশ্চিত নই যে এটি কেবল (ভুল) প্রান্তিককরণ ইস্যুতে হস্তক্ষেপ করছে না; আমার মেশিনে এর পুরোপুরি কোনও প্রভাব নেই। (তবুও, আমি তার উত্তর upvated।)
আপডেট 2:
আমরা -Os
ছবিটি বাইরে নিতে পারি । নিম্নলিখিত সময়গুলি সংকলন দ্বারা প্রাপ্ত হয়
-O2 -fno-omit-frame-pointer
0.37s-O2 -fno-align-functions -fno-align-loops
0.37s-S -O2
তারপরে 0.37 এরadd()
পরে ম্যানুয়ালিwork()
অ্যাসেমব্লিলিটি সরানো-O2
0.44s
দেখে মনে হচ্ছে add()
কল সাইট থেকে দূরত্ব অনেক বেশি গুরুত্বপূর্ণ matters আমি চেষ্টা করেছি perf
, কিন্তু আউটপুট perf stat
এবংperf report
আমার জন্য খুবই সামান্য জ্ঞান করে তোলে। তবে আমি এর মধ্যে কেবল একটি ধারাবাহিক ফলাফল পেতে পারি:
-O2
:
602,312,864 stalled-cycles-frontend # 0.00% frontend cycles idle
3,318 cache-misses
0.432703993 seconds time elapsed
[...]
81.23% a.out a.out [.] work(int, int)
18.50% a.out a.out [.] add(int const&, int const&) [clone .isra.0]
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ return x + y;
100.00 ¦ lea (%rdi,%rsi,1),%eax
¦ }
¦ ? retq
[...]
¦ int z = add(x, y);
1.93 ¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
79.79 ¦ add %eax,%ebx
এর জন্য fno-align-*
:
604,072,552 stalled-cycles-frontend # 0.00% frontend cycles idle
9,508 cache-misses
0.375681928 seconds time elapsed
[...]
82.58% a.out a.out [.] work(int, int)
16.83% a.out a.out [.] add(int const&, int const&) [clone .isra.0]
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ return x + y;
51.59 ¦ lea (%rdi,%rsi,1),%eax
¦ }
[...]
¦ __attribute__((noinline))
¦ static int work(int xval, int yval) {
¦ int sum(0);
¦ for (int i=0; i<LOOP_BOUND; ++i) {
¦ int x(xval+sum);
8.20 ¦ lea 0x0(%r13,%rbx,1),%edi
¦ int y(yval+sum);
¦ int z = add(x, y);
35.34 ¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
39.48 ¦ add %eax,%ebx
¦ }
এর জন্য -fno-omit-frame-pointer
:
404,625,639 stalled-cycles-frontend # 0.00% frontend cycles idle
10,514 cache-misses
0.375445137 seconds time elapsed
[...]
75.35% a.out a.out [.] add(int const&, int const&) [clone .isra.0] ¦
24.46% a.out a.out [.] work(int, int)
[...]
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
18.67 ¦ push %rbp
¦ return x + y;
18.49 ¦ lea (%rdi,%rsi,1),%eax
¦ const int LOOP_BOUND = 200000000;
¦
¦ __attribute__((noinline))
¦ static int add(const int& x, const int& y) {
¦ mov %rsp,%rbp
¦ return x + y;
¦ }
12.71 ¦ pop %rbp
¦ ? retq
[...]
¦ int z = add(x, y);
¦ ? callq add(int const&, int const&) [clone .isra.0]
¦ sum += z;
29.83 ¦ add %eax,%ebx
দেখে মনে হচ্ছে আমরা কলটিতে স্টল করছি add()
মন্থর অবস্থায় ।
আমি পরীক্ষা আছে সবকিছু যেperf -e
আমার মেশিনে থুতু ফেলতে পারে ; উপরে দেওয়া হয় না শুধুমাত্র পরিসংখ্যান।
একই এক্সিকিউটেবলের জন্য, stalled-cycles-frontend
সম্পাদনের সময়টির সাথে লিনিয়ার পারস্পরিক সম্পর্ক দেখায়; আমি অন্য কোনও বিষয় লক্ষ্য করি নি যা এত পরিষ্কারভাবে সম্পর্কিত হতে পারে। (তুলনাstalled-cycles-frontend
বিভিন্ন এক্সিকিউটেবলের সাথে করা আমার কাছে বোধগম্য নয়))
প্রথম মন্তব্য হিসাবে আসার সাথে সাথে আমি ক্যাশে মিসগুলি অন্তর্ভুক্ত করেছি। কেবলমাত্র উপরেরটি perf
নয়, আমার মেশিনে মেশানো যায় এমন সমস্ত ক্যাশে মিস করেছি । ক্যাশে মিস করা খুব শোরগোলযুক্ত এবং মৃত্যুদন্ডের সময়গুলির সাথে কোনও সম্পর্ক নেই।