2009-12-28 5 views
7

Core i7 아키텍처에서 복소수/정수 벡터의 최소/최대 계산을 가속화 할 수있는 asm 명령어가 있습니까?x86 최대/분 asm 명령어?

업데이트 :

나는 그런 풍부한 답변을 기대하지 않았다가, 감사합니다. 그래서 최대/최소 분기없이 할 수있는 참조하십시오. 하위 질문이 있습니다.

배열에서 가장 큰 double의 인덱스를 얻는 효율적인 방법이 있습니까?

+0

호스트 언어 란 무엇입니까? 그것이 c/C++라면 너무 걱정하지 않을 것입니다. –

+0

최대 약 300 개의 두 배가 큰 프로그램의 가장 내부 루프에 있습니다. 8,000 줄의 코드 중 약 10 개에 85 %의 시간이 소요됩니다. 호스트 언어는 그 때문에 중요하지 않습니다. 하지만 그렇습니다 C++ –

답변

12

SSE4는 부호있는/부호없는 32 비트 정수의 경우 PMAXSD 또는 PMAXUD이므로 유용 할 수 있습니다.

SSE2가있다 MAXPD와 사이에 두 배의 쌍에 걸쳐 비교, 그래서 당신은 부하 및 운영의 일반적인 인터레이스와, N의 벡터의 최대를 얻기 위해 N 하나 MAXSD와/2-1 MAXPDs에 따라 MAXSD.

위의 MIN 상당량이 있습니다.

peregrino:$ g++ -O3 src/min_max.cpp -o bin/min_max 
peregrino:$ g++ -O3 -msse4 -mfpmath=sse src/min_max.cpp -o bin/min_max_sse 
peregrino:$ time bin/min_max 
0,40 

real 0m0.874s 
user 0m0.796s 
sys 0m0.004s 
peregrino:$ time bin/min_max_sse 
0,40 

real 0m0.457s 
user 0m0.404s 
sys 0m0.000s 

min_max 500 복식의 배열의 최소 및 최대를 계산 : 이중 케이스를 들어

, 당신은 아마 SSE 모드에서 반 괜찮은 C의 ++ 컴파일러보다 어셈블러에 더 잘하지 않을거야 순진 루프를 사용하여 10 번 : 두 번째 부분에 대응

bool min_max (double array[], size_t len, double& min, double& max) 
{ 
    double min_value = array [ 0 ]; 
    double max_value = array [ 0 ]; 

    for (size_t index = 1; index < len; ++index) { 
     if (array [ index ] < min_value) min_value = array [ index ]; 
     if (array [ index ] > max_value) max_value = array [ index ]; 
    } 

    min = min_value; 
    max = max_value; 
} 

는 최대 동작에서 분기 제거하는 기존의 최적화는 노래로 플래그를 얻을, 값을 비교하는 것입니다 le 비트 (0 또는 1), 1을 뺀 (0 또는 0xffff_ffff) 및 'and'를 두 가지 가능한 결과의 xor로 비교하면 (a > best ? (current_index^best_index) : 0)^best_index)과 같습니다. SSE가 태그 값보다는 패킹 된 값으로 작동하는 경향이 있기 때문에 단순한 SSE 방법이있는 것 같습니다. 몇 가지 수평 인덱스 작업이 있으므로 최대 벡터를 찾은 다음 원래 벡터의 모든 요소에서 해당 인덱스를 뺀 다음 부호 비트를 모으고 0으로 서명 한 인덱스는 최대 인덱스에 해당하지만 아마도 반바지 또는 바이트를 사용하지 않는 한 개선되지 않습니다. 대부분의 플랫폼에서 이미 최적화 포함 된 라이브러리가있다 : 당신은 당신이 당신의 두 번째 질문에 대한 응답으로 (다른 것들 사이) 벡터의 최소/최대

+0

