SIMD 命令などを Intrinsic で表記して高速な実行を実現する方法について記載します。
1. 4バイト整数(int)の加算
2. ***
3. ***
[概要]
4バイト整数(int)の加算を、標準的な方法、SSE2、AVX2、の3つの方法で作成してみます。
[環境]
[stdafx.h]
#pragma once #include "targetver.h" #include <iostream> #include <stdio.h> #include <tchar.h> // tstring typedef std::basic_string<TCHAR> tstring; // tcout #if defined(UNICODE) || defined(_UNICODE) #define tcout std::wcout #define tcin std::wcin #else #define tcout std::cout #define tcin std::cin #endif
[Intrinsic_add_01.cpp]
#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
const size_t n = 130000000;
static INT32 a[n];
static INT32 b[n];
static INT32 c[n];
DWORD dwStartCount = GetTickCount();
for (int i = 0; i < n; ++i) {
a[i] = b[i] + c[i];
}
tcout << _T("(Execute time [ms]) ") << GetTickCount() - dwStartCount << _T("[ms]") << endl;
/*========*/
/* 後処理 */
/*========*/
{
tstring str;
tcout << _T("HIT [Enter] KEY !! ");
getline(tcin, str);
}
return EXIT_SUCCESS;
}
[stdafx.h]
#pragma once #include "targetver.h" #include <iostream> #include <stdio.h> #include <tchar.h> // tstring typedef std::basic_string<TCHAR> tstring; // tcout #if defined(UNICODE) || defined(_UNICODE) #define tcout std::wcout #define tcin std::wcin #else #define tcout std::cout #define tcin std::cin #endif
[Intrinsic_add_02.cpp]
#include "stdafx.h" #include <stdio.h> #include <Windows.h> #include <string> #include <emmintrin.h> // for SSE2 using namespace std; int _tmain(int argc, _TCHAR* argv[]) { const size_t n = 130000000; __declspec(align(256)) static volatile INT32 a[n]; __declspec(align(256)) static volatile INT32 b[n]; __declspec(align(256)) static volatile INT32 c[n]; // SIMD - _mm_add_epi32 __m128i ma; __m128i mb; __m128i mc; DWORD dwStartCount = GetTickCount(); for (int i = 0; i < n; i+=4) { mb = _mm_load_si128((__m128i*)&b[i]); // load - SSE2 mc = _mm_load_si128((__m128i*)&c[i]); // load - SSE2 ma = _mm_add_epi32(mb, mc); // add - SSE2 _mm_store_si128((__m128i*)a, ma); // store - SSE2 } cout << "(Execute time [ms]) " << GetTickCount() - dwStartCount << "[ms]" << endl; /*========*/ /* 後処理 */ /*========*/ { string str; cout << "HIT [Enter] KEY !! "; getline(cin, str); } return EXIT_SUCCESS; }
#include <stdio.h> #include <Windows.h> #include <tchar.h> #include <iostream> #include <string> #include <immintrin.h> // for AVX2 using namespace std; int _tmain(int argc, _TCHAR* argv[]) { const size_t n = 130000000; __declspec(align(256)) static volatile INT32 a[n]; __declspec(align(256)) static volatile INT32 b[n]; __declspec(align(256)) static volatile INT32 c[n]; // SIMD - _mm_add_epi32 __m256i ma; __m256i mb; __m256i mc; DWORD dwStartCount = GetTickCount(); for (int i = 0; i < n; i+=8) { mb = _mm256_load_si256((__m256i*)&b[i]); // load - AVX mc = _mm256_load_si256((__m256i*)&c[i]); // load - AVX ma = _mm256_add_epi32(mb, mc); // add - AVX2 _mm256_store_si256((__m256i*)a, ma); // store - AVX } cout << "(Execute time [ms]) " << GetTickCount() - dwStartCount << "[ms]" << endl; /*========*/ /* 後処理 */ /*========*/ { string str; cout << "HIT [Enter] KEY !! "; getline(cin, str); } return EXIT_SUCCESS; }
上記の3つについて、私のPC環境での実行時間は以下のような結果となりました。
AVX2 の速度が SSE2
と変わらなかったことが不思議です。継続調査して AVX2 の性能を正しく出せるようにするノウハウを蓄積したいと思います。
方式 | 実行時間 [ms] |
標準的な方法 | 約 1390ms |
SSE2 | 約 890ms |
AVX2 | 約 890ms |
[概要]
[環境]
[プログラムソース "***.cpp"]
|
サンプルプログラム ダウンロード
記載: 2017年09月30日 木下英俊:新規作成