আমি যদি গতির পরিবর্তে আকারের জন্য অপ্টিমাইজ করি তবে জিসিসি কেন 15-20% দ্রুত কোড উত্পন্ন করে?


445

আমি ২০০৯ সালে প্রথম লক্ষ্য করেছি যে জিসিসি (কমপক্ষে আমার প্রকল্পগুলিতে এবং আমার মেশিনে) আমি গতির ( বা ) পরিবর্তে আকারের ( -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নয়, আমার মেশিনে মেশানো যায় এমন সমস্ত ক্যাশে মিস করেছি । ক্যাশে মিস করা খুব শোরগোলযুক্ত এবং মৃত্যুদন্ডের সময়গুলির সাথে কোনও সম্পর্ক নেই।


36
অন্ধ অনুমান: এটি কি ক্যাশে মিস হতে পারে?

@ এইচ 2 সি 3 এটি আমার প্রথম চিন্তাও ছিল, তবে ওপির প্রশ্নটি গভীরভাবে না পড়ে এবং বুঝতে না পেরে মন্তব্য পোস্ট করার পক্ষে যথেষ্ট উত্সাহিত হয়নি।
ῥεῖ

2
@ g-maululik এ কারণেই আমি সতর্ক করেছিলাম যে এটি একটি "অন্ধ অনুমান" ;-) "টিএল; ডিআর" খারাপ প্রশ্নের জন্য সংরক্ষিত। : পি

3
শুধু একটি আকর্ষণীয় ডাটা পয়েন্ট: (। আমি জিসিসি সঙ্গে প্রতিলিপি চেষ্টা করিনি) আমি এটি -O3 বা -Ofast 1.5x হিসাবে দ্রুত -Os যেমন আমি যখন OS X এর উপর ঝনঝন সঙ্গে এই কম্পাইল সম্পর্কে যে
রব নেপিয়ের

2
এটি একই কোড। .L3 এর ঠিকানাটি ঘনিষ্ঠভাবে দেখুন, বিভক্ত শাখার লক্ষ্যগুলি ব্যয়বহুল।
হ্যানস প্যাস্যান্ট

উত্তর:


504

ডিফল্ট সংকলকগণ "গড়" প্রসেসরের জন্য অনুকূলিত হন। যেহেতু বিভিন্ন প্রসেসর বিভিন্ন নির্দেশের অনুক্রমের পক্ষে, তাই সংকলক অপ্টিমাইজেশানগুলি সক্ষম -O2হতে পারে গড় প্রসেসরের উপকার করতে পারে, তবে আপনার নির্দিষ্ট প্রসেসরের (এবং এটি একই সাথে প্রযোজ্য -Os) কার্যকারিতা হ্রাস করতে পারে । আপনি যদি বিভিন্ন প্রসেসরের উপর একই উদাহরণ ব্যবহার করে দেখেন যে আপনি তাদের -O2বেশিরভাগের পক্ষে উপকার পেয়েছেন অন্যদিকে আরও অনুকূল-Os অপ্টিমাইজেশনের ।

time ./test 0 0বেশ কয়েকটি প্রসেসরের ফলাফল এখানে রয়েছে (ব্যবহারকারীর সময় রিপোর্ট করা):

Processor (System-on-Chip)             Compiler   Time (-O2)  Time (-Os)  Fastest
AMD Opteron 8350                       gcc-4.8.1    0.704s      0.896s      -O2
AMD FX-6300                            gcc-4.8.1    0.392s      0.340s      -Os
AMD E2-1800                            gcc-4.7.2    0.740s      0.832s      -O2
Intel Xeon E5405                       gcc-4.8.1    0.603s      0.804s      -O2
Intel Xeon E5-2603                     gcc-4.4.7    1.121s      1.122s       -
Intel Core i3-3217U                    gcc-4.6.4    0.709s      0.709s       -
Intel Core i3-3217U                    gcc-4.7.3    0.708s      0.822s      -O2
Intel Core i3-3217U                    gcc-4.8.1    0.708s      0.944s      -O2
Intel Core i7-4770K                    gcc-4.8.1    0.296s      0.288s      -Os
Intel Atom 330                         gcc-4.8.1    2.003s      2.007s      -O2
ARM 1176JZF-S (Broadcom BCM2835)       gcc-4.6.3    3.470s      3.480s      -O2
ARM Cortex-A8 (TI OMAP DM3730)         gcc-4.6.3    2.727s      2.727s       -
ARM Cortex-A9 (TI OMAP 4460)           gcc-4.6.3    1.648s      1.648s       -
ARM Cortex-A9 (Samsung Exynos 4412)    gcc-4.6.3    1.250s      1.250s       -
ARM Cortex-A15 (Samsung Exynos 5250)   gcc-4.7.2    0.700s      0.700s       -
Qualcomm Snapdragon APQ8060A           gcc-4.8       1.53s       1.52s      -Os

