その1の続き

要素の並べ替え(正確にはアンパック)を行う場合はどうでしょうか。 UtVideo では packed フォーマットと planar フォーマットの変換でこの処理が出てきます。

とりあえず簡単な例として以下のようなコードで試します。

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

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

char の場合の模範解答はこんな感じ。

    movdqu  xmm0, [rdx]
    movdqu  xmm1, [r8]
    movdqa  xmm2, xmm0
    puhpcklbw xmm0, xmm1
    punpckhbw xmm2, xmm1
    movdqu  [rcx], xmm0
    movdqu  [rcx+16], xmm2

で、実際にやってみると、

D:\>cl /c /O2 /Qvec-report:2 autovec2.cpp
Microsoft(R) C/C++ Optimizing Compiler Version 19.10.25019 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

autovec2.cpp

--- 分析関数: void __cdecl foo<float>(float * __ptr64 __restrict,float const * __ptr64 __restrict,float const * __ptr64 __restrict,unsigned __int64)
d:\autovec2.cpp(4) : info C5002: ループはベクター化されません。理由: '1300'

--- 分析関数: void __cdecl foo<char>(char * __ptr64 __restrict,char const * __ptr64 __restrict,char const * __ptr64 __restrict,unsigned __int64)
d:\autovec2.cpp(4) : info C5002: ループはベクター化されません。理由: '1300'

--- 分析関数: void __cdecl foo<int>(int * __ptr64 __restrict,int const * __ptr64 __restrict,int const * __ptr64 __restrict,unsigned __int64)
d:\autovec2.cpp(4) : info C5002: ループはベクター化されません。理由: '1300'

--- 分析関数: void __cdecl foo<short>(short * __ptr64 __restrict,short const * __ptr64 __restrict,short const * __ptr64 __restrict,unsigned __int64)
d:\autovec2.cpp(4) : info C5002: ループはベクター化されません。理由: '1300'

えっ

ちなみに理由コードの一覧はここにあって、1300 は「ループ本体に計算がまったくかほとんど含まれていません。」だそうです。確かに計算はしてないけどさ。

さて、最近の Visual C++ には Clang/C2 という「別のコンパイラ」が含まれているので、こっちも試してみましょう。

D:\>"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\ClangC2\14.10.25903\bin\HostX64\clang.exe" --version
clang with Microsoft CodeGen version 3.8.0
Provided as - is without support
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\ClangC2\14.10.25903\bin\HostX64\x64\c2.dll version 19.10.25903.0
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\ClangC2\14.10.25903\bin\HostX64

D:\>"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\ClangC2\14.10.25903\bin\HostX64\clang.exe" -c -O2 -msse4.1 autovec2.cpp
umezawa@selene /cygdrive/d
$ objdump -d -M intel autovec2.o

autovec2.o:     ファイル形式 pe-bigobj-x86-64


セクション .text$mn の逆アセンブル:

0000000000000000 <??$foo@D@@YAXPEIADPEIBD1_K@Z>:
   0:   4d 85 c9                test   r9,r9
   3:   74 29                   je     2e <??$foo@D@@YAXPEIADPEIBD1_K@Z+0x2e>
   5:   66 66 66 0f 1f 84 00    data16 data16 nop WORD PTR [rax+rax*1+0x0]
   c:   00 00 00 00
  10:   0f b6 02                movzx  eax,BYTE PTR [rdx]
  13:   48 8d 52 01             lea    rdx,[rdx+0x1]
  17:   88 01                   mov    BYTE PTR [rcx],al
  19:   48 8d 49 02             lea    rcx,[rcx+0x2]
  1d:   41 0f b6 00             movzx  eax,BYTE PTR [r8]
  21:   4d 8d 40 01             lea    r8,[r8+0x1]
  25:   88 41 ff                mov    BYTE PTR [rcx-0x1],al
  28:   49 83 e9 01             sub    r9,0x1
  2c:   75 e2                   jne    10 <??$foo@D@@YAXPEIADPEIBD1_K@Z+0x10>
  2e:   c3                      ret

ダメじゃねぇか

wandbox 上でいろいろ試した結果、 GCC の場合は少なくともだいぶ前のバージョンである 4.4.7 の時点で -O3 オプション(より正確には -ftree-vectorize オプション。-O2 ではダメ)を付けた時にベクトル化してくれますが、 Clang だと最新版である 4.0.0 でようやくベクトル化してくれるようです。

一番簡単な2要素のケースですらこれでは先が思いやられます。

その3に続く

Trackback

only 1 comment untill now

  1. 自動ベクトル化はどれくらい使い物になるか(その3)

    その2の続き 2要素の場合で既にかなり残念なことになっていますが、3要素の場合はどうでしょうか。 template<typename T> void foo(T* __restrict dst, const T* __restrict a, const T* __restrict b, const T* __r…

Add your comment now