その2の続き

2要素の場合で既にかなり残念なことになっていますが、3要素の場合はどうでしょうか。

template<typename T>
void foo(T* __restrict dst, const T* __restrict a, const T* __restrict b, const T* __restrict c, size_t n)
{
	for (size_t i = 0; i < n; ++i)
	{
		dst[0] = *a++;
		dst[1] = *b++;
		dst[2] = *c++;
		dst += 3;
	}
}

template void foo<char>(char* __restrict, const char* __restrict, const char* __restrict, const char* __restrict, size_t);

3要素の場合は自明な最適解はないと思うのですが、UtVideo では以下のようなコードを書いています。

    lddqu   xmm0, [rsi+rcx]
    lddqu   xmm1, [rdx+rcx]
    lddqu   xmm2, [rbx+rcx]

    palignr xmm1, xmm1, 5
    palignr xmm2, xmm2, 11

    movdqa  xmm5, xmm2
    palignr xmm2, xmm0, 11
    palignr xmm0, xmm1, 11
    palignr xmm1, xmm5, 11

    movdqa  xmm5, xmm2
    palignr xmm2, xmm1, 10
    palignr xmm1, xmm0, 10
    palignr xmm0, xmm5, 10

    pshufb  xmm0, xmm7
    pshufb  xmm1, xmm7
    pshufb  xmm2, xmm7

    movdqu  [rdi   ], xmm0
    movdqu  [rdi+16], xmm1
    movdqu  [rdi+32], xmm2

2要素でダメなものが3要素でできるわけがないので、Visual C++ (の cl.exe)と Clang/C2 はほっといて wandbox 上で GCC と Clang で試してみました。まず Clang は 4.0.0 でも HEAD でもダメで、 GCC だと 5.1.0 以降で以下のようなコードを吐いてくれます。

.L5:
    movdqa  xmm0, xmm12
    add     rbx, 1
    add     r11, 48
    movdqu  xmm3, XMMWORD PTR [rsi+r10]
    movdqu  xmm1, XMMWORD PTR [rdx+r10]
    movdqa  xmm4, xmm3
    movdqu  xmm2, XMMWORD PTR [rcx+r10]
    add     r10, 16
    punpcklbw xmm4, xmm1
    movdqa  xmm15, xmm2
    pshufb  xmm4, xmm14
    pshufb  xmm15, xmm13
    pblendvb xmm4, xmm15, xmm0
    movdqa  xmm0, xmm1
    movdqa  xmm15, xmm2
    movups  XMMWORD PTR [r11-64], xmm4
    movdqa  xmm4, xmm3
    pshufb  xmm0, xmm10
    pshufb  xmm4, xmm11
    pshufb  xmm3, xmm7
    pshufb  xmm1, xmm6
    por     xmm4, xmm0
    movdqa  xmm0, xmm8
    pshufb  xmm15, xmm9
    por     xmm1, xmm3
    pshufb  xmm2, xmm5
    pblendvb xmm4, xmm15, xmm0
    movdqa  xmm0, XMMWORD PTR .LC10[rip]
    pblendvb xmm1, xmm2, xmm0
    movups  XMMWORD PTR [r11-48], xmm4
    movups  XMMWORD PTR [r11-32], xmm1
    cmp     r9, rbx
    ja      .L5

これ要するに、a, b, c のそれぞれから pshufb で要素を必要な位置に持ってきた後、por や pblendvb で合成する、ということのようです。x64 でレジスタが多いので pshufb のインデックスや pblendvb のセレクタはほぼ全部レジスタに乗っていますが、x86 だとレジスタが少ないのでかなり辛いことになりそうです。

命令の数や種類を見る限りでは私が手書きしたものの方が速そうです。やっていることは単純でもそのものズバリの命令がない例ですが、そういうものこそコンパイラが謎の技術で高速なコードを出力してほしい所です。

その4に続くかもしれない

Trackback

no comment untill now

Add your comment now