2013-06-08 3 views
4

A와 B는 벡터 또는 길이 N입니다. 여기서 N은 20에서 200 사이의 값일 수 있습니다. 이 벡터들 사이의 거리의 제곱을 계산하고 싶습니다. 즉 d^2 = || A-B ||^2입니다.SSE2를 사용하여 거리 계산을 벡터화하는 방법

지금까지 내가 가진 : 내 코드를 프로파일 링하고이 (시간의 50 % 이상이 바로이 일을 소요됩니다) 병목 점을 제외 보인다

float* a = ...; 
float* b = ...; 
float d2 = 0; 

for(int k = 0; k < N; ++k) 
{ 
    float d = a[k] - b[k]; 
    d2 += d * d; 
} 

는 잘 작동 할 수 있습니다. Windows 7의 Visual Studio 2012에서 다음 최적화 옵션 인 /O2 /Oi /Ot /Oy-을 사용하고 있습니다. 제 이해는 VS2012가 해당 루프를 자동 벡터화해야한다는 것입니다 (SSE2 사용). 그러나 코드에 #pragma loop(no_vector)을 삽입하면 눈에 띄는 속도가 느려지지 않으므로 루프가 벡터화되지 않습니다. 컴파일러는 확인이이 메시지 :

info C5002: loop not vectorized due to reason '1105' 

내 질문

은 다음과 같습니다

  1. 는 VS2012 그것을 벡터화 할 수 있도록이 코드를 수정할 수 있습니까?
  2. 그렇지 않다면 코드를 직접 벡터화하려고하는 것이 맞습니까?
  3. SSE2 코딩에 대해 배우려면 저에게 웹 사이트를 권장 할 수 있습니까?
  4. 벡터화가 생산성을 떨어 뜨릴 수있는 N 미만의 값이 있습니까?
  5. reason '1105'은 무엇입니까?

답변

4

MSDN documentation에서 1105 오류 코드는 컴파일러가 코드를 벡터화 된 명령어로 줄이는 방법을 파악할 수 없음을 의미합니다. 부동 소수점 연산의 경우 부동 소수점 감소를 사용하려면/fp : fast 옵션을 지정해야한다고 표시됩니다.

+0

+1은/fp : fast 옵션에 해당합니다. 코드가 벡터화됩니다. 감사! 그러나 (/ fp : fast를 사용하는 경우에도) 코드는 실제로/fp : fast가없는 원래 코드보다 두 배 오래 걸립니다. 새로운 []/delete []는 대히트입니다. 힙 할당 된 dist를 스택 할당 된 버퍼로 대체하는 것은 시간의 절반을 줄여서 시작 시간을 다시 시작하게합니다. 데이터를 두 번 반복하는 것도 대히트입니다. 가장 좋은 방법은 원래 코드를 유지하고/fp : fast를 사용하여 2 배속을 향상시키는 것입니다. – Bull

+0

또한 MSDN의 1105 오류 코드 페이지에 감사드립니다. 왜 그 페이지를 찾을 수 없었는지 확실하지 않았습니다. 코드를 제거하면이 대답을 수락합니다./fp : fast를 사용하는 원래 코드는 필자가 선호하는 솔루션입니다. – Bull

+0

@ user2151446 필자의 테스트 케이스에서는 예제의 코드가 벡터화되지 않았으므로 코드가 제거되었으므로 추가 된 복잡성이 있습니다. 나는 내일 그것에 대해 질문 할 것입니다. – masrtis

6

그것은이 사용 SSE의 내장 함수를 구현하기 위해 매우 간단합니다 :

#include "pmmintrin.h" 

__m128 vd2 = _mm_set1_ps(0.0f); 
float d2 = 0.0f; 
int k; 

// process 4 elements per iteration 
for (k = 0; k < N - 3; k += 4) 
{ 
    __m128 va = _mm_loadu_ps(&a[k]); 
    __m128 vb = _mm_loadu_ps(&b[k]); 
    __m128 vd = _mm_sub_ps(va, vb); 
    vd = _mm_mul_ps(vd, vd); 
    vd2 = _mm_add_ps(vd2, vd); 
} 

// horizontal sum of 4 partial dot products 
vd2 = _mm_hadd_ps(vd2, vd2); 
vd2 = _mm_hadd_ps(vd2, vd2); 
_mm_store_ss(&d2, vd2); 

// clean up any remaining elements 
for (; k < N; ++k) 
{ 
    float d = a[k] - b[k]; 
    d2 += d * d; 
} 

당신이 ab 다음 성능을 도움이 될 수 _mm_load_ps보다는 _mm_loadu_ps를 사용할 수 정렬 16 바이트 것을 보장 할 수있는 경우, 특히 오래된 (Nehalem 이전) CPU의 경우.

부하의 수에 비해 산술 명령어가 거의없는이 루프의 경우 성능은 메모리 대역폭에 따라 제한 될 수 있으며 실제로 벡터화로 인해 예상되는 속도가 실제로 실현되지 못할 수도 있습니다.

+1

좋은 답변입니다! 내가 그랬던 것처럼. 그러나 나는 @ user2151446에게 당신이 16 바이트의 정렬로'a'와'b'를 할당했음을 확인하도록 제안 할 것이다. 그런 식으로'_mm_loadu_ps' 대신'_mm_load_ps'를 사용할 수 있습니다. 그러면 코드가 더 최적화됩니다. – oysteijo

+1

감사. 이것은 원래의 코드가'/ fp : fast 옵션 '을 사용하는 것만 큼 빠르지 만 잘 작동합니다. '/ fp : fast'를 지정하면 두 번째 루프에'#pragma loop (no_vector) '를 넣으면 VS가 벡터화하고 첫 번째 루프의 벡터화에서 절반의 이득을 잃지 않게됩니다. – Bull

+0

@oysteijo : 감사합니다. 이제 정렬 된로드에 대한 메모를 추가했습니다. –

관련 문제