コヌド呜什の敎列

このような単玔な関数のパフォヌマンスを枬定するのはどれほど難しいでしょうか



// func.cpp void benchmark_func(int* a) { for (int i = 0; i < 32; ++i) a[i] += 1; }
      
      





さお、それをある皮のマむクロベンチマヌクでラップし、䜕床も結果を平均するために呌び出しお、䜕が起こるか芋おみたしょう。 コンパむラヌがそこで䜕かを「最適化」しおいないこずを確認するためだけに、生成された呜什を芋るこずができたす。 たた、ルヌプがボトルネックであるこずを確認するために、いく぀かの異なるテストを実行できたす。 たあ、それだけです。 私たちは枬定するものを理解しおいたすよね



ファむルに別の関数があるこずを想像しおみたしょう。速床も枬定したすが、個別のテストです。 すなわち ファむルは次のようになりたす。



 // func.cpp void foo(int* a) { for (int i = 0; i < 32; ++i) a[i] += 1; } void benchmark_func(int* a) { for (int i = 0; i < 32; ++i) a[i] += 1; }
      
      





そしおある日、あなたのマネヌゞャヌがあなたのずころに来お、あなたのラむブラリのナヌザヌから、あなたが玄束したほど速く動䜜しないずいう苊情を瀺したす。 ただし、パフォヌマンスをよく枬定し、テスト結果から埗たものを正確に玄束したした。 䜕が悪かったのですか



ナヌザヌは、benchmark_func関数のテストにのみ興味があるため、そのためだけにパフォヌマンステストを実行したず蚀いたす。



フィギュア



次のオプションを䜿甚しお、最新のClangでこのコヌドをコンパむルしたした。



 -O2 -march=skylake -fno-unroll-loops
      
      





このコヌドをIntel Core i7-6700 Skylakeプロセッサヌで実行したした

すべおのコヌドずビルドスクリプトは、 ここからダりンロヌドできたす 。 Googleベンチマヌクラむブラリも必芁になりたす 。



2぀の関数を䜿甚するコヌドのバヌゞョンを「基本」、そしお、benchmark_func関数のみを䜿甚するオプション-「no_foo」を呌び出したしょう。 結果は次のずおりです。



 $ ./baseline.sh --------------------------------------------------------- Benchmark CPU Iterations Throughput Clockticks/iter --------------------------------------------------------- func_bench_median 4 ns 191481954 32.5626GB/s 74.73 $ ./no_foo.sh --------------------------------------------------------- Benchmark CPU Iterations Throughput Clockticks/iter --------------------------------------------------------- func_bench_median 4 ns 173214907 29.5699GB/s 84.54
      
      





「Clockticks / iter」メトリックを自分で蚈算し、benchmark_func関数のティック数を反埩回数で陀算したした。

奇劙なこずに、テストではたったく呌び出されない関数fooを゜ヌスコヌドのあるファむルから削陀するず、残りの関数のパフォヌマンスが10ほど䜎䞋したした。



ここで䜕が起こっおいるのかを理解しおみたしょう。



少し先を芋お、benchmark_func関数甚に生成されたアセンブラコヌドは䞡方の堎合で同䞀であり、唯䞀の違いはバむナリ内の䜍眮ず内郚ルヌプのアラむメントです。



最初に、「ベヌス」バヌゞョン甚に生成されたコヌドを芋おみたしょう。



 $ objdump -d a.out -M intel | grep "<_Z14benchmark_funcPi>:" -A15 00000000004046c0 <_Z14benchmark_funcPi>: 4046c0: 48 c7 c0 80 ff ff ff mov rax,0xffffffffffffff80 4046c7: c5 fd 76 c0 vpcmpeqd ymm0,ymm0,ymm0 4046cb: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0] 4046d0: c5 fe 6f 8c 07 80 00 vmovdqu ymm1,YMMWORD PTR [rdi+rax*1+0x80] 4046d7: 00 00 4046d9: c5 f5 fa c8 vpsubd ymm1,ymm1,ymm0 4046dd: c5 fe 7f 8c 07 80 00 vmovdqu YMMWORD PTR [rdi+rax*1+0x80],ymm1 4046e4: 00 00 4046e6: 48 83 c0 20 add rax,0x20 4046ea: 75 e4 jne 4046d0 <_Z14benchmark_funcPi+0x10> 4046ec: c5 f8 77 vzeroupper 4046ef: c3 ret
      
      





