popcntとSSE4.2を使用して、CPUのアレイの近似平方根をより速く計算するにはどうすればよいですか?アレイの近似平方根近似
入力は、浮動小数点配列に格納された正の整数(0〜約200,000の範囲)です。
出力は浮動小数点数の配列です。
どちらのアレイも、sseの正しいメモリ配置を持っています。
コードのみ1つのXMMレジスタを使用して下には、Linux上で動作し、ありがとうgcc -O3 code.cpp -lrt -msse4.2
でコンパイルすることができます。
#include <iostream>
#include <emmintrin.h>
#include <time.h>
using namespace std;
void print_xmm(__m128 xmm){
float out[4];
_mm_storeu_ps(out,xmm);
int i;
for (i = 0; i < 4; ++i) std::cout << out[i] << " ";
std::cout << std::endl;
}
void print_arr(float* ptr, size_t size){
size_t i;
for(i = 0; i < size; ++i){
cout << ptr[i] << " ";
}
cout << endl;
}
int main(void){
size_t size = 25000 * 4;
// this has to be multiple of 4
size_t repeat = 10000;
// test 10000 cycles of the code
float* ar_in = (float*)aligned_alloc(16, size*sizeof(float));
float* ar_out = (float*)aligned_alloc(16, size*sizeof(float));
//fill test data into the input array
//the data is an array of positive numbers.
size_t i;
for (i = 0; i < size; ++i){
ar_in[i] = (i+1) * (i+1);
}
//prepare for recipical square root.
__m128 xmm0;
size_t size_fix = size*sizeof(float)/sizeof(__m128);
float* ar_in_end = ar_in + size_fix;
float* ar_out_now;
float* ar_in_now;
//timing
struct timespec tp_start, tp_end;
i = repeat;
clock_gettime(CLOCK_MONOTONIC, &tp_start);
//start timing
while(--i){
ar_out_now = ar_out;
for(ar_in_now = ar_in;
ar_in_now != ar_in_end;
ar_in_now += 4, ar_out_now+=4){
//4 = sizeof(__m128)/sizeof(float);
xmm0 = _mm_load_ps(ar_in_now);
//cout << "load xmm: ";
//print_xmm(xmm0);
xmm0 = _mm_rsqrt_ps(xmm0);
//cout << "rsqrt xmm: ";
//print_xmm(xmm0);
_mm_store_ps(ar_out_now,xmm0);
}
}
//end timing
clock_gettime(CLOCK_MONOTONIC, &tp_end);
double timing;
const double nano = 0.000000001;
timing = ((double)(tp_end.tv_sec - tp_start.tv_sec)
+ (tp_end.tv_nsec - tp_start.tv_nsec) * nano)/repeat;
cout << " timing per cycle: " << timing << endl;
/*
cout << "input array: ";
print_arr(ar_in, size);
cout << "output array: ";
print_arr(ar_out,size);
*/
//free mem
free(ar_in);
free(ar_out);
return 0;
}
skylakeのようなものでは、 'rsqrtps'は4サイクルの待ち時間を持ちますが、パイプライン化されているので、毎回新しい' rsqrtps'が発行されます。おそらく、ループを最大4回展開することでスピードアップを得ることができますが、結果はすぐに処理されるのではなく保存されるため、レジスタの名前変更とアウトオブオーダーの実行はおそらく、今ある。 – EOF
このコードの並列版を書くと、誤った共有以外のことについて考える必要がありますか? – rxu
データ競合を避けるようにする必要があります。たとえば、配列を分割する場合は、重複がないことを確認します。同じ結果を同じ場所に2回書くのはOKだと思いますが、配列をアトミックとして宣言しない限り、そうではありません。 – EOF