2018-03-01【LinuxでGCC環境でSIMDを使用】
2617 ワード
SIMDとは
SIMDのフルネームはSingle Instruction Multiple Data(単一命令マルチデータストリーム)です.
SIMD対応のCPUには、512ビットなどの特別な幅のレジスタが含まれている.特別な命令により、これらのレジスタで指定操作を実行することができる.これらの動作は通常、通常、64ビットなどの正常レジスタ上の動作の拡張であり、1つの命令が複数の正常レジスタ、すなわちいわゆるSIMDを同時に動作させたと理解できる.
SIMDの性能
簡単な除算で分かるように、512ビットのレジスタは64ビットのレジスタに比べて8倍の速度で向上しています.
しかし、実際の状況はそれだけではない.SIMDの命令には、正規分布の累積分布関数とその逆関数を計算する命令など、非常に奇妙な命令も含まれている.それらを見たとき、私は心の中で「こんな操作もある!」と叫んだ.これらの特化した指令は特別なシーンで神器である.
SIMDの使い方
まず、LinuxのGCCコンパイラでSIMDコマンドを使用します.この条件では、2つの方法があります.
簡単な除算で分かるように、512ビットのレジスタは64ビットのレジスタに比べて8倍の速度で向上しています.
しかし、実際の状況はそれだけではない.SIMDの命令には、正規分布の累積分布関数とその逆関数を計算する命令など、非常に奇妙な命令も含まれている.それらを見たとき、私は心の中で「こんな操作もある!」と叫んだ.これらの特化した指令は特別なシーンで神器である.
SIMDの使い方
まず、LinuxのGCCコンパイラでSIMDコマンドを使用します.この条件では、2つの方法があります.
組み込みアセンブリは今日のトピックではありません.今日は主にIntrinsicsの使い方を記録します.どの方法を使っても、必ずコレクションしなければならないサイトがあります.Intel Intrinsics Guide
SIMD命令セットの各サブセット:MMX,SSE,SSE 4を与える.2、AVX 2など.同時に、各命令にいくつかのラベルを付けて検索します:Load、Store、Cast、Arithmeticなど.各命令の等価操作とアセンブリ命令も与えられる.
具体的には、C言語におけるSIMDの使用には、以下の3つの態様が含まれる.
コンパイルオプションはGCCの仕様です.i 386 and x 86-64 Optionsは、関連するオプションを含みますが、より広いです.各命令が属する命令セット(例えばSSE 4.2)は、その命令を使用すると、リンクのオプションに関連する項目(例えば
-msse4.2
)を追加する.オプションのネーミングは直接的で、i 386 and x 86-64 Optionsで
-mmmx
を検索するとSIDMオプションが比較的集中している領域にジャンプすることができ、必要なオプションが何であるかを簡単に特定することができます.メモリの位置合わせ
SIMD命令の使用パターンは簡単です.
ここでは、インポート・エクスポートで使用されるメモリが特定の位置合わせ条件を満たす必要があるという問題に関連します.例えば128ビット(16バイト)のSIMDを使用すると、メモリヘッダアドレスは16で割り切れる必要があります.この条件を満たさないと、データのインポート時にセグメントエラーが発生して終了します.
Cでは、
stdlib.h
からのvoid* aligned_alloc(size_t alignment, size_t size)
の関数を使用する特定の整列方式の動的メモリが得られる.使用例 // Filename: main.cpp
#include
#include
#include
using namespace std;
void print(float* data, int n) {
for (int i = 0; i < n; i ++) {
printf("%f ", data[i]);
}
printf("
");
}
int main() {
const int WIDTH = 256;
const float x = 0.2;
int n = WIDTH/8/sizeof(float);
float* w = (float*) aligned_alloc(64, sizeof(float)*n);
float* y = (float*) aligned_alloc(64, sizeof(float)*n);
//
for(int i = 0; i < n; i ++) {
w[i] = rand();
}
print(w, n);
// y_i = w_i * x
__m256 _x = _mm256_set1_ps(0.2);
__m256 _w = _mm256_load_ps(w);
__m256 _y = _mm256_mul_ps(_x, _w);
_mm256_store_ps(y, _y);
print(y, n);
free(y);
free(w);
}
コンパイルされたコマンドは次のとおりです.g++ -o a.out -mavx main.cpp
// Filename: main.cpp
#include
#include
#include
using namespace std;
void print(float* data, int n) {
for (int i = 0; i < n; i ++) {
printf("%f ", data[i]);
}
printf("
");
}
int main() {
const int WIDTH = 256;
const float x = 0.2;
int n = WIDTH/8/sizeof(float);
float* w = (float*) aligned_alloc(64, sizeof(float)*n);
float* y = (float*) aligned_alloc(64, sizeof(float)*n);
//
for(int i = 0; i < n; i ++) {
w[i] = rand();
}
print(w, n);
// y_i = w_i * x
__m256 _x = _mm256_set1_ps(0.2);
__m256 _w = _mm256_load_ps(w);
__m256 _y = _mm256_mul_ps(_x, _w);
_mm256_store_ps(y, _y);
print(y, n);
free(y);
free(w);
}
g++ -o a.out -mavx main.cpp