কিছু ক্ষেত্রে আপনি gccআপনার নির্দিষ্ট প্রসেসরের জন্য অপ্টিমাইজ করতে বলে (অপশনগুলি ব্যবহার করে -mtune=nativeবা -march=native): অসুবিধাগুলি অনুকূলকরণের প্রভাবকে হ্রাস করতে পারেন :

Processor            Compiler   Time (-O2 -mtune=native) Time (-Os -mtune=native)
AMD FX-6300          gcc-4.8.1         0.340s                   0.340s
AMD E2-1800          gcc-4.7.2         0.740s                   0.832s
Intel Xeon E5405     gcc-4.8.1         0.603s                   0.803s
Intel Core i7-4770K  gcc-4.8.1         0.296s                   0.288s

আপডেট করুন: আইভি ব্রিজ ভিত্তিক কোর উপর তিনটি সংস্করণ i3 gcc( 4.6.4, 4.7.3, এবং 4.8.1উল্লেখযোগ্যভাবে ভিন্ন কর্মক্ষমতা সঙ্গে) উত্পাদন বাইনেরিতে কিন্তু সমাবেশ কোড শুধুমাত্র সূক্ষ্ম তারতম্য রয়েছে। এখনও পর্যন্ত, আমার এই সত্যের কোনও ব্যাখ্যা নেই।

সমাবেশ থেকে gcc-4.6.4 -Os(0.709 সেকেন্ডে কার্যকর করা):

00000000004004d2 <_ZL3addRKiS0_.isra.0>:
  4004d2:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004d5:       c3                      ret

00000000004004d6 <_ZL4workii>:
  4004d6:       41 55                   push   r13
  4004d8:       41 89 fd                mov    r13d,edi
  4004db:       41 54                   push   r12
  4004dd:       41 89 f4                mov    r12d,esi
  4004e0:       55                      push   rbp
  4004e1:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  4004e6:       53                      push   rbx
  4004e7:       31 db                   xor    ebx,ebx
  4004e9:       41 8d 34 1c             lea    esi,[r12+rbx*1]
  4004ed:       41 8d 7c 1d 00          lea    edi,[r13+rbx*1+0x0]
  4004f2:       e8 db ff ff ff          call   4004d2 <_ZL3addRKiS0_.isra.0>
  4004f7:       01 c3                   add    ebx,eax
  4004f9:       ff cd                   dec    ebp
  4004fb:       75 ec                   jne    4004e9 <_ZL4workii+0x13>
  4004fd:       89 d8                   mov    eax,ebx
  4004ff:       5b                      pop    rbx
  400500:       5d                      pop    rbp
  400501:       41 5c                   pop    r12
  400503:       41 5d                   pop    r13
  400505:       c3                      ret

সমাবেশ থেকে gcc-4.7.3 -Os(0.822 সেকেন্ডে কার্যকর করা):

00000000004004fa <_ZL3addRKiS0_.isra.0>:
  4004fa:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004fd:       c3                      ret

00000000004004fe <_ZL4workii>:
  4004fe:       41 55                   push   r13
  400500:       41 89 f5                mov    r13d,esi
  400503:       41 54                   push   r12
  400505:       41 89 fc                mov    r12d,edi
  400508:       55                      push   rbp
  400509:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  40050e:       53                      push   rbx
  40050f:       31 db                   xor    ebx,ebx
  400511:       41 8d 74 1d 00          lea    esi,[r13+rbx*1+0x0]
  400516:       41 8d 3c 1c             lea    edi,[r12+rbx*1]
  40051a:       e8 db ff ff ff          call   4004fa <_ZL3addRKiS0_.isra.0>
  40051f:       01 c3                   add    ebx,eax
  400521:       ff cd                   dec    ebp
  400523:       75 ec                   jne    400511 <_ZL4workii+0x13>
  400525:       89 d8                   mov    eax,ebx
  400527:       5b                      pop    rbx
  400528:       5d                      pop    rbp
  400529:       41 5c                   pop    r12
  40052b:       41 5d                   pop    r13
  40052d:       c3                      ret

