webdevqa.jp.net

32バイト境界整列ルーチンがuopsキャッシュに適合しない

KbL i7-8550U

私はuops-cacheの動作を調査していて、それに関する誤解に遭遇しました。

インテル最適化マニュアル2.5.2.2(鉱山)で指定されているとおり:

デコードされたICacheは32セットで構成されています。各セットには8つのウェイが含まれています。 各ウェイは最大6つのマイクロオペレーションを保持できます。

-

Way内のすべてのマイクロオペレーションは、コード内で静的に隣接し、同じアラインされた32バイト領域内にEIPを持つ命令を表します。

-

最大3つのウェイを同じ32バイト境界整列チャンクに割り当てることができるため、元のIAプログラムの32バイト領域ごとに合計18のマイクロオペレーションをキャッシュできます。

-

無条件ブランチは、ある意味で最後のマイクロオペレーションです。


ケース1:

次のルーチンを検討してください。

uop.h

void inhibit_uops_cache(size_t);

uop.S

align 32
inhibit_uops_cache:
    mov edx, esi
    mov edx, esi
    mov edx, esi
    mov edx, esi
    mov edx, esi
    mov edx, esi
    jmp decrement_jmp_tgt
decrement_jmp_tgt:
    dec rdi
    ja inhibit_uops_cache ;ja is intentional to avoid Macro-fusion
    ret

ルーチンのコードが実際に32バイトにアラインされていることを確認するには、ここにasm

0x555555554820 <inhibit_uops_cache>     mov    edx,esi
0x555555554822 <inhibit_uops_cache+2>   mov    edx,esi
0x555555554824 <inhibit_uops_cache+4>   mov    edx,esi
0x555555554826 <inhibit_uops_cache+6>   mov    edx,esi
0x555555554828 <inhibit_uops_cache+8>   mov    edx,esi
0x55555555482a <inhibit_uops_cache+10>  mov    edx,esi
0x55555555482c <inhibit_uops_cache+12>  jmp    0x55555555482e <decrement_jmp_tgt>
0x55555555482e <decrement_jmp_tgt>      dec    rdi
0x555555554831 <decrement_jmp_tgt+3>    ja     0x555555554820 <inhibit_uops_cache>
0x555555554833 <decrement_jmp_tgt+5>    ret
0x555555554834 <decrement_jmp_tgt+6>    nop
0x555555554835 <decrement_jmp_tgt+7>    nop
0x555555554836 <decrement_jmp_tgt+8>    nop
0x555555554837 <decrement_jmp_tgt+9>    nop
0x555555554838 <decrement_jmp_tgt+10>   nop
0x555555554839 <decrement_jmp_tgt+11>   nop
0x55555555483a <decrement_jmp_tgt+12>   nop
0x55555555483b <decrement_jmp_tgt+13>   nop
0x55555555483c <decrement_jmp_tgt+14>   nop
0x55555555483d <decrement_jmp_tgt+15>   nop
0x55555555483e <decrement_jmp_tgt+16>   nop
0x55555555483f <decrement_jmp_tgt+17>   nop             

走っている

int main(void){
    inhibit_uops_cache(4096 * 4096 * 128L);
}

私はカウンターを手に入れました

 Performance counter stats for './bin':

     6 431 201 748      idq.dsb_cycles                                                (56,91%)
    19 175 741 518      idq.dsb_uops                                                  (57,13%)
         7 866 687      idq.mite_uops                                                 (57,36%)
         3 954 421      idq.ms_uops                                                   (57,46%)
           560 459      dsb2mite_switches.penalty_cycles                                     (57,28%)
           884 486      frontend_retired.dsb_miss                                     (57,05%)
     6 782 598 787      cycles                                                        (56,82%)

       1,749000366 seconds time elapsed

       1,748985000 seconds user
       0,000000000 seconds sys

これはまさに私が期待していたものです。

Uopsの大部分はuopsキャッシュからのものです。また、uops数は私の期待と完全に一致しています

mov edx, esi - 1 uop;
jmp imm      - 1 uop; near 
dec rdi      - 1 uop;
ja           - 1 uop; near

4096 * 4096 * 128 * 9 = 19 327 352 832カウンターとほぼ同じ19 326 755 442 + 3 836 395 + 1 642 975


ケース2:

コメントアウトされた1つの命令が異なるinhibit_uops_cacheの実装を検討してください。

