2016-11-12 4 views
2

인텔의 SSE 내장 함수를 사용하여 프로세서의 플래그 레지스터를 확인할 수 있는지 궁금합니다. 예를 들어SSE 내장 함수 제로 체크 플래그

: 컴파일러는 단일 명령 (pcmpistri) 및 플래그가 점프 명령 (jz)로 등록 확인에 두 내장 함수를 최적화 할 수있는 본 실시 예에서는

int idx = _mm_cmpistri(mmrange, mmstr, 0x14); 
int zero = _mm_cmpistrz(mmrange, mmstr, 0x14); 

.

그러나 다음 예에서 컴파일러는 적절하게 코드를 최적화하기 위해 관리하지 않습니다 : 여기에

__m128i mmmask = _mm_cmpistrm(mmoldchar, mmstr, 0x40); 
int zero = _mm_cmpistrz(mmoldchar, mmstr, 0x40); 

는, 컴파일러는 pcmpistrmpcmpistri 명령어를 생성합니다. 그러나 제 생각에는 pcmpistrmpcmistri과 같은 방식으로 프로세서의 플래그 레지스터에 플래그를 설정하기 때문에 두 번째 명령어가 중복됩니다.

그래서 내 질문에 다시 와서 플래그 레지스터를 직접 읽거나 컴파일러에만 pcmpistrm 명령어를 생성하도록 지시하는 방법이 있습니까?

+1

어떤 컴파일러가 어떤 옵션을 제공합니까? 이것은 컴파일러가 성공적으로 CSEing하는 것에 대한 질문 인 것처럼 보입니다. ISA 설명서에는 [PCMPISTRI] (http://www.felixcloutier.com/x86/PCMPISTRI.html)와 [PCMPISTRM] (http://www.felixcloutier.com/x86)의 내장 함수 중 하나 인 '_mm_cmpistrz' /PCMPISTRM.html), 인텔에 따르면 컴파일러는'_mm_cmpistrz'에 대한 명령어를 방출 할 수 있습니다. –

+0

또한 이것을 컴파일하는 함수로 감쌀 수 있습니까? 사람들이 그것을 http://gcc.godbolt.org/에 복사 할 수 있습니까? 또는 Godbolt에서 직접 source + asm 출력에 연결하십시오. –

+0

@Peter Cordes 모든 최적화가 활성화 된 상태에서 MSVC 컴파일러를 사용합니다 (/ O2) – Philinator

답변

1

MSVC 누락 - 최적화 버그처럼 보입니다. 고유하지는 않습니다.

#include <immintrin.h> 
__m128i foo(__m128i mmoldchar, __m128i mmstr) 
{  
    __m128i mmmask = _mm_cmpistrm(mmoldchar, mmstr, 0x40); 
    int zero = _mm_cmpistrz(mmoldchar, mmstr, 0x40); 
    if(zero) 
    return mmmask; 
    else 
    return _mm_setzero_si128(); 
} 

    ##gcc6.2 -O3 -march=nehalem 
    pcmpistrm  xmm0, xmm1, 64 
    je  .L5 
    pxor xmm0, xmm0 
    ret 
.L5: 
    ret 

OTOH, clang3.9는 CSE 및 사용에 실패

gcc6.2와 icc17 성공적으로 테스트 기능을 하나의 PCMPISTRM에서 두 결과를 사용하여 본인은 zero 결과에 지점 (on the Godbolt compiler explorer) 썼다 PCMPISTRI. Agner Fog's instruction tables에 따라, PCMPISTRM 좋은 처리량하지만 대기 시간이 있으므로 대기 시간이 병목 인 경우 병렬로 두 가지를 할 수있는 공간을 많이 거기에

foo: 
    movdqa xmm2, xmm0 
    pcmpistri  xmm2, xmm1, 64 
    pxor xmm0, xmm0 
    jne  .LBB0_2 
    pcmpistrm  xmm2, xmm1, 64 
.LBB0_2: 
    ret 

참고. __readflags()을 사용하는 것과 같은 농구대를 뛰어 넘는 것은 실제로 더 나쁠 수 있습니다.

0

해결책을 직접 찾았습니다.

__readeflags()이라는 플래그 레지스터를 읽는 기능이 있습니다. 그것은 pushf (x64 플랫폼에 pushfq) 명령어를 래핑합니다.

__m128i mmmask = _mm_cmpistrm(mmoldchar, mmstr, 0x40); 
int zero = __readeflags() & 0x40; //0x40 is the mask for the zero flag (bit 6) 

이 솔루션은 최적 아니지만, 트릭을 수행합니다

코드는 이제 다음과 같습니다.

+1

나는 최적화가 PCMPISTRM을 PUSHF와 분리 할 수 ​​있고, 정수 덧셈/뺄셈 또는 무언가로부터 플래그를 읽는 것에 대해 심각하게 걱정할 것이다. 이것이 신뢰할 수있는 경우 스택에 플래그를 쓰는 ~ 5 사이클 저장 대기 시간을 측정 한 다음 테스트하면 처리량에 대해 대부분의 CPU에서 다른 PCMPISTRI보다 좋을 수 있습니다. 대기 시간의 경우 PCMPISTRM의 처리량은 높지만 대기 시간이 길기 때문에 두 개를 병렬로 실행하면 동일한 결과가 두 번 표시 될 수 있으므로 여유있는 5c보다 좋을 수 있습니다. –

+0

맞습니다! 필자는 두 가지 솔루션을 모두 벤치마킹했으며 'pushf'를 사용하는 벤치 마크는'pcmpistrm'과'pcmpistri'를 병렬로 사용하는 것보다 실제로 약 1ns 느립니다. – Philinator

+0

벤치 마크에 실제 사용 사례가 반영되어 있으므로주의하십시오. 대기 시간 대 처리량은 큰 문제입니다. –