সমাবেশ থেকে gcc-4.8.1 -Os(0.994 সেকেন্ডে কার্যকর করা):

00000000004004fd <_ZL3addRKiS0_.isra.0>:
  4004fd:       8d 04 37                lea    eax,[rdi+rsi*1]
  400500:       c3                      ret

0000000000400501 <_ZL4workii>:
  400501:       41 55                   push   r13
  400503:       41 89 f5                mov    r13d,esi
  400506:       41 54                   push   r12
  400508:       41 89 fc                mov    r12d,edi
  40050b:       55                      push   rbp
  40050c:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  400511:       53                      push   rbx
  400512:       31 db                   xor    ebx,ebx
  400514:       41 8d 74 1d 00          lea    esi,[r13+rbx*1+0x0]
  400519:       41 8d 3c 1c             lea    edi,[r12+rbx*1]
  40051d:       e8 db ff ff ff          call   4004fd <_ZL3addRKiS0_.isra.0>
  400522:       01 c3                   add    ebx,eax
  400524:       ff cd                   dec    ebp
  400526:       75 ec                   jne    400514 <_ZL4workii+0x13>
  400528:       89 d8                   mov    eax,ebx
  40052a:       5b                      pop    rbx
  40052b:       5d                      pop    rbp
  40052c:       41 5c                   pop    r12
  40052e:       41 5d                   pop    r13
  400530:       c3                      ret