コヌドがキャッシュラむンの境界に配眮されおいるこずがわかりたす0x406c0 mod 0x40 == 0x0。 これはいいです。 しかし、Intelプロセッサアヌキテクチャに぀いおは、ただ知っおおくべきこずがありたす。 Skylakeプロセッサヌには、1回のパスで16バむトの呜什を遞択するマむクロ呜什倉換゚ンゞンであるMITEマむクロ呜什倉換゚ンゞンがありたす。 ここで重芁な点は、16バむトだけでなく、16バむト間隔にアラむンされたりィンドりからの16バむトであるずいうこずです。 これらの呜什が遞択された埌、デコヌダヌはそれらを䞀連の小さなマむクロ操䜜uopに倉換したす。 さらに、これらのマむクロオペレヌションは、実行の次のステヌゞに転送されたす。



しかし、DSBDecoded Stream Bufferず呌ばれる別のハヌドりェアナニットがあり、その名前が瀺すように、これはマむクロオペレヌションキャッシュです。 すでに最近完了した䞀連の呜什を実行する堎合は、DSBでそれに察応するマむクロ操䜜があるかどうかを最初に確認したす。 そこで芋぀かった堎合、これにより、MITEを再ブロヌドキャストするだけでなく、RAMから読み取るこずもできたす䞀般的には優れおいたす。 ただし、マむクロ呜什がDSBに到達するたたは取埗しない方法に圱響する特定の制限がありたす。これに぀いおは以䞋で説明したす。



䞊蚘のアセンブラヌコマンドでは、コヌドがベクトル化されおおり、実際にはルヌプの反埩が4回のみであるこずがわかりたす。これはこの䟋に適しおいたす。それ以倖の堎合は、LSDルヌプストリヌムディテクタヌがルヌプを怜出し、メモリからの呜什のフェッチを停止したす。



Intelアヌキテクチャのこれらすべおのニュアンスに関する詳现は、ドキュメント「Intel 64およびIA-32アヌキテクチャ最適化リファレンスマニュアル」に蚘茉されおいたす。 このトピックに関する優れたZia Ansariのプレれンテヌションもご芧ください。



コヌド呜什の敎列が重芁



埌で議論する内容をすでに掚枬しおいるず思いたす。 どちらの堎合でも、benchmark_func関数がコヌド内でどのように配眮されおいるかを芋おみたしょう。



「基本ケヌス」



画像



「No_foo」



画像



䞊の図の倪い長方圢は32バむトのりィンドりを瀺し、ルヌプ本䜓の指瀺は黄色の背景でマヌクされおいたす。 最初の芳察結果は、2番目の堎合、ルヌプのコヌド党䜓が1぀の32バむトりィンドりに分類され、最初の堎合は2぀のりィンドりに分散されるずいうこずです。 実際、2番目のケヌスでは、DSBにアクセスするずきにミスが半分になりDSB_MISS_PS 1800M察888M、DSB-MITEを切り替えるオヌバヌヘッドがたったくれロDSB2MITE_SWITCHES、PENALTY_CYCLES 888M察0になりたす。 しかし、なぜ、すべおが10悪くなるのでしょうか おそらく、ただ考慮に入れおいない他のアヌキテクチャ機胜がいく぀かありたす。



いく぀かの実隓を行い、デコヌドされた呜什がDSBにどのように配眮されるかに぀いおのさたざたな仮説をテストしたしたが、それでも完党に理解しおいるずは100確信できたせん。 ここに実隓を投皿したした 。



パフォヌマンスカりンタヌに異垞はありたせんでした。 泚意できる唯䞀のこずは、パラメヌタヌの2぀のケヌスの違いです。

IDQ_UOPS_NOT_DELIVERED、CYCLES_0_UOPS_DELIV4100M察5200M。 あなたがそれが䜕であるかわからない堎合-蚘事の終わりを芋お、すべおの䜿甚されたカりンタヌの説明がありたす。



さらに先ぞ



アラむメントを明瀺的に蚭定しお、さらに2぀の実隓を行いたした。-mllvm-align-all-functions = 5および-mllvm -align-all-blocks = 5



 $ ./aligned_functions.sh --------------------------------------------------------- Benchmark CPU Iterations Throughput Clockticks/iter --------------------------------------------------------- func_bench_median 3 ns 218294614 36.8538GB/s 63.37 $ ./aligned_blocks.sh --------------------------------------------------------- Benchmark CPU Iterations Throughput Clockticks/iter --------------------------------------------------------- func_bench_median 3 ns 262104631 44.3106GB/s 46.25
      
      





bench_funcを32バむトの境界で敎列するず、+ 13のパフォヌマンスが埗られ、32バむトの境界で関数bench_funcのすべおのベヌスブロック関数の開始を含むを敎列するず、+ 36の速床向䞊が埗られたした。 おかしいですよね



関数の配眮があるケヌスの関数の䜍眮は、「ベヌス」の堎合ずそれほど倉わりたせん。



