このスライドに出てくるSIMD命令を試したくなったので試してみました。
IntelのCPUでSIMD命令を使う場合、SSEという命令セットと、その後継であるAVXという命令セットのどちらかを使うことになると思います。私の手持ちのWindows機に載ってるCPUはCore i7-870とやや古く、SSEしか使えません。
kawa0810 のブログにあるサンプルはAVXを用いたものだったので、コードを改変してSSEでも動くようにしたコードが以下です。Windows 7 + Visual Studio 2013 Expressでビルドできることを確認しています。コードの正当性に自信が持てませんが、とりあえずSSEを使う場合と使わない場合とで結果は一致しています。
#include <stdio.h> #include <stdlib.h> #include <immintrin.h> #include <chrono> #include <iostream> void vec_add(const size_t n, float *z, const float *x, const float *y){ static const size_t single_size = 4; //単精度は4つずつ計算 const size_t end = n / single_size; __m128 *vz = (__m128 *)z; __m128 *vx = (__m128 *)x; __m128 *vy = (__m128 *)y; for (size_t i = 0; i < end; ++i) vz[i] = _mm_add_ps(vx[i], vy[i]); } void print_elapsed_time(std::chrono::time_point<std::chrono::system_clock>& start, std::chrono::time_point<std::chrono::system_clock>& end) { std::cout << "elapsed time = " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " msec." << std::endl; } int main(void){ const size_t n = 1024 * 1024 * 64; float *x, *y, *z1, *z2; //メモリのアライメントを 16byte 境界に揃える x = (float *)_mm_malloc(sizeof(float)* n, 16); y = (float *)_mm_malloc(sizeof(float)* n, 16); z1 = (float *)_mm_malloc(sizeof(float)* n, 16); z2 = (float *)_mm_malloc(sizeof(float)* n, 16); for (size_t i = 0; i<n; ++i) x[i] = i + 0.2; for (size_t i = 0; i<n; ++i) y[i] = i + 1.7; for (size_t i = 0; i<n; ++i) z1[i] = 0.0; for (size_t i = 0; i<n; ++i) z2[i] = 0.0; // SIMDを使って求める auto start = std::chrono::system_clock::now(); vec_add(n, z1, x, y); auto end = std::chrono::system_clock::now(); print_elapsed_time(start, end); // 普通に計算する start = std::chrono::system_clock::now(); for (size_t i = 0; i < n; ++i) { z2[i] = x[i] + y[i]; } end = std::chrono::system_clock::now(); print_elapsed_time(start, end); // 答えが一致しているかどうか調べる for (size_t i = 0; i < n; ++i) { if (z1[i] != z2[i]) { printf("答が一致していません!"); exit(1); } } //_mm_malloc で確保した領域は _mm_free で解放する _mm_free(x); _mm_free(y); _mm_free(z1); _mm_free(z2); return 0; }
Debugモードでビルドし、実行して処理時間を比較しました。1行目がSIMD命令使用、2行目が使用なしです。
elapsed time = 137 msec. elapsed time = 305 msec.
SIMD命令を使うことで処理時間は45%程度になっています。
ただし、Releaseモードでビルドすると、以下のように時間に差が出ませんでした。最適化されてしまってるんでしょうか??
elapsed time = 85 msec. elapsed time = 90 msec.