一次元配列同士の演算(float)をAVX512アセンブリ言語で記述します。呼び出し側をC++で、演算処理をAVX512アセンブリ言語で記述します。
一次元配列同士の演算(float)
呼び出し側
呼び出し側のC++コードを示します。common.hをincludeしていますがSIMDで記述したアセンブリ言語の関数と等価な機能を持つC++言語で記述した共通関数のヘッダです。最下部にソースコードを示します。
addは加算、subは減算、mulは乗算、divは除算を行います。
#include "..\common.h" // TEMPLATES // asmbler関数名: 処理+型 #define T float extern "C" { void aaddps(const T*, const T*, T*, const size_t); void asubps(const T*, const T*, T*, const size_t); void amulps(const T*, const T*, T*, const size_t); void adivps(const T*, const T*, T*, const size_t); } typedef void (*Dfunc)(const T*, const T*, T*, const size_t); extern "C" Dfunc afunc[] = { aaddps, asubps, amulps, adivps }; Dfunc cfunc[] = { cadd, csub, cmul, cdiv }; // main int main(void) { const size_t ArrLen = 4096; static_assert(ArrLen % 16 == 0, "number of elements must be an integral multiple of 16."); T a[ArrLen], b[ArrLen], c[ArrLen], v[ArrLen]; init(a, b, ArrLen); for (int i = 0; i < sizeof(cfunc) / sizeof(*cfunc);i++) { cout << "---[" << i << "]--- "; cfunc[i](a, b, c, ArrLen); afunc[i](a, b, v, ArrLen); verify(c, v, ArrLen); } return 0; }
呼び出され側
アセンブラーのコードを示します。演算ごとに関数を記述するのは面倒なのでマクロを使って記述します。
mymacro macro MNAME, MINST public MNAME align 16 MNAME proc mov r10, rcx ; r10 = a[] ; rdx = b[] ; r8 = c[] mov rcx, r9 ; rcx = length sar rcx, 4 ; length/=16; (16float=64bytes=512bit) xor r9, r9 ; clear index loop_f: vmovups zmm0, zmmword ptr [r10+r9] ; load a[] MINST zmm0, zmm0, zmmword ptr [rdx+r9]; c[] = a[] <op> b[] vmovups zmmword ptr [r8+r9], zmm0 ; stote c[] lea r9, qword ptr[r9+64] ; next offset loop loop_f ret MNAME endp endm ;------------------------------------------------------------------- ; code _TEXT segment mymacro aaddps, vaddps mymacro asubps, vsubps mymacro amulps, vmulps mymacro adivps, vdivps _TEXT ends end
実行結果
C:\>ml64 /c arithPSAsm.asm
C:\>cl /O2 /EHsc arithPS.cpp arithPSAsm.obj
C:\>arithPS
---[0]--- Ok!
---[1]--- Ok!
---[2]--- Ok!
---[3]--- Ok!
common.h、共通関数
#include <iostream> using namespace std; // initialize template <typename T> void init(T* a, T* b, const size_t length) { for (size_t i = 0; i < length; i++) { a[i] = b[i] = (T)(rand() - (RAND_MAX / 2)); } } // verify template <typename T> void verify(const T* a, const T*b, const size_t length) { bool errorFlag = false; for (size_t i = 0; i < length; i++) { if (a[i] != b[i]) { cout << "Error, " << "i = " << i << ", a = " << a[i] << ", b = " << b[i] << endl; errorFlag = true; break; } } if(errorFlag == false) cout << "Ok!" << endl; } // add template <typename T> void cadd(const T* a, const T* b, T* c, const size_t length) { for (size_t i = 0; i < length; i++) { c[i] = a[i] + b[i]; } } // sub template <typename T> void csub(const T* a, const T* b, T* c, const size_t length) { for (size_t i = 0; i < length; i++) { c[i] = a[i] - b[i]; } } // mul template <typename T> void cmul(const T* a, const T* b, T* c, const size_t length) { for (size_t i = 0; i < length; i++) { c[i] = a[i] * b[i]; } } // div template <typename T> void cdiv(const T* a, const T* b, T* c, const size_t length) { for (size_t i = 0; i < length; i++) { c[i] = a[i] / b[i]; } }