popcnt와 SSE4.2를 사용하는 cpu에서 배열의 근사 역수 제곱근을 더 빨리 계산하는 방법은 무엇입니까?배열의 더 빠른 근사 역수 제곱근
입력 값은 float 배열에 저장된 양의 정수 (0부터 약 200,000 범위)입니다.
출력은 실수 배열입니다.
두 어레이의 sse에 대한 올바른 메모리 정렬이 있습니다.
코드
아래 만 1 개 XMM 레지스터를 사용하는 리눅스에서 실행하고,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가 매회 발행 될 수 있습니다. 루프를 최대 네 번 풀어서 속도를 향상시킬 수는 있지만 결과가 처리되지 않고 바로 저장되기 때문에 레지스터 이름 바꾸기 및 순서가 벗어난 실행은 아마 당신이 당신보다 훨씬 나아지지 않는다는 것을 의미합니다. 그래. – EOF
이 코드의 병렬 버전을 작성하면 허위 공유를 제외한 모든 것에 대해 생각해야합니까? – rxu
데이터 경쟁을 피하십시오. 예를 들어 배열을 분할하는 경우 겹침이 없는지 확인하십시오. 동일한 결과를 동일한 장소에 두 번 쓰는 것이 좋다고 생각할 수도 있지만 배열을 원자 적으로 선언하지 않는 한 그렇지 않습니다. – EOF