2017-11-14 32 views
3

인텔 내장 함수를 사용하여 여러 개의 단 정밀도 연산을 병렬로 수행하는 알고리즘을 작성했습니다. 내 알고리즘의 각 반복의 결과는 단일 256 비트 벡터 (__m256)의 0이 아닌 항목의 수입니다. 예를 들어__mm256 벡터에서 0이 아닌 항목의 수를 계산하는 가장 빠른 방법은 무엇입니까?

:

반복의 결과가 4

벡터의 수가 제로가 아닌 항목을 계산하는 가장 빠른 방법은 무엇입니까이다

00000000 FFFFFFFF 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF 

?

float results[8]; 
_mm256_storeu_ps(results, result_vector); 

int count = 0; 
for (uint32_t idx = 0; idx < 8; ++idx) 
{ 
    if (results[idx] != 0) 
    {    
     ++count; 
    } 
} 

이 방법은 잘 작동하지만, 아마도, 상점을 포함하지 않는 한 그것을 할 수있는 더 효율적인 방법이 있는지 궁금 :

현재 나는이 같은 일을하고 있어요.

+0

0이 아닌 항목은 '0xFFFFFFFF'가 보장됩니까? 그렇다면 마스크를 사용하여 각 32 비트 섹션에서 최하위 비트를 분리 한 다음 절대 차이의 합을 적용하는 것이 좋습니다. – njuffa

+3

아니면 그냥 제로 ('_mm256_cmp_ps')와 비교하고, 비트 마스크 ('_mm256_movemask_ps')를 추출하고'popcnt'를 사용하여 비트를 계산합니까? 3 가지 지시 사항. –

+2

이미 0/0xFFF ... (즉, 비교 결과)이면 'cmpps' 단계를 건너 뛰고 movemask/popcnt 만 이동할 수 있습니다. –

답변

6

하드웨어 popcnt 지침이 최선의 방법입니다. 그것은 빠르며 vmovmskps은 정수 비트 마스크로 각 요소의 상위 비트를 제공하는 데 매우 효율적입니다. (비교/movemask는 벡터 비교 결과에서 분기하거나 index a lookup table of shuffle masks에 사용하는 표준 방법입니다).

movemask/popcnt는 유용한 포인터 when left-packing을 사용하여 저장 한 요소의 수만큼 대상 포인터를 증가시킵니다 (셔플 후). 는 CPU (또는 가상 머신) AVX와하지만 하드웨어 popcnt이있을 수 이론적 있도록

#include <immintrin.h> 

// use only with compare-results. 
// or to count elements with their sign-bit set 
unsigned count_true(__m256 v) { 
    unsigned mask = _mm256_movemask_ps(v); 
    return _mm_popcnt_u32(mask); 
} 

popcnt는 AVX는 별도의 기능 비트를 가지고 있지만, 실제로 나는 그것에 대해 걱정하지 않을 것입니다. 당신이 뭔가에 대한 벡터 레지스터에 결과를 원하는 경우에도


을 (popcnt가 SSE4.2 도입하고, AVX는 SSE4.2을 의미했다)/popcnt/movd 아마 수평을 추가하는 것보다 더 나은 순서입니다 vmovmskps 0/-1 정수가있는 요소가 추가됩니다. 그러면 8 개의 원소를 1로 줄이기 위해 3 개의 셔플/스텝을 추가하면 음의 합이 생깁니다.

대부분의 경우 비교 결과를 정수로 처리하기 때문에 0/-1이 유용 할 때가 있습니다. 예 : 조건부로 카운터 벡터를 증가 시키려면 cmpps/psubd 트릭을 수행합니다. (0 + x = x이므로 false 요소는 변경되지 않습니다.)