186
কেবল এটি পরিষ্কার করার জন্য: আপনি কি আসলে গিয়ে 12 টি বিভিন্ন প্ল্যাটফর্মে ওপির কোডটির কার্যকারিতা পরিমাপ করেছিলেন? (আপনি যে এটি করবেন এই নিছক
ধারণাটির

194
@ অ্যানাটলিগ হ্যাঁ, আমি করেছি! (এবং শীঘ্রই আরও কিছু যোগ করবে)
মারাত দুখান

43
প্রকৃতপক্ষে. ভিন্ন ভিন্ন সিপিইউ সম্পর্কে না শুধুমাত্র তাত্ত্বিকতার জন্যই কিন্তু এটি প্রমাণ করার জন্য আরও একটি +1 । গতি সম্পর্কিত প্রতিটি উত্তরে আপনি কিছু (হায়রে) দেখেন না। এই পরীক্ষাগুলি কি একই ওএস দিয়ে চলছে? (যতটা সম্ভব এটি
ফলাফলটিকে স্কিঙ্ক করে

7
অলি এএমডি-এফএক্স-তে 6300 -O2 -fno-align-functions -fno-align-loopsসময় নেমে যায় 0.340s, তাই এটি প্রান্তিককরণের মাধ্যমে ব্যাখ্যা করা যায়। তবে অনুকূল প্রান্তিককরণটি প্রসেসর-নির্ভর: কিছু প্রসেসর প্রান্তিককরণযুক্ত লুপ এবং ফাংশন পছন্দ করে।
মারাত দুখন

13
@ জংওয়্যার আমি দেখতে পাচ্ছি না ওএস কীভাবে ফলাফলগুলিতে উল্লেখযোগ্যভাবে প্রভাব ফেলবে; লুপ কখনই সিস্টেম কল করে না।
আলী

186

আমার সহকর্মী আমাকে আমার প্রশ্নের একটি প্রশ্রয়জনক উত্তর খুঁজে পেতে সহায়তা করেছেন। তিনি 256 বাইট সীমানার গুরুত্ব লক্ষ্য করেছেন। তিনি এখানে নিবন্ধীকৃত নেই এবং আমাকে উত্তর পোস্ট করতে উত্সাহিত করেছেন (এবং সমস্ত খ্যাতি নিন) take


সংক্ষিপ্ত উত্তর:

এই প্যাডিং কি এই ক্ষেত্রে দোষী? কেন এবং কিভাবে?

এগুলি সমস্ত প্রান্তিককরণে ফোটে। সারিবদ্ধকরণগুলি পারফরম্যান্সের উপর উল্লেখযোগ্য প্রভাব ফেলতে পারে, এজন্যই আমাদের -falign-*পতাকাগুলি প্রথম স্থানে রয়েছে।

আমি সিসি বিকাশকারীদের কাছে একটি (বোগাস?) বাগ রিপোর্ট জমা দিয়েছি । দেখা যাচ্ছে যে ডিফল্ট আচরণটি "আমরা ডিফল্টরূপে 8 টি বাইটে লুপগুলি প্রান্তিককরণ করি তবে 10 বাইটের বেশি পূরণ করার প্রয়োজন না হলে এটি 16 বাইটে সারিবদ্ধ করার চেষ্টা করি।" স্পষ্টতই, এই বিশেষ ক্ষেত্রে এবং আমার মেশিনে এই ডিফল্টটি সেরা পছন্দ নয়। -O3যথাযথ প্রান্তিককরণের সাথে 3.4 (ট্রাঙ্ক) ঝাঁকুনি দেওয়া এবং উত্পন্ন কোডটি এই অদ্ভুত আচরণটি দেখায় না।

অবশ্যই, যদি কোনও অনুপযুক্ত প্রান্তিককরণ করা হয় তবে এটি জিনিসগুলিকে আরও খারাপ করে। একটি অপ্রয়োজনীয় / খারাপ সারিবদ্ধতা কেবল অকারণে বাইট খায় এবং সম্ভাব্যভাবে ক্যাশে মিস করা ইত্যাদি বৃদ্ধি করে etc.

গোলমাল এটি বেশ কার্যকর করে সময় মাইক্রো-অপ্টিমাইজেশানকে অসম্ভব করে তোলে।

আমি কীভাবে নিশ্চিত করতে পারি যে আমি সি বা সি ++ উত্স কোডগুলিতে মাইক্রো-অপটিমাইজেশন (স্ট্যাক অ্যালাইনমেন্টের সাথে সম্পর্কযুক্ত) করি না এমন দুর্ঘটনাক্রমে ভাগ্যবান / দুর্ভাগ্যযুক্ত সারিবদ্ধতাগুলি হস্তক্ষেপ করছে না?

কেবলমাত্র জিসিসি-কে সঠিক প্রান্তিককরণ করতে বলার মাধ্যমে:

g++ -O2 -falign-functions=16 -falign-loops=16


দীর্ঘ উত্তর:

কোডটি ধীরে চলবে যদি:

  • মাঝখানে একটি XXবাইট সীমানা কাটা add()( XXমেশিন নির্ভর)

  • যদি কলটি বাইট সীমানা add()পেরিয়ে যেতে হয় XXএবং লক্ষ্যটি সারিবদ্ধ না হয়।

  • যদি add()সারিবদ্ধ না হয়

  • যদি লুপটি সারিবদ্ধ না হয়।

মারাত দুখান সদয়ভাবে পোস্ট করা কোড এবং ফলাফলগুলিতে প্রথম 2 সুন্দরভাবে দৃশ্যমান । এই ক্ষেত্রে, gcc-4.8.1 -Os(0.994 সেকেন্ডে কার্যকর করা হয়):

00000000004004fd <_ZL3addRKiS0_.isra.0>:
  4004fd:       8d 04 37                lea    eax,[rdi+rsi*1]
  400500:       c3   

একটি 256 বাইট সীমানা add()ঠিক মাঝখানে কাটবে এবং না add()লুপটি একত্রিত হবে না। অবাক, অবাক, এটাই সবচেয়ে ধীরতম ঘটনা!

যদি gcc-4.7.3 -Os(0.822 সেকেন্ডে কার্যকর করা হয়), 256 বাইট সীমানা কেবল একটি ঠান্ডা অংশে কাটা হয় (তবে লুপটিও কাটা হয় না add()বা কাটা হয় না):

00000000004004fa <_ZL3addRKiS0_.isra.0>:
  4004fa:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004fd:       c3                      ret

[...]

  40051a:       e8 db ff ff ff          call   4004fa <_ZL3addRKiS0_.isra.0>

কিছুই সারিবদ্ধ হয় না, এবং কল add() 256 বাইট সীমানা ছাড়িয়ে যেতে হবে। এই কোডটি দ্বিতীয় ধীরতম।

যদি gcc-4.6.4 -Os(0.709 সেকেন্ডে কার্যকর করা হয়), যদিও কিছুই প্রান্তিক add()না করা হয়েছে , কলটি 256 বাইট সীমানা ছাড়িয়ে যেতে হবে না এবং লক্ষ্যটি ঠিক 32 বাইট দূরে রয়েছে:

  4004f2:       e8 db ff ff ff          call   4004d2 <_ZL3addRKiS0_.isra.0>
  4004f7:       01 c3                   add    ebx,eax
  4004f9:       ff cd                   dec    ebp
  4004fb:       75 ec                   jne    4004e9 <_ZL4workii+0x13>

এটি তিনটির মধ্যেই দ্রুততম। কেন তার মেশিনে 256 বাইট সীমানা সংক্ষিপ্ত হয়, আমি এটি বের করার জন্য এটি তার কাছে রেখে দেব। আমার কাছে এমন প্রসেসর নেই।

এখন, আমার মেশিনে আমি এই 256 বাইট সীমানা প্রভাব পাই না। শুধুমাত্র ফাংশন এবং লুপ সারিবদ্ধতা আমার মেশিনে কিক করে। আমি যদি পাস করি g++ -O2 -falign-functions=16 -falign-loops=16তবে সবকিছু স্বাভাবিক অবস্থায় ফিরে আসবে: আমি সবসময় দ্রুততম কেস পাই এবং সময়টি -fno-omit-frame-pointerআর পতাকা সম্পর্কিত সংবেদনশীল নয়। আমি পাস করতে পারি g++ -O2 -falign-functions=32 -falign-loops=32বা 16 এর যেকোন গুণকে, কোডটিও এতে সংবেদনশীল নয়।

আমি ২০০৯ সালে প্রথম লক্ষ্য করেছি যে সিসি (কমপক্ষে আমার প্রকল্পগুলিতে এবং আমার মেশিনে) উল্লেখযোগ্যভাবে দ্রুত কোড উত্পন্ন করার প্রবণতা রয়েছে যদি আমি গতির পরিবর্তে আকারের (-Os) জন্য অনুকূলিত হন (-O2 বা -O3) এবং আমি ভাবছিলাম কখন থেকে কেন।

সম্ভবত ব্যাখ্যাটি হ'ল আমার হটস্পটগুলি ছিল যা এই সারিবদ্ধকরণের জন্য সংবেদনশীল ছিল, যেমন এই উদাহরণের মতো। পতাকাগুলির সাথে জগাখিচু হয়ে ( -Osপরিবর্তে উত্তীর্ণ হয়ে -O2), সেই হটস্পটগুলি দুর্ঘটনার দ্বারা ভাগ্যবান উপায়ে সংযুক্ত করা হয়েছিল এবং কোডটি আরও দ্রুত হয়ে ওঠে। আকারের অনুকূলকরণের সাথে এর কোনও সম্পর্ক ছিল না: এগুলি নিখুঁত দুর্ঘটনার দ্বারা হটস্পটগুলি আরও ভালভাবে সাজানো হয়েছিল। এখন থেকে, আমি আমার প্রকল্পগুলিতে প্রান্তিককরণের প্রভাবগুলি যাচাই করব।

ওহ, এবং আরও একটি জিনিস। উদাহরণস্বরূপ দেখানো ধরণের মতো এই জাতীয় হটস্পটগুলি কীভাবে উঠতে পারে? কীভাবে এই জাতীয় ক্ষুদ্র ক্রিয়াকলাপের ইনলাইনিং add()ব্যর্থ হতে পারে?

এই বিবেচনা:

// add.cpp
int add(const int& x, const int& y) {
    return x + y;
}

এবং একটি পৃথক ফাইলে:

// main.cpp
int add(const int& x, const int& y);

const int LOOP_BOUND = 200000000;

__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;
}

এবং হিসাবে কম্পাইল: g++ -O2 add.cpp main.cpp

      জিসিসি ইনলাইন হবে না add()!

এতটুকু, অনিচ্ছাকৃতভাবে ওপি-র মতো হটস্পট তৈরি করা এত সহজ। অবশ্যই এটি আংশিকভাবে আমার দোষ: জিসিসি একটি দুর্দান্ত সংকলক। যদি উপরের হিসাবে সংকলিত হয়:,g++ -O2 -flto add.cpp main.cpp এটি, যদি আমি লিঙ্ক টাইম অপ্টিমাইজেশান সম্পাদন করি তবে কোডটি 0.19 এর মধ্যে চলে!

(ইনলাইনিং অপারেটিংমে কৃত্রিমভাবে অক্ষম করা হয়েছে, সুতরাং, ওপিতে কোডটি 2x ধীর ছিল)।


19
বাহ ... এটি বেঞ্চমার্কিং এর সাথে জড়িত আচরণগুলি ঘিরে আমি সাধারণত যা করি তা ছাড়িয়ে যায়।
রহস্যময়

@ অলি আমার অনুমান যে এই ধারণাটি বোধ হয় যেহেতু সংকলকটি এমন কিছু দেখায় না যা ইনলাইন করতে পারে? সম্ভবত আমরা inlineশিরোনামে + ফাংশন সংজ্ঞা ব্যবহার করি । জিসিসিতে এলটিও কতটা পরিপক্ক তা নিশ্চিত নয়। কমপক্ষে মিংডুতে এটির সাথে আমার অভিজ্ঞতা হিট বা মিস।
গ্রেটওয়ल्फ

7
আমি মনে করি এটি ACM এর যোগাযোগ ছিল যে বেশ কয়েক বছর আগে মোটামুটি বড় অ্যাপ্লিকেশনগুলি (পারল, স্পাইস ইত্যাদি) চালানোর বিষয়ে একটি নিবন্ধ ছিল যখন পুরো বাইনারি চিত্রটি একসাথে এক বাইটে বিভিন্ন আকারের লিনাক্স পরিবেশ ব্যবহার করে চালানো হয়েছিল। আমি 15% বা তার বেশি বৈকল্পিকতার কথা স্মরণ করি। তাদের সংক্ষিপ্তসারটি ছিল যে অনেক বেনমার্ক ফলাফল অকেজো কারণ এই প্রান্তিককরণের এই বাহ্যিক চলকটি বিবেচনায় নেওয়া হয় না।
জিন

1
আপ বিশেষত জন্য -flto। অভিজ্ঞতা থেকে বলার আগে যদি আপনি এটি আগে কখনও ব্যবহার না করেন তবে এটি বেশ বিপ্লবী :) :)
আন্ডারস্কোর_৩