align 32
inhibit_uops_cache:
    mov edx, esi
    mov edx, esi
    mov edx, esi
    mov edx, esi
    mov edx, esi
    ; mov edx, esi
    jmp decrement_jmp_tgt
decrement_jmp_tgt:
    dec rdi
    ja inhibit_uops_cache ;ja is intentional to avoid Macro-fusion
    ret

ディス:

0x555555554820 <inhibit_uops_cache>     mov    edx,esi
0x555555554822 <inhibit_uops_cache+2>   mov    edx,esi
0x555555554824 <inhibit_uops_cache+4>   mov    edx,esi
0x555555554826 <inhibit_uops_cache+6>   mov    edx,esi
0x555555554828 <inhibit_uops_cache+8>   mov    edx,esi
0x55555555482a <inhibit_uops_cache+10>  jmp    0x55555555482c <decrement_jmp_tgt>
0x55555555482c <decrement_jmp_tgt>      dec    rdi
0x55555555482f <decrement_jmp_tgt+3>    ja     0x555555554820 <inhibit_uops_cache>
0x555555554831 <decrement_jmp_tgt+5>    ret
0x555555554832 <decrement_jmp_tgt+6>    nop
0x555555554833 <decrement_jmp_tgt+7>    nop
0x555555554834 <decrement_jmp_tgt+8>    nop
0x555555554835 <decrement_jmp_tgt+9>    nop
0x555555554836 <decrement_jmp_tgt+10>   nop
0x555555554837 <decrement_jmp_tgt+11>   nop
0x555555554838 <decrement_jmp_tgt+12>   nop
0x555555554839 <decrement_jmp_tgt+13>   nop
0x55555555483a <decrement_jmp_tgt+14>   nop
0x55555555483b <decrement_jmp_tgt+15>   nop
0x55555555483c <decrement_jmp_tgt+16>   nop
0x55555555483d <decrement_jmp_tgt+17>   nop
0x55555555483e <decrement_jmp_tgt+18>   nop
0x55555555483f <decrement_jmp_tgt+19>   nop                      

走っている

int main(void){
    inhibit_uops_cache(4096 * 4096 * 128L);
}

私はカウンターを手に入れました

 Performance counter stats for './bin':

     2 464 970 970      idq.dsb_cycles                                                (56,93%)
     6 197 024 207      idq.dsb_uops                                                  (57,01%)
    10 845 763 859      idq.mite_uops                                                 (57,19%)
         3 022 089      idq.ms_uops                                                   (57,38%)
           321 614      dsb2mite_switches.penalty_cycles                                     (57,35%)
     1 733 465 236      frontend_retired.dsb_miss                                     (57,16%)
     8 405 643 642      cycles                                                        (56,97%)

       2,117538141 seconds time elapsed

       2,117511000 seconds user
       0,000000000 seconds sys

カウンターは完全に予期されていません。

ルーチンはuopsキャッシュの要件に一致するため、すべてのuopsは以前と同じようにdsbから取得されると予想していました。

対照的に、uopsのほぼ70%はレガシーデコードパイプラインからのものです。

質問:ケース2の何が問題になっていますか?何が起こっているのかを理解するために、どのカウンターを見てください。


PD: @PeterCordesのアイデアに従って、無条件分岐ターゲットdecrement_jmp_tgtの32バイトのアライメントをチェックしました。結果は次のとおりです。

ケース3:

次のように、条件付きのjumpターゲットを32バイトに揃えます

align 32
inhibit_uops_cache:
    mov edx, esi
    mov edx, esi
    mov edx, esi
    mov edx, esi
    mov edx, esi
    ; mov edx, esi
    jmp decrement_jmp_tgt
align 32 ; align 16 does not change anything
decrement_jmp_tgt:
    dec rdi
    ja inhibit_uops_cache
    ret

ディス:

0x555555554820 <inhibit_uops_cache>     mov    edx,esi
0x555555554822 <inhibit_uops_cache+2>   mov    edx,esi
0x555555554824 <inhibit_uops_cache+4>   mov    edx,esi
0x555555554826 <inhibit_uops_cache+6>   mov    edx,esi
0x555555554828 <inhibit_uops_cache+8>   mov    edx,esi
0x55555555482a <inhibit_uops_cache+10>  jmp    0x555555554840 <decrement_jmp_tgt>
#nops to meet the alignment
0x555555554840 <decrement_jmp_tgt>      dec    rdi
0x555555554843 <decrement_jmp_tgt+3>    ja     0x555555554820 <inhibit_uops_cache>
0x555555554845 <decrement_jmp_tgt+5>    ret                                              