하나의 SIMD 벡터의 수평 최대 값을 얻으려면 log2 (vector_length) 셔플 + MAXPS/MAXPD 연산 만 필요합니다 (VL/2가 아님). 기본적으로 [가로 합]과 동일한 개념입니다 (https://stackoverflow.com/questions/6996764/fastest-way-to-do-horizontal-float-vector-sum-on-x86) : 매회 반으로 좁음 . (또는 모든 요소에 결과 브로드 캐스트를 남기고, 고/저 교환). –

+0

메모리가 병목 현상이 아닌 경우 다중 누적기로 언 롤링하면 속도가 2 배 이상 향상됩니다. ('MAXPD'는 3 또는 4 사이클 레이턴시를 갖지만 사이클 당 처리량이 1이기 때문에 컴파일러가 여러 벡터를 사용하고 배열의 끝에 그들을 결합하는 것이 필요합니다.) clang은 auto- vectorizing,하지만 gcc는 여전히 일반적으로하지 않습니다. –

4

SSPS의 MAXPS 및 MINPS는 모두 단 정밀도 부동 소수점 숫자로 작동합니다. PMAXSW, PMINSW, PMAXUB 및 PMINUB은 모두 부호있는 또는 부호없는 8 비트 워드로 작동합니다. 두 입력 SSE 레지스터 또는 주소 위치를 요소별로 비교하고 그 결과를 SSE 레지스터 또는 메모리 위치에 저장합니다.

MAXPS 및 MINPS의 SSE2 버전은 배정도 수레에서 작동해야합니다.

사용중인 컴파일러 및 최적화 플래그는 무엇입니까? gcc 4.0 이상에서는 대상에서 지원하는 경우 작업을 자동으로 벡터화해야하며 이전 버전에서는 특정 플래그가 필요할 수 있습니다.

2

이 바로 연산 (및 대부분의 다른 간단한 벡터 연산)의 구현. 을 사용하십시오.OS의 X에

  • 는에서 vDSP_maxviD()cblas_idamax()가있는 Accelerate.framework
  • 인텔 컴파일러는 cblas_idamax()있을 것이다 cblas_idamax()
  • 대부분의 리눅스 시스템을 포함한 고성능 구현을 가지고있는 IPP 및 MKL 라이브러리를 포함 그 출처에 따라 잘 조정될 수도 있고 그렇지 않을 수도있는 BLAS 도서관에서;
  • 다른 모든 것이 실패 할 경우 ATLAS (자동 튜닝 된 선형 대수 소프트웨어)를 사용하여 대상 플랫폼
  • 에서 적절한 성능 구현을 얻을 수 있습니다 (성능에 신경 쓰는 사용자는 일반적으로 좋은 구현을 갖습니다.
2

을 계산하는 벡터 statistical functions를 사용할 수있는 인텔의 IPP 라이브러리를 사용하는 경우

-1

두 번째 질문에 대한 대답으로이 데이터를 수집하고 저장하는 방법에 대해 생각하는 것이 가치가 있습니다.

데이터를 항상 정렬 된 상태로 유지하는 B- 트리에 데이터를 저장할 수 있으며 대수 비교 연산 만 필요합니다.

그러면 최대 값을 알 수 있습니다.

http://en.wikipedia.org/wiki/B_tree

+1

당신은 단지 300 개의 double을 다루기 때문에 self-balanced binary tree가 가장 좋습니다. http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree – Drew

+0

왜 바이너리 힙이 아닌가요? 로그보다 일정 시간 이상 ... –

0

업데이트 : 난 그냥 당신이 2 부에서 "배열"이 아닌 "벡터"나는 그것이 유용 경우 어쨌든 여기를 떠날거야 말했다 깨달았다.


재 : 부분 2 :

  • 수평 최대 수행 SSE 벡터의 최대/최소의 요소의 인덱스를 찾는. 2 double 요소의 128b 벡터의 경우 두 요소에 결과 브로드 캐스트를 남기는 것은 shufpd + maxpd입니다.

    다른 경우에는 더 많은 단계를 거쳐야합니다. 아이디어는 addpsmaxps 또는 minps으로 바꾸고 아이디어를 얻으려면 Fastest way to do horizontal float vector sum on x86을 참조하십시오. (그러나 SSE4 phminposuw을 사용할 수 있기 때문에 16 비트 정수는 특별합니다. 최대 값은 255에서 뺍니다)

  • 모든 요소가 최대 인 벡터 원본 벡터와 벡터 비교를 수행합니다.

    (pcmpeqq 정수 비트 패턴 또는 보통 cmpeqpd은 모두 double의 경우 작동합니다).

  • int _mm_movemask_pd (__m128d a) (movmskpd) 정수 비트 맵으로 비교 결과를 얻으십시오.
  • 비트 스캔 (bsf) (첫 번째 일치) : index = _bit_scan_forward(cmpmask). 정수 비교를 사용하면 cmpmask = 0을 사용할 수 없습니다 (적어도 하나의 요소가 NaN이더라도 일치합니다).

이것은 6 개의 명령어 (movapd 포함)로 컴파일해야합니다. 예, 그냥 the Godbolt compiler explorer을 확인하고 SSE를 사용합니다.

#include <immintrin.h> 
#include <x86intrin.h> 

int maxpos(__m128d v) { 
    __m128d swapped = _mm_shuffle_pd(v,v, 1); 
    __m128d maxbcast = _mm_max_pd(swapped, v); 
    __m128d cmp = _mm_cmpeq_pd(maxbcast, v); 
    int cmpmask = _mm_movemask_pd(cmp); 
    return _bit_scan_forward(cmpmask); 
} 

참고 : _mm_max_pd is not commutative with NaN inputs.NaN이 가능하고 Intel Nehalem의 성능에 신경 쓰지 않는다면 _mm_cmpeq_epi64을 사용하여 비트 패턴을 비교하는 것이 좋습니다. Float에서 vec-int 로의 바이 패스 지연은 Nehalem에서 문제가된다.

NaN! = IEEE 부동 소수점의 NaN이므로 _mm_cmpeq_pd 결과 마스크는 모두 NaN 경우에서 모두 0이 될 수 있습니다.

2 요소의 경우 항상 0 또는 1이되도록 할 수있는 또 다른 방법은 비트 스캔을 cmpmask >> 1으로 바꾸는 것입니다. (bsf은 이상하게 입력 = 모두 0 임).