2
এটি একটি দুর্দান্ত ভিডিও যা অ্যালাইনমেন্টটি পারফরম্যান্সকে কীভাবে প্রভাবিত করতে পারে এবং কীভাবে এটির জন্য প্রোফাইল তৈরি করতে পারে তা নিয়ে কথা বলছে: youtube.com/watch?Time_continue=1&v=r-TLSBdHe1A
Zhro

73

আমি এই পোস্টটি গ্রহণ করে এটিকে যুক্ত করে বলছি যে প্রোগ্রামগুলি - বড়গুলি সহ - সামগ্রিক পারফরম্যান্সে প্রান্তিককরণের প্রভাবগুলি অধ্যয়ন করা হয়েছে। উদাহরণস্বরূপ, এই নিবন্ধ (এবং আমি বিশ্বাস করি যে এর একটি সংস্করণও সিএসিএম-এ প্রকাশিত হয়েছিল) এটি দেখায় যে কীভাবে লিঙ্ক অর্ডার এবং ওএস পরিবেশের আকার পরিবর্তনগুলি পারফরম্যান্সকে উল্লেখযোগ্যভাবে স্থানান্তর করতে যথেষ্ট ছিল। তারা এটিকে "হট লুপস" এর প্রান্তিককরণের জন্য দায়ী করে।

