19

誰かが私のためにこれに光を当てることができるかどうか私は興味があります。私はいくつかの数値データ変換に取り組んでおり、データ変換を行ういくつかの関数があります。これらは2つのマクロを使用して定義します。

#define CONV_VIA_CAST(name, dtype, vtype)                               \
    static inline void name(void *data, void *view, size_t len) {       \
        vtype *vptr = (vtype*)view;                                     \
        dtype *dptr = (dtype*)data;                                     \
        for (size_t ii=0; ii < len/sizeof(vtype); ii++) {               \
            *vptr++ = (vtype)*dptr++;                                   \
        }                                                               \
    } 


#define CONV_VIA_FUNC(name, dtype, vtype, via)                          \
    static inline void name(void *data, void *view, size_t len) {       \
        vtype *vptr = (vtype*)view;                                     \
        dtype *dptr = (dtype*)data;                                     \
        for (size_t ii=0; ii < len/sizeof(vtype); ii++) {               \
            *vptr++ = (vtype)via(*dptr++);                              \
        }                                                               \
    } 

floatからintへの変換を定義する場合:

 CONV_VIA_FUNC(f_to_i, float, int16_t, lrintf); 

-O3をオンにしたすてきな簡潔なアセンブルを取得します。

   0x0000000000401fb0 <+0>:     shr    %rdx
   0x0000000000401fb3 <+3>:     je     0x401fd3 <f_to_i+35>
   0x0000000000401fb5 <+5>:     xor    %eax,%eax
   0x0000000000401fb7 <+7>:     nopw   0x0(%rax,%rax,1)
   0x0000000000401fc0 <+16>:    cvtss2si (%rdi,%rax,4),%rcx
   0x0000000000401fc6 <+22>:    mov    %cx,(%rsi,%rax,2)
   0x0000000000401fca <+26>:    add    $0x1,%rax
   0x0000000000401fce <+30>:    cmp    %rdx,%rax
   0x0000000000401fd1 <+33>:    jne    0x401fc0 <f_to_i+16>
   0x0000000000401fd3 <+35>:    repz retq 

ただし、float-> double(またはdouble-> float)関数を定義すると、次のようになります。

CONV_VIA_CAST(f_to_d, float,   double); 

私はこの怪物を手に入れます:

   0x0000000000402040 <+0>:     mov    %rdx,%r8
   0x0000000000402043 <+3>:     shr    $0x3,%r8
   0x0000000000402047 <+7>:     test   %r8,%r8
   0x000000000040204a <+10>:    je     0x402106 <f_to_d+198>
   0x0000000000402050 <+16>:    shr    $0x5,%rdx
   0x0000000000402054 <+20>:    lea    0x0(,%rdx,4),%r9
   0x000000000040205c <+28>:    test   %r9,%r9
   0x000000000040205f <+31>:    je     0x402108 <f_to_d+200>
   0x0000000000402065 <+37>:    lea    (%rdi,%r8,4),%rax
   0x0000000000402069 <+41>:    cmp    $0xb,%r8
   0x000000000040206d <+45>:    lea    (%rsi,%r8,8),%r10
   0x0000000000402071 <+49>:    seta   %cl
   0x0000000000402074 <+52>:    cmp    %rax,%rsi
   0x0000000000402077 <+55>:    seta   %al
   0x000000000040207a <+58>:    cmp    %r10,%rdi
   0x000000000040207d <+61>:    seta   %r10b
   0x0000000000402081 <+65>:    or     %r10d,%eax
   0x0000000000402084 <+68>:    test   %al,%cl
   0x0000000000402086 <+70>:    je     0x402108 <f_to_d+200>
   0x000000000040208c <+76>:    xorps  %xmm3,%xmm3
   0x000000000040208f <+79>:    xor    %eax,%eax
   0x0000000000402091 <+81>:    xor    %ecx,%ecx
   0x0000000000402093 <+83>:    nopl   0x0(%rax,%rax,1)
   0x0000000000402098 <+88>:    movaps %xmm3,%xmm0
   0x000000000040209b <+91>:    add    $0x1,%rcx
   0x000000000040209f <+95>:    movlps (%rdi,%rax,1),%xmm0
   0x00000000004020a3 <+99>:    movhps 0x8(%rdi,%rax,1),%xmm0
   0x00000000004020a8 <+104>:   movhlps %xmm0,%xmm1
   0x00000000004020ab <+107>:   cvtps2pd %xmm0,%xmm2
   0x00000000004020ae <+110>:   cvtps2pd %xmm1,%xmm0
   0x00000000004020b1 <+113>:   movlpd %xmm2,(%rsi,%rax,2)
   0x00000000004020b6 <+118>:   movhpd %xmm2,0x8(%rsi,%rax,2)
   0x00000000004020bc <+124>:   movlpd %xmm0,0x10(%rsi,%rax,2)
   0x00000000004020c2 <+130>:   movhpd %xmm0,0x18(%rsi,%rax,2)
   0x00000000004020c8 <+136>:   add    $0x10,%rax
   0x00000000004020cc <+140>:   cmp    %rcx,%rdx
   0x00000000004020cf <+143>:   ja     0x402098 <f_to_d+88>
   0x00000000004020d1 <+145>:   cmp    %r9,%r8
   0x00000000004020d4 <+148>:   lea    (%rsi,%r9,8),%rsi
   0x00000000004020d8 <+152>:   lea    (%rdi,%r9,4),%rdi
   0x00000000004020dc <+156>:   je     0x40210d <f_to_d+205>
   0x00000000004020de <+158>:   mov    %r9,%rdx
   0x00000000004020e1 <+161>:   mov    %r9,%rax
   0x00000000004020e4 <+164>:   neg    %rdx
   0x00000000004020e7 <+167>:   lea    (%rsi,%rdx,8),%rcx
   0x00000000004020eb <+171>:   lea    (%rdi,%rdx,4),%rdx
   0x00000000004020ef <+175>:   nop
   0x00000000004020f0 <+176>:   movss  (%rdx,%rax,4),%xmm0
   0x00000000004020f5 <+181>:   cvtps2pd %xmm0,%xmm0
   0x00000000004020f8 <+184>:   movsd  %xmm0,(%rcx,%rax,8)
   0x00000000004020fd <+189>:   add    $0x1,%rax
   0x0000000000402101 <+193>:   cmp    %rax,%r8
   0x0000000000402104 <+196>:   ja     0x4020f0 <f_to_d+176>
   0x0000000000402106 <+198>:   repz retq 
   0x0000000000402108 <+200>:   xor    %r9d,%r9d
   0x000000000040210b <+203>:   jmp    0x4020de <f_to_d+158>
   0x000000000040210d <+205>:   nopl   (%rax)
   0x0000000000402110 <+208>:   retq   

