7

次の SIMD コードから Visual Studio 2012 のアセンブリ出力を確認しました。

    float *end = arr + sz;
    float *b = other.arr;
    for (float *a = arr; a < end; a += 4, b += 4)
    {
        __m128 ax = _mm_load_ps(a);
        __m128 bx = _mm_load_ps(b);
        ax = _mm_add_ps(ax, bx);
        _mm_store_ps(a, ax);
    }

ループ本体は次のとおりです。

$LL11@main:
    movaps  xmm1, XMMWORD PTR [eax+ecx]
    addps   xmm1, XMMWORD PTR [ecx]
    add ecx, 16                 ; 00000010H
    movaps  XMMWORD PTR [ecx-16], xmm1
    cmp ecx, edx
    jb  SHORT $LL11@main

ecx次の行に格納するときに 16 を減算するためだけに、16ずつインクリメントするのはなぜですか?

4

3 に答える 3

7

さて、ここには基本的に2つのオプションがあります。

 add ecx, 16
 movaps XMMWORD PTR [ecx-16], xmm1 ; stall for ecx?
 cmp ecx, edx
 jb loop

また

 movaps XMMWORD PTR [ecx], xmm1
 add ecx, 16
 cmp ecx, edx ; stall for ecx?
 jb loop

オプション 1 では、 と の間で失速する可能性がaddありmovapsます。オプション 2 では、 と の間で失速する可能性がaddありcmpます。ただし、使用される実行ユニットの問題もあります。addcmp(= sub) は ALU を使用しますが、[ecx-16]AGU (アドレス生成ユニット) を使用すると思います。したがって、ALU の使用と AGU の使用が交互に行われるため、オプション 1 がわずかに有利になる可能性があると思います。

于 2013-09-11T11:06:40.737 に答える
1

確かにこれは少し奇妙です。

多くのコンパイラは、一部のプロセッサではコードの実行速度が遅くなるため、命令が変更された後にレジスタを読み取ることを避けています。例:

; Code that runs fast:
add ecx, 16
mov esi, edi
cmp ecx, edx

; Code doing the same that may run slower:
mov esi, edi
add ecx, 16
cmp ecx, edx

このため、コンパイラはアセンブラ命令の順序を変更することがよくあります。ただし、あなたの場合、これは間違いなく理由ではありません。

おそらく、コンパイラの最適化コードは 100% 正しく記述されていないため、この種の「最適化」が行われます。

于 2013-09-11T04:50:42.637 に答える