2014-11-12 31 views
4

512 비트 AVX 레지스터에서 float로 수평 덧셈을 효율적으로 수행하는 방법 (단일 벡터의 항목을 함께 추가)? 128 및 256 비트 레지스터의 경우 _mm_hadd_ps 및 _mm256_hadd_ps를 사용하여 수행 할 수 있지만 _mm512_hadd_ps는 없습니다. 인텔 내장 설명서는 _mm512_reduce_add_ps 문서를 제공합니다. 실제로는 하나의 명령어에 해당하는 것은 아니지만 그 존재는 최적의 방법이 있음을 시사합니다. 그러나 GCC의 최신 스냅 샷과 함께 제공되는 헤더 파일에는 정의 된 것으로 보이지 않으며 정의를 찾을 수 없습니다. 그것은 Google과 함께합니다.__m512 (AVX512)로 수평 덧셈

"hadd"는 _mm512_shuffle_ps 및 _mm512_add_ps로 에뮬레이션 될 수 있습니다. 또는 _mm512_extractf32x4_ps를 사용하여 512 비트 레지스터를 4 개의 128 비트 레지스터로 분리 할 수 ​​있지만 더 나은 기능이 누락되지 않도록하고 싶습니다.

+1

무엇 정확하게 수평으로하려고 노력하고 있습니다 조작? 대규모 감축 작업이 끝나면 성능에 심각한 영향을 미치지 않을 것입니다. (그럼에도 불구하고,'_mm512_reduce_add_ps'는 그 목적을 위해 존재하며 셔플과 합계의 바이너리 감소로 컴파일됩니다.) – Mysticial

+1

AVX-512는 표준에서 벗어난 것처럼 보였으므로이 사실을 알지 못합니다. 너비 "향상. 작업은 이미 128 비트 또는 256 비트 uops로 축소되어 있으므로 수평 명령은 아직 많은 의미가 없습니다. –

+2

@CoryNelson 더 나쁘게하려면 수평 명령어가 기존 프로세서에 마이크로 코드됩니다. 그래서 그들은 이미 느립니다. 또한 수평 벡터화 작업은 SIMD 패러다임을 위반하고 확장되지 않습니다. – Mysticial

답변

2

인텔 컴파일러는

_mm512_reduce_add_ps  //horizontal sum of 16 floats 
_mm512_reduce_add_pd  //horizontal sum of 8 doubles 
_mm512_reduce_add_epi32 //horizontal sum of 16 32-bit integers 
_mm512_reduce_add_epi64 //horizontal sum of 8 64-bit integers 

그러나, 지금까지의 나는 당신이하고있는 것보다 더 많은 것을 얻을 수 있다고 생각하지 않도록 어쨌든 여러 지침으로 나누어 이들을 말할 수있는 다음과 같은 고유 할 정의 수평 금액을 가지고 AVX512 레지스터의 상단과 하단의 수평 합.

__m256 low = _mm512_castps512_ps256(zmm); 
__m256 high = _mm256_castpd_ps(_mm512_extractf64x4_pd(_mm512_castps_pd(zmm),1)); 

__m256d low = _mm512_castpd512_pd256(zmm); 
__m256d high = _mm512_extractf64x4_pd(zmm,1); 

__m256i low = _mm512_castsi512_si256(zmm); 
__m256i high = _mm512_extracti64x4_epi64(zmm,1); 

가로 합계를 얻으려면 sum = horizontal_add(low + high)을 입력하십시오.

static inline float horizontal_add (__m256 a) { 
    __m256 t1 = _mm256_hadd_ps(a,a); 
    __m256 t2 = _mm256_hadd_ps(t1,t1); 
    __m128 t3 = _mm256_extractf128_ps(t2,1); 
    __m128 t4 = _mm_add_ss(_mm256_castps256_ps128(t2),t3); 
    return _mm_cvtss_f32(t4);   
} 

static inline double horizontal_add (__m256d a) { 
    __m256d t1 = _mm256_hadd_pd(a,a); 
    __m128d t2 = _mm256_extractf128_pd(t1,1); 
    __m128d t3 = _mm_add_sd(_mm256_castpd256_pd128(t1),t2); 
    return _mm_cvtsd_f64(t3);   
} 

나는 Agner Fog's Vector Class LibraryIntel Instrinsics Guide online에서 모든 정보와 기능을 얻었다.

+0

추출 높이 256 내장의 '_ps'버전이 없다고 확신합니까? 거기에'_pd'에 던져 넣는 것은 정말 이상하게 보입니다. 하지만 그렇습니다. 좋은 첫 번째 단계는 높은 256 및 수직 추가를 추출하는 것입니다. 그런 다음 동일한 작업을 128 번 수행 한 다음 'vhaddps'보다 더 나은 셔플을 사용하십시오.이 셔플은 2 번 셔플 + 수직 추가 비용이 든다. https://stackoverflow.com/questions/6996764/fastest-way-to-do-horizontal-float-vector-sum-on-x86을 참조하십시오. –

1

포스트는 내 질문에 대답 않는 내가, 체크 보손 Z를 줄 것이다,하지만 난 지침의 정확한 시퀀스에 따라 개선 될 수 있다고 생각 :

inline float horizontal_add(__m512 a) { 
    __m512 tmp = _mm512_add_ps(a,_mm512_shuffle_f32x4(a,a,_MM_SHUFFLE(0,0,3,2))); 
    __m128 r = _mm512_castps512_ps128(_mm512_add_ps(tmp,_mm512_shuffle_f32x4(tmp,tmp,_MM_SHUFFLE(0,0,0,1)))); 
    r = _mm_hadd_ps(r,r); 
    return _mm_cvtss_f32(_mm_hadd_ps(r,r)); 
} 
+0

더 나은 솔루션을 찾은 것을 기쁘게 생각합니다. 리눅스 용 인텔 컴파일러의 비상업적 인 버전을 무료로 구할 수 있습니다. 그런 다음 디스 어셈블리를보고'_mm512_reduce'로 수행 할 작업을 볼 수 있습니다. 그러나 중요한 루프에서 수평 추가를해서는 안된다는 것을 명심해야합니다. 그것은 SIMD의 목적을 이깁니다. –

+0

@Zboson Intel 컴파일러의 무료 버전은 좋을 지 모르지만 인텔 웹 사이트의 비상업적 소프트웨어 개발 섹션에 가면 "이 사이트는 개정 중입니다."라는 한 페이지 만 있습니다. 그것은 그 동안 그 길이었습니다. 수평 덧셈 코멘트에 관해서 : 알아요. 그러나 피할 수없는 경우 한 번에 하나씩 16 개의 숫자를 합치는 것보다 낫습니다. 그리고 그것은 하나의 작업을 최적화하려고하는 것과는 다릅니다. 나는 특수한 배열 (C++) 클래스를 가지고 있는데, 모든 SIMD 코드 (이는 내 벡터 클래스의 기초이기도하다)를 숨기고, 최적화하려고한다. – Rouslan

+0

그건 나쁜 소식입니다. 비상업적 인 소프트웨어 버전이 "수정 중"인지는 몰랐습니다. 음, 그것이 위안이라면 ICC는 도서관 (예 : MKL)이 아주 좋은 것 외에는 제 의견으로 과대 평가됩니다. –