として実行

int main(void){
    inhibit_uops_cache(4096 * 4096 * 128L);
}

私は次のカウンターを得ました

 Performance counter stats for './bin':

     4 296 298 295      idq.dsb_cycles                                                (57,19%)
    17 145 751 147      idq.dsb_uops                                                  (57,32%)
        45 834 799      idq.mite_uops                                                 (57,32%)
         1 896 769      idq.ms_uops                                                   (57,32%)
           136 865      dsb2mite_switches.penalty_cycles                                     (57,04%)
           161 314      frontend_retired.dsb_miss                                     (56,90%)
     4 319 137 397      cycles                                                        (56,91%)

       1,096792233 seconds time elapsed

       1,096759000 seconds user
       0,000000000 seconds sys

結果は完全に予想されます。uopsの99%以上はdsbからのものです。

平均dsb uops配信率= 17 145 751 147 / 4 296 298 295 = 3.99

これはピーク帯域幅に近いです。

7
St.Antario

OBSERVATION 1:同じ32バイト領域内にターゲットがあり、分岐すると予測される分岐は、uopsキャッシュの観点からの無条件分岐のように動作します(つまり、それは行の最後のuopでなければなりません)。

次のinhibit_uops_cacheの実装を検討してください。

align 32
inhibit_uops_cache:
    xor eax, eax
    jmp t1 ;jz, jp, jbe, jge, jle, jnb, jnc, jng, jnl, jno, jns, jae
t1:
    jmp t2 ;jz, jp, jbe, jge, jle, jnb, jnc, jng, jnl, jno, jns, jae
t2:
    jmp t3 ;jz, jp, jbe, jge, jle, jnb, jnc, jng, jnl, jno, jns, jae
t3:
    dec rdi
    ja inhibit_uops_cache
    ret

コードは、コメントに記載されているすべてのブランチに対してテストされます。違いは非常に重要ではないことが判明したので、そのうちの2つだけを提供します。

jmp:

 Performance counter stats for './bin':

     4 748 772 552      idq.dsb_cycles                                                (57,13%)
     7 499 524 594      idq.dsb_uops                                                  (57,18%)
     5 397 128 360      idq.mite_uops                                                 (57,18%)
         8 696 719      idq.ms_uops                                                   (57,18%)
     6 247 749 210      dsb2mite_switches.penalty_cycles                                     (57,14%)
     3 841 902 993      frontend_retired.dsb_miss                                     (57,10%)
    21 508 686 982      cycles                                                        (57,10%)

       5,464493212 seconds time elapsed

       5,464369000 seconds user
       0,000000000 seconds sys

jge:

 Performance counter stats for './bin':

     4 745 825 810      idq.dsb_cycles                                                (57,13%)
     7 494 052 019      idq.dsb_uops                                                  (57,13%)
     5 399 327 121      idq.mite_uops                                                 (57,13%)
         9 308 081      idq.ms_uops                                                   (57,13%)
     6 243 915 955      dsb2mite_switches.penalty_cycles                                     (57,16%)
     3 842 842 590      frontend_retired.dsb_miss                                     (57,16%)
    21 507 525 469      cycles                                                        (57,16%)

       5,486589670 seconds time elapsed

       5,486481000 seconds user
       0,000000000 seconds sys

IDKが、dsb uopsの数が7 494 052 019である理由、これは4096 * 4096 * 128 * 4 = 8 589 934 592よりも大幅に少ない。

Jmpのいずれかを、実行されないと予測される分岐に置き換えると、結果が大きく異なります。例えば:

align 32
inhibit_uops_cache:
    xor eax, eax
    jnz t1 ; perfectly predicted to not be taken
t1:
    jae t2
t2:
    jae t3
t3:
    dec rdi
    ja inhibit_uops_cache
    ret