"স্পষ্টত ভুল কিছু না করে ভুল ডেটা উত্পাদন করা" শিরোনামে এই কাগজটি! বলেছেন যে প্রোগ্রাম চলমান পরিবেশে প্রায় অনিয়ন্ত্রিত পার্থক্যের কারণে অজান্তে পরীক্ষামূলক পক্ষপাতটি সম্ভবত অনেকগুলি বেঞ্চমার্ক ফলাফলকে অর্থহীন রূপ দেয়।

আমি মনে করি আপনি একই পর্যবেক্ষণে একটি ভিন্ন কোণ সম্মুখীন হচ্ছেন।

পারফরম্যান্স-ক্রিটিকাল কোডের জন্য, এটি এমন সিস্টেমগুলির জন্য একটি দুর্দান্ত যুক্তি যা ইনস্টলেশন বা রান সময় সময় পরিবেশকে মূল্যায়ন করে এবং কী রুটিনগুলির বিভিন্নভাবে অনুকূলিত সংস্করণগুলির মধ্যে স্থানীয় সেরাটিকে বেছে নেয়।


33

আমি মনে করি আপনি যেমন করেছেন তেমন ফলাফল পেতে পারেন:

আমি -O2-এর জন্য অ্যাসেম্বলি দখল করেছি এবং এর সমস্ত পার্থক্যগুলি .O2 এর জন্য অ্যাসেমব্লিতে একীভূত করেছি .p2 এলাইন লাইনগুলি ব্যতীত:

