一次元配列同士の加算する。呼び出し側をC++で、加算処理をAVX512アセンブリ言語で記述した例を示す。
まず、単純な例を示す。
単純な例、int、zmmレジスター一つ分の例
呼び出し側
呼び出し側のC++コードを示す。common.hをincludeしているが、これはprintDataなど、既に説明済みの共通関数のヘッダである。
#include <immintrin.h> #include "../../common.h" extern "C" void asmCode(const int *a, const int *b, int *c); //main int main(void) { __m512i a = _mm512_set_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); __m512i b = _mm512_set_epi32(11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26); __m512i c; asmCode((int*)&a, (int*)&b, (int*)&c); printData((int*)&a); printData((int*)&b); printData((int*)&c); return 0; }
呼び出され側
アセンブラーのコードを示す。このアセンブリ言語で記述したものは、C++イントリンシックの「__m512i c = _mm512_add_epi32(a, b);」に対応する。
_TEXT segment public asmCode align 16 ;------------------------------------------------------------------- ; rcx: a, rdx: b, r8: c ; ; a: aligned ; b: aligned ; c: aligned ;------------------------------------------------------------------- asmCode proc vmovdqa32 zmm0, dword ptr [rcx] ; load a to zmm0 vpaddd zmm1, zmm0, zmmword ptr [rdx] ; zmm1 = a * b vmovdqa32 zmmword ptr [r8], zmm1 ; store zmm1 to c ret asmCode endp _TEXT ends end
実行結果
C:\>ml64 /c addAsm.asm
C:\>cl /O2 /EHsc /arch:AVX512 add.cpp addAsm.obj
C:\>add
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42
長いint配列同士の加算、unaligned
alignmentの揃っていない、32ビット整数の一次元配列同士の加算をAVX-512で処理する。
ここで示す例は、arques.hatenablog.comの「AVX-512 intrinsicで記述、intの例。」をアセンブリ言語で書き直したものである。
呼び出し側
呼び出し側のC++コードを示す。
#include <immintrin.h> #include "../../common.h" extern "C" void asmCode(const int* a, const int* b, int* c, size_t length); // main int main(void) { const int ArrLen = 256; const int AlignSize = 64; int* a = new int[ArrLen]; int* b = new int[ArrLen]; int* c = new int[ArrLen]; int* r = new int[ArrLen]; initData(ArrLen, a, b); asmCode(a, b, c, ArrLen); // by C++ for (size_t i = 0; i < ArrLen; i++) { r[i] = a[i] + b[i]; } verifyVVec(ArrLen, c, r); delete[] a, b, c, r; return 0; }
呼び出され側
アセンブラーのコードを示す。
_TEXT segment public asmCode align 16 ;------------------------------------------------------------------- ; rcx = &a, unaligned ; rdx = &b, unaligned ; r8 = &c, unaligned ; r9 = ArrLen ; ;--- ;const int units = sizeof(__m512i) / sizeof(int); ;for (size_t i = 0; i < ArrLen / units; i++) ;{ ; __m512i ia = _mm512_loadu_epi32(&a[i * units]); ; __m512i ib = _mm512_loadu_epi32(&b[i * units]); ; __m512i y = _mm512_add_epi32(ia, ib); ; _mm512_storeu_epi32(&c[i * units], y); ;} ; ;--- ; asmCode(a, b, c, ArrLen); ; ;------------------------------------------------------------------- asmCode proc mov r11, rcx ; r11 = &a mov rcx, r9 ; rcx = ArrLen sar rcx, 4 ; rcx /= 16; (16:sizeof(__mm512i)/sizeof(int)) xor r9, r9 ; init. offset loop_f: vmovdqu32 zmm0, zmmword ptr [r11+r9] ; load a to zmm0 vmovdqu32 zmm1, zmmword ptr [rdx+r9] ; load b to zmm1 vpaddd zmm1, zmm0, zmm1 ; zmm1 = a + b vmovdqu32 zmmword ptr [r8+r9], zmm1 ; store zmm1 to c add r9, 64 ; next offset loop loop_f ret asmCode endp _TEXT ends end
実行結果
C:\>ml64 /c vAdd512iAsm.asm
C:\>cl /O2 /EHsc /arch:AVX512 vAdd512i.cpp vAdd512iAsm.obj
C:\>vAdd512i
長いint配列同士の加算、aligned
alignmentされている 32 ビット整数の一次元配列同士の加算をAVX-512で処理する。
ここで示す例は、arques.hatenablog.comの「AVX-512 intrinsicで記述、aligned intの例。」をアセンブリ言語で書き直したものである。
呼び出し側
呼び出し側のC++コードを示す。
#include <immintrin.h> #include "../../common.h" extern "C" void asmCode(const int* a, const int* b, int* c, size_t length); // main int main(void) { const int ArrLen = 256; const int AlignSize = 64; int* a = (int*)_mm_malloc(sizeof(int) * ArrLen, AlignSize); int* b = (int*)_mm_malloc(sizeof(int) * ArrLen, AlignSize); int* c = (int*)_mm_malloc(sizeof(int) * ArrLen, AlignSize); int* r = new int[ArrLen]; initData(ArrLen, a, b); asmCode(a, b, c, ArrLen); // by C++ for (size_t i = 0; i < ArrLen; i++) { r[i] = a[i] + b[i]; } verifyVVec(ArrLen, c, r); _mm_free(a); _mm_free(b); _mm_free(c); delete[] r; return 0; }
呼び出され側
アセンブラーのコードを示す。
_TEXT segment public asmCode align 16 ;------------------------------------------------------------------- ; rcx = &a, aligned ; rdx = &b, aligned ; r8 = &c, unaligned ; r9 = ArrLen ; ;--- ;const int units = sizeof(__m512i) / sizeof(int); ;__m512i* pa = (__m512i*)a; ;__m512i* pb = (__m512i*)b; ;for (size_t i = 0; i < ArrLen / units; i++, pa++, pb++) ;{ ; __m512i y = _mm512_add_epi32(*pa, *pb); ; _mm512_store_epi32(&c[i * units], y); ;} ; ;--- ; asmCode(a, b, c, ArrLen); ; ;------------------------------------------------------------------- asmCode proc mov r11, rcx ; r11 = &a mov rcx, r9 ; rcx = ArrLen sar rcx, 4 ; rcx /= 16; ; (16:sizeof(__mm512i)/sizeof(int)) xor r9, r9 ; init. offset loop_f: vmovdqa32 zmm0, zmmword ptr [r11+r9] ; load a to zmm0 vpaddd zmm1, zmm0, zmmword ptr [rdx+r9]; zmm1 = a + b vmovdqu32 zmmword ptr [r8+r9], zmm1 ; store zmm1 to c add r9, 64 ; next offset loop loop_f ret asmCode endp _TEXT ends end
実行結果
C:\>ml64 /c vAlignAdd512iAsm.asm
C:\>cl /O2 /EHsc /arch:AVX512 vAlignAdd512i.cpp vAlignAdd512iAsm.obj
C:\>vAlignAdd512i
共通関数
#include <iostream> #include <iomanip> #include <random> using namespace std; // print template <typename T> void printData(T a[]) { cout.setf(ios::right); for (int i = (sizeof(__m512i) / sizeof(float)) - 1; i >= 0; i--) cout << fixed << setprecision(0) << setw(3) << a[i] << ","; cout << "\b \b" << endl; } // verify template <typename T> void verifyVVec(const size_t length, const T* c, T* r) { cout.setf(ios::right); for (size_t i = 0; i < length; i++) { if (c[i] != r[i]) { cout << fixed << setprecision(2) << "c[" << i << "] =" << setw(10) << c[i] << ", " << "r[" << i << "] =" << setw(10) << r[i] << endl; } } } // random int inline genRandom(int low, int high) { random_device rd; default_random_engine eng(rd()); uniform_int_distribution<int> distr(low, high); return distr(eng); } // init template <typename T> void initData(const size_t length, T* a, T* b) { for (size_t i = 0; i < length; i++) { a[i] = (T)genRandom(-100, 100); b[i] = (T)genRandom(-100, 100); } } // init template <typename T> void initData(const size_t length, T* a) { for (size_t i = 0; i < length; i++) { a[i] = (T)genRandom(-100, 100); } }