画像



぀たり、「ベヌス」の堎合のように、DSBで䜕らかの問題を凊理しおいたす。 DSB_MISS_PS 2600M察1800Mのカりンタヌでは、さらに悪いDSBパフォヌマンスが瀺されたす。 さらに重芁なのは、カりンタヌIDQ_UOPS_NOT_DELIVERED、CYCLES_0_UOPS_DELIVを比范するこずです330M察4100M。 最埌に、私たちにずっお本圓に重芁なのは、バック゚ンドがデコヌドされたマむクロ呜什で満たされるようにするこずです。



ベヌスブロックが敎列しおいる堎合



画像



興味深いのは、DSBの䜿甚率が高いこずず、配信されたマむクロ呜什がなかった察策の数が少ないこずです。 特定のカりンタヌ倀を含む以䞋の衚をご芧ください。



䜿甚枈みパフォヌマンスカりンタヌ



画像



そしお、この衚の列芋出しの説明は次のずおりです。



FRONTEND_RETIRED.DSB_MISS_PS -DSBデコヌドストリヌムバッファヌで怜玢ミスが発生した呜什をカりントしたす



DSB2MITE_SWITCHES.PENALTY_CYCLES -DSBずMITEを切り替える際のペナルティ枬定倀をカりントしたす。 必芁な指瀺がなく、MITEを䜿甚しなければならなかったDSBぞのアピヌルは、最悪の堎合、IDQにマむクロ操䜜が転送されない最倧6クロックサむクルかかる可胜性がありたす。 原則ずしお、これには最倧2぀の手段が必芁です。



IDQ.ALL_DSB_CYCLES_4_UOPS-デコヌドストリヌムバッファヌDSBから呜什デコヌドキュヌIDQに正確に4぀のマむクロ呜什が配信されたメゞャヌの数をカりントしたす



IDQ.ALL_DSB_CYCLES_ANY_UOPS-デコヌドストリヌムバッファヌDSBから呜什デコヌドキュヌIDQにマむクロ呜什が配信されたメゞャヌの数をカりントしたす



IDQ_UOPS_NOT_DELIVERED.CORE-各ストリヌムのリ゜ヌス割り圓おテヌブルRATに配信されないマむクロオペレヌションの数をカりントし、呜什デコヌドキュヌIDQがリ゜ヌス割り圓おテヌブルRATにx個のマむクロオペレヌションを配信するずきに「4」を远加したすxはセット{0 、1,2,3}



IDQ_UOPS_NOT_DELIVERED.CYCLES_0_UOPS_DELIV.CORE-各ストリヌムに぀いお、マむクロオペレヌションがリ゜ヌス割り圓おテヌブルRATに配信されなかったメゞャヌの数をカりントしたす。 IDQ_Uops_Not_Delivered.core = 4。



è­Šå‘Š



この特定のケヌスでは、たずえば、反埩回数を1024に増やすず、これらのアラむメントの問題はすべおなくなりたす。この時点で、ルヌプ怜出噚LSDが機胜したす。 圌は、私たちが埪環しおいるこずを理解し、同じ指瀺を䜕床も繰り返したす。 次に、メモリからの呜什の読み取りを犁止し、内郚バッファから実行を開始したす。 この時点で、呜什がメモリ内でどのように配眮され敎列されるかは完党に無関係になりたす。



別の興味深い䟋ずしお、 ゎヌルドリンカヌを䜿甚したずきにパフォヌマンスがさらに10䜎䞋したこずがありたす。 これは、それが䜕らかの理由で悪いからではなく、コヌドのアラむメントのためです。



垞にコヌドを揃えないのはなぜですか



アラむメントずは、コンパむラヌがコヌドにNOP呜什を挿入するこずを意味したす。 これにより、バむナリのサむズが倧きくなり、これらのNOP呜什が䞀般的に䜿甚されるルヌプに陥るず、パフォヌマンスが䜎䞋する可胜性がありたす。 NOP呜什の実行は完党に無料ではありたせん。メモリから読み取り、デコヌドする必芁がありたす。



結論



ご芧のずおり、このような少量のコヌドでも難しい堎合がありたす。 私たち党員がマむクロプロセッサアヌキテクチャの専門家である必芁はないず思いたすが、少なくずもそのような問題が存圚する可胜性があるこずを知っおおく必芁がありたす。 䞀床枬定された関数のパフォヌマンスは、この関数のコヌドを倉曎しなくおも将来倉曎される可胜性があるこずに泚意しおください。 これが重芁なポむントである堎合-远加のパフォヌマンス枬定を行っお、この蚘事で説明した問題ず同様の問題を特定するこずを忘れないでください。



All Articles