... ব্যবহার করে -O2 -falign-functions=1 -falign-jumps=1 -falign-loops=1 -falign-labels=1। আমি এই বিকল্পগুলি দিয়ে সবকিছু সংকলন করছি, যা -O2আমি 15 বছর ধরে প্রতিবার পরিমাপ করতে বিরক্ত করে তুলনায় দ্রুত ছিল ।

এছাড়াও, সম্পূর্ণ ভিন্ন প্রসঙ্গে (একটি পৃথক সংকলক সহ) আমি লক্ষ্য করেছি যে পরিস্থিতিটি একই রকম : বিকল্পটি কোডের আকার এবং গতির জন্য "গতির চেয়ে কোডের আকারকে অনুকূলিতকরণ" বলে মনে করে।

যদি আমি সঠিকভাবে অনুমান করি তবে এগুলি স্ট্যাক সারিবদ্ধকরণের জন্য প্যাডিংস।

না, স্ট্যাকের সাথে এর কোনও যোগসূত্র নেই, ডিফল্টরূপে উত্পন্ন এনওপিগুলি এবং সেই বিকল্পগুলি - ফালাইন - * = 1 প্রতিরোধের কোড প্রান্তিককরণের জন্য।

কেন জিসিসি প্যাড এনওপিগুলিতে কাজ করে? আশা করা যায় যে কোডটি দ্রুত চলবে তবে স্পষ্টতই এই অপ্টিমাইজেশনটি আমার ক্ষেত্রে ব্যাকফায়ার্ড।

এই প্যাডিং কি এই ক্ষেত্রে দোষী? কেন এবং কিভাবে?

এটা খুব সম্ভবত প্যাডিং অপরাধী হয়। প্যাডিং প্রয়োজনীয় বলে মনে হচ্ছে এবং কিছু ক্ষেত্রে এটি কার্যকর কারণ হ'ল কোডটি সাধারণত 16 বাইটের লাইনে আনা হয় ( বিশদগুলির জন্য অ্যাগনার ফগের অনুকূলিতকরণ সংস্থানগুলি দেখুন, যা প্রসেসরের মডেল অনুসারে পরিবর্তিত হয়)। একটি ফাংশন, লুপ, বা একটি 16-বাইট সীমানায় লেবেল সারিবদ্ধ করার অর্থ হ'ল সম্ভাবনাটি পরিসংখ্যানগতভাবে বৃদ্ধি পেয়েছে যে ফাংশন বা লুপটি ধারণ করতে একটি কম লাইন প্রয়োজনীয় হবে necessary স্পষ্টতই, এটি ব্যাকফায়ার করে কারণ এই এনওপিগুলি কোড ঘনত্ব এবং তাই ক্যাশে দক্ষতা হ্রাস করে। লুপস এবং লেবেলের ক্ষেত্রে, এনওপিগুলিকে একবারে মৃত্যুদণ্ড কার্যকর করা প্রয়োজন হতে পারে (যখন কার্যকরভাবে লুপ / ​​লেবেলে সাধারণত আসে, লাফ দেওয়ার বিপরীতে)।


মজার বিষয় হ'ল: -O2 -fno-omit-frame-pointerঠিক তেমন ভাল -Os। দয়া করে আপডেট হওয়া প্রশ্নটি পরীক্ষা করুন।
আলী

11

যদি আপনার প্রোগ্রামটি কোডে এল 1 ক্যাশে আবদ্ধ থাকে, তবে আকারের জন্য অনুকূলিতকরণ হঠাৎ করে অর্থ প্রদান শুরু হয়।