結果は次のカウンターになります:

 Performance counter stats for './bin':

     5 420 107 670      idq.dsb_cycles                                                (56,96%)
    10 551 728 155      idq.dsb_uops                                                  (57,02%)
     2 326 542 570      idq.mite_uops                                                 (57,16%)
         6 209 728      idq.ms_uops                                                   (57,29%)
       787 866 654      dsb2mite_switches.penalty_cycles                                     (57,33%)
     1 031 630 646      frontend_retired.dsb_miss                                     (57,19%)
    11 381 874 966      cycles                                                        (57,05%)

       2,927769205 seconds time elapsed

       2,927683000 seconds user
       0,000000000 seconds sys

CASE 1に類似した別の例を考えます:

align 32
inhibit_uops_cache:
    nop
    nop
    nop
    nop
    nop
    xor eax, eax
    jmp t1
t1:
    dec rdi
    ja inhibit_uops_cache
    ret

結果は

 Performance counter stats for './bin':

     6 331 388 209      idq.dsb_cycles                                                (57,05%)
    19 052 030 183      idq.dsb_uops                                                  (57,05%)
       343 629 667      idq.mite_uops                                                 (57,05%)
         2 804 560      idq.ms_uops                                                   (57,13%)
           367 020      dsb2mite_switches.penalty_cycles                                     (57,27%)
        55 220 850      frontend_retired.dsb_miss                                     (57,27%)
     7 063 498 379      cycles                                                        (57,19%)

       1,788124756 seconds time elapsed

       1,788101000 seconds user
       0,000000000 seconds sys

jz:

 Performance counter stats for './bin':

     6 347 433 290      idq.dsb_cycles                                                (57,07%)
    18 959 366 600      idq.dsb_uops                                                  (57,07%)
       389 514 665      idq.mite_uops                                                 (57,07%)
         3 202 379      idq.ms_uops                                                   (57,12%)
           423 720      dsb2mite_switches.penalty_cycles                                     (57,24%)
        69 486 934      frontend_retired.dsb_miss                                     (57,24%)
     7 063 060 791      cycles                                                        (57,19%)

       1,789012978 seconds time elapsed

       1,788985000 seconds user
       0,000000000 seconds sys

jno:

 Performance counter stats for './bin':

     6 417 056 199      idq.dsb_cycles                                                (57,02%)
    19 113 550 928      idq.dsb_uops                                                  (57,02%)
       329 353 039      idq.mite_uops                                                 (57,02%)
         4 383 952      idq.ms_uops                                                   (57,13%)
           414 037      dsb2mite_switches.penalty_cycles                                     (57,30%)
        79 592 371      frontend_retired.dsb_miss                                     (57,30%)
     7 044 945 047      cycles                                                        (57,20%)

       1,787111485 seconds time elapsed

       1,787049000 seconds user
       0,000000000 seconds sys

これらすべての実験から、観察はuopsキャッシュの実際の動作に対応していると思いました。また、別の実験を実行し、カウンターbr_inst_retired.near_takenbr_inst_retired.not_takenで判断した結果は、観測と相関しています。


次のinhibit_uops_cacheの実装を検討してください。

align 32
inhibit_uops_cache:
t0:
    ;nops 0-9
    jmp t1
t1:
    ;nop 0-6
    dec rdi
    ja t0
    ret

dsb2mite_switches.penalty_cyclesfrontend_retired.dsb_missの収集:

enter image description here

プロットのX軸はnopsの数を表します。たとえば、 24は、t1ラベルの後の2 nops、t0ラベルの後の4 nopsを意味します

align 32
inhibit_uops_cache:
t0:
    nop
    nop
    nop
    nop
    jmp t1
t1:
    nop
    nop
    dec rdi
    ja t0
    ret

私が来た陰謀から判断すると

OBSERVATION 2:32バイト領域内に2つの分岐があり、分岐すると予測されている場合、dsb2miteの間に観測可能な相関関係はありません。スイッチとdsbミス。したがって、dsbミスはdsb2miteスイッチとは無関係に発生する可能性があります。


frontend_retired.dsb_missレートの増加は、idq.mite_uopsレートの増加および減少idq.dsb_uopsとよく相関します。これは、次のプロットで確認できます。

enter image description here

OBSERVATION 3:何らかの(不明確?)理由でdsbミスが発生すると、IDQ読み取りバブルが発生し、RATアンダーフローが発生します。


結論:すべての測定値を考慮に入れると、Intel Optimization Manual, 2.5.2.2 Decoded ICacheで定義された動作間に明らかにいくつかの違いがあります

1
St.Antario