2013-03-15 4 views
4

OpenMP 및 SIMD를 사용하여 배열 축소를 수행하려고합니다. 나는 다음과 링크에서이 아이디어를 가지고SSE/AVX가있는 OpenMP에서의 감소

inline float sum_scalar_openmp2(const float a[], const size_t N) { 
    float sum = 0.0f; 
    #pragma omp parallel 
    { 
     float sum_private = 0.0f; 
     #pragma omp parallel for nowait 
     for(int i=0; i<N; i++) { 
      sum_private += a[i]; 
     } 
     #pragma omp atomic 
     sum += sum_private; 
    } 
    return sum; 
} 

: http://bisqwit.iki.fi/story/howto/openmp/#ReductionClause 을하지만 원자는 복잡한 연산자를 지원하지 않습니다 난에 OpenMP의 감소가 동등 읽어 보시기 바랍니다. 내가 한 일은 중요와 원자 대체했고이 같은 OpenMP를하고 SSE와 감소를 구현 :

#define ROUND_DOWN(x, s) ((x) & ~((s)-1)) 
inline float sum_vector4_openmp(const float a[], const size_t N) { 
    __m128 sum4 = _mm_set1_ps(0.0f); 
    #pragma omp parallel 
    { 
     __m128 sum4_private = _mm_set1_ps(0.0f); 
     #pragma omp for nowait 
     for(int i=0; i < ROUND_DOWN(N, 4); i+=4) { 
      __m128 a4 = _mm_load_ps(a + i); 
      sum4_private = _mm_add_ps(a4, sum4_private); 
     } 
     #pragma omp critical 
     sum4 = _mm_add_ps(sum4_private, sum4); 
    } 
    __m128 t1 = _mm_hadd_ps(sum4,sum4); 
    __m128 t2 = _mm_hadd_ps(t1,t1); 
    float sum = _mm_cvtss_f32(t2); 
    for(int i = ROUND_DOWN(N, 4); i < N; i++) { 
     sum += a[i]; 
    } 
    return sum; 
} 

그러나,이 기능뿐만 아니라 내가 희망대로 작동하지 않습니다. Visual Studio 2012 Express를 사용하고 있습니다. SSE로드를 풀어서 성능을 조금 향상시킬 수 있다는 것을 알고 있습니다.하지만 몇 번 추가해도 여전히 예상보다 적습니다.

내가 스레드의 수와 동일한 배열의 슬라이스에 걸쳐 실행하여 더 나은 성능을 얻을 : 더 복잡와 감축을하는 더 나은 방법이 있는지

inline float sum_slice(const float a[], const size_t N) { 
    int nthreads = 4; 
    const int offset = ROUND_DOWN(N/nthreads, nthreads); 
    float suma[8] = {0}; 
    #pragma omp parallel for num_threads(nthreads) 
    for(int i=0; i<nthreads; i++) { 
     suma[i] = sum_vector4(&a[i*offset], offset); 
    } 
    float sum = 0.0f; 
    for(int i=0; i<nthreads; i++) { 
     sum += suma[i]; 
    } 
    for(int i=nthreads*offset; i < N; i++) { 
     sum += a[i]; 
    } 
    return sum;  
} 

inline float sum_vector4(const float a[], const size_t N) { 
    __m128 sum4 = _mm_set1_ps(0.0f); 
    int i = 0; 
    for(; i < ROUND_DOWN(N, 4); i+=4) { 
     __m128 a4 = _mm_load_ps(a + i); 
     sum4 = _mm_add_ps(sum4, a4); 
    } 
    __m128 t1 = _mm_hadd_ps(sum4,sum4); 
    __m128 t2 = _mm_hadd_ps(t1,t1); 
    float sum = _mm_cvtss_f32(t2); 
    for(; i < N; i++) { 
     sum += a[i]; 
    } 
    return sum; 

}

사람은 알고 있나요 OpenMP의 연산자?

+0

테스트를 수행하는 데 사용하는 N의 크기는 무엇입니까? – veda

+0

N (L1) = 32k, N (L2) = 256K, N (L3/4) = 2M 및 N >> N (L3)의 캐시 크기에 대한 테스트를 수행합니다. –

답변

1

질문에 대한 대답은 아닐 것입니다. OpenMP에서 더 복잡한 연산자를 사용하는 것이 더 좋은 방법이라고 생각하지 않습니다.

어레이가 16 비트 정렬되어 있고 openmp 스레드 수가 4라고 가정하면 OpenMP + SIMD가 성능 이득을 12 배 - 16 배로 기대할 수 있습니다. 실제로는 충분한 성능 향상을 얻을 수 없기 때문에

  1. openmp 스레드를 만드는 데 오버 헤드가 있습니다.
  2. 코드는 1로드 작업에 대해 1 더하기 연산을 수행하고 있습니다. 따라서 CPU는 충분한 계산을 수행하지 않습니다. 따라서 CPU가 데이터를로드하는 데 대부분의 시간을 소비하는 것처럼 보입니다. 일종의 메모리 대역폭이 필요합니다.
+0

SSE 코드에는 전달 루프 종속성이 있습니다. 속도를 높이기 위해 2 번 이상 펼칠 수 있습니다. 사실, 이미 그렇게했지만 텍스트를 더 길게 만들기 때문에 코드를 추가하지 않았습니다. –

+0

AVX도 테스트하기 때문에 배열은 32 비트 정렬됩니다. –