সর্বশেষে যখন আমি যাচাই করেছি, সংকলকটি সব ক্ষেত্রে এটিকে বোঝার মতো যথেষ্ট স্মার্ট নয়।

আপনার ক্ষেত্রে, -O3 সম্ভবত দুটি ক্যাশে লাইনের জন্য যথেষ্ট কোড জেনারেট করে, তবে -Os একটি ক্যাশে লাইনে ফিট করে।


1
আপনি এই এলাইন = পরামিতিগুলি ক্যাশে লাইনের আকারের সাথে সম্পর্কিত কতটা বাজি রাখতে চান?
জোশুয়া

আমি সত্যিই আর যত্ন করি না: এটি আমার মেশিনে দৃশ্যমান নয়। এবং -falign-*=16পতাকাগুলি পাস করার মাধ্যমে , সমস্ত কিছু স্বাভাবিক অবস্থায় ফিরে আসে, সবকিছু ধারাবাহিকভাবে আচরণ করে। যতদূর আমি উদ্বিগ্ন, এই প্রশ্নটি সমাধান হয়েছে।
আলী 21

7

আমি কোনও উপায়েই এই অঞ্চলে বিশেষজ্ঞ নই, তবে আমি মনে করি মনে হয় যে আধুনিক প্রসেসরগুলি যখন শাখার পূর্বাভাসের কথা আসে তখন এটি বেশ সংবেদনশীল । শাখার পূর্বাভাস দেওয়ার জন্য ব্যবহৃত অ্যালগরিদমগুলি হ'ল (বা কমপক্ষে আমি এসেম্বলার কোডটি লেখার দিনগুলিতে ফিরে এসেছি) কোডের একাধিক বৈশিষ্ট্যের উপর ভিত্তি করে একটি টার্গেটের দূরত্ব এবং দিকনির্দেশসহ।

যে দৃশ্যটি মাথায় আসে তা ছোট লুপগুলি। যখন শাখাটি পিছনের দিকে চলে যাচ্ছিল এবং দূরত্ব খুব বেশি দূরে ছিল না, তখন সমস্ত ছোট লুপগুলি এভাবে করা হয়ে যাওয়ার কারণে শাখার পূর্বাভাসটি এই কেসটির জন্য অনুকূল ছিল। একই নিয়ম খেলার মধ্যে আসতে পারে যখন আপনি অবস্থান অদলবদল addএবং workকোডটি অথবা যখন উভয় পদে সামান্য পরিবর্তন।

এটি বলেছিল, এটি কীভাবে যাচাই করা যায় তা সম্পর্কে আমার কোনও ধারণা নেই এবং আমি কেবল আপনাকে জানাতে চেয়েছিলাম যে এটি এমন কিছু যা আপনি সন্ধান করতে চান।


ধন্যবাদ। আমি এটি দিয়ে খেলেছি: আমি কেবল অদলবদল করে পাস করি add()এবং work()যদি -O2পাস হয়ে যায়। অন্যান্য সমস্ত ক্ষেত্রে কোড অদলবদল করে উল্লেখযোগ্যভাবে ধীর হয়ে যায়। সপ্তাহের শেষে, আমি শাখার পূর্বাভাস / ভুল-পূর্বাভাসের পরিসংখ্যানগুলিও বিশ্লেষণ perfকরেছি এবং আমি এমন কোনও বিষয় লক্ষ্য করি নি যা এই অদ্ভুত আচরণটি ব্যাখ্যা করতে পারে। একমাত্র সঙ্গতিপূর্ণ ফলাফল হ'ল ধীর ক্ষেত্রে perfকেসটি 100.0 এ রিপোর্ট করে add()এবং লুপটিতে কল করার পরে লাইনে ডানদিকে একটি বড় মান add()। দেখে মনে হচ্ছে আমরা কোনও কারণে add()ধীরগতিতে স্টল করছি তবে দ্রুত রান নয় in
আলী

আমি আমার একটি মেশিনে ইন্টেলের ভিটিউন ইনস্টল করার বিষয়ে ভাবছি এবং নিজেই একটি প্রোফাইলিং করছি। perfকেবলমাত্র সীমিত সংখ্যক জিনিসকেই সমর্থন করে, সম্ভবত ইন্টেলের জিনিসগুলি তাদের নিজস্ব প্রসেসরে আরও কিছুটা কার্যকর।
আলী
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.