フロート->ダブルコンバージョンのために、ここでボンネットの下で何が起こっているのかを誰かが明らかにすることができますか?そして、おそらくそれはより効率的なアセンブリを引き出すためにどのように書かれるのでしょうか?それが重要な場合は、gcc4.6.3を使用しています。

4

2 に答える 2

7

あなたが「怪物」と呼ぶものは、実際には自動的にベクトル化されたコードのように見えます。この種の技術がうまく機能し、汎用コンパイラーで役立つようになる前に、20年ほどの研究がこの種の技術に費やされてきました。

きれいではないかもしれませんが、GCCの実装者は、長い配列の方が高速になると考えています。配列が実際に長くない場合、またはコンパイルされたコードがこのように見えるという考えに耐えられない場合は、その特定の最適化を無効にします。でコンパイルする-O2必要があります(未試行)。

于 2012-08-26T23:36:02.603 に答える
6

ここでは、すぐにわかることがいくつかあります(コードが少し長く、時間が少し遅れており、AT&T構文のファンではありません)。

最初に、2番目のループがベクトル化されました(ただし、ひどいことに、以下を参照してください)。これは本質的にコードの膨張を引き起こします。ベクトルなどよりも短い「テールエンド」を処理する必要があります。

第二に、floatからdoubleへの変換は拡大変換です。これはスカラーには関係ありませんが、ベクトルを使用すると、データを読み取って変換し、書き戻すことはできません。行のどこかで、2倍のバイト数になり、処理する必要があります。と。(したがってmovhlps %xmm0,%xmm1

実際のループは402098hから4020cfhまでしかありません。その下は「テールハンドリング」であり、その上はメインループを完全にスキップするかどうかをテストする怪物であり、私が完全に理解していないことがいくつかあります。それが整列のためであったかどうかを感知しますがtest rdi, 15、そこには何も表示されません。また、整列されていない始まりを取り除く明らかなものもありません。

そして第三に、GCCは足が不自由です。これは珍しいことではありません。xmm3は何らかの形で関与していると思われますが、そうではありません。また、ベクトルをメモリから1つのピースにロードできることを忘れているようです。これも、最初の怪物が実際には関与していなかったためである可能性がありますアラインメントをテストします。これは、アラインメントされていないポインターに対する防御です。いずれにせよ、GCCはここで悪い仕事をしました。

于 2012-08-26T23:35:21.293 に答える