2016-09-04 3 views
2

테스트 중입니다. Intel ADX 캐리와 함께 더하고 파이프 라인에 오버 플로우를 추가하면 큰 정수가 추가됩니다. 예상되는 코드 생성이 어떻게 보이는지보고 싶습니다. _addcarry_u64 and _addcarryx_u64 with MSVC and ICC에서, 나는이 적절한 테스트 케이스가 될 것이라고 생각 : 나는 -O3-madx를 사용하여 generated code from GCC 6.1을 검토 할 때는adcx 및 adox 용 테스트 케이스

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

int main(int argc, char* argv[]) 
{ 
    #define MAX_ARRAY 100 
    uint8_t c1 = 0, c2 = 0; 
    uint64_t a[MAX_ARRAY]={0}, b[MAX_ARRAY]={0}, res[MAX_ARRAY]; 
    for(unsigned int i=0; i< MAX_ARRAY; i++){ 
     c1 = _addcarryx_u64(c1, res[i], a[i], (unsigned long long int*)&res[i]); 
     c2 = _addcarryx_u64(c2, res[i], b[i], (unsigned long long int*)&res[i]); 
    } 
    return 0; 
} 

, 그것은 직렬화 addc을 보여준다. -O1-O2 유사한 결과를 생성합니다 ... 꽤 마크를 타격하지 않거나 뭔가 잘못하고있는 중이 야, 아니면 내가 잘못 뭔가를 사용하고

main: 
     subq $688, %rsp 
     xorl %edi, %edi 
     xorl %esi, %esi 
     leaq -120(%rsp), %rdx 
     xorl %ecx, %ecx 
     leaq 680(%rsp), %r8 
.L2: 
     movq (%rdx), %rax 
     addb $-1, %sil 
     adcq %rcx, %rax 
     setc %sil 
     addb $-1, %dil 
     adcq %rcx, %rax 
     setc %dil 
     movq %rax, (%rdx) 
     addq $8, %rdx 
     cmpq %r8, %rdx 
     jne  .L2 
     xorl %eax, %eax 
     addq $688, %rsp 
     ret 

그래서 테스트 케이스 같은데요을

_addcarryx_u64에있는 Intel의 문서를 올바르게 파싱한다면 C 코드가 파이프 라인을 생성해야한다고 생각합니다. 그래서 내가 뭔가 잘못하고있는 중이 야 같은데요 :

이 C_IN 부호없는 8 비트 반입와 부호없는 64 비트 정수 a와 b를 추가

설명 (운반 또는 오버 플로우 플래그), 부호없는 64 비트 결과를 out에 저장하고, 및 캐리 아웃을 dst (캐리 또는 오버플로 플래그)에 저장합니다.

나는 pipeline'd가 오버 플로우 (adcx/adox)를 추가/캐리와 함께 추가 생성 할 수있는 방법

?

$ cat /proc/cpuinfo | grep adx 
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush 
dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc 
arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni 
pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 fma cx16 xtpr pdcm pcid sse4_1 
sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 
3dnowprefetch ida arat epb pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase 
tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt 
... 
+0

MSVC가 64 비트 모드에서 인라인 어셈블리를 허용하지 않기 때문에 이러한 내장 함수가 대부분 있다고 생각합니다. GCC를 사용하면이 경우 인라인 어셈블리를 사용해야합니다. 실제로 GCC를 사용하여 수십 년 동안 지속 되어온'adc '를 사용하는 가장 좋은 방법은 인라인 어셈블리입니다. 옵션으로 인라인 어셈블리를 사용하는 것이 좋지만 GCC에서 사용하는 PITA와 같이 너무 좋지 않습니다. –

답변

1

이 좋은 테스트의 경우처럼 않습니다


은 실제로 테스트를위한 준비 5 세대 코어 i7합니다 ( adx CPU 플래그를 통지)를 가지고있다. 올바른 코드로 어셈블됩니다. 컴파일러가 최적의 코드 작성을 아직 지원하지 않더라도 그러한 의미에서 내장 함수를 지원하는 것이 유용합니다. 그것은 사람들이 내장 함수를 사용하게합니다. 이것은 호환성을 위해 필요합니다.

내년 또는 adcx/adox에 대한 컴파일러의 백엔드 지원이 완료 될 때마다 동일한 코드가 소스 수정없이 더 빠른 바이너리로 컴파일됩니다.

나는 그것이 gcc에서 일어난 일이라고 생각한다.


그 소리는 3.8.1의 구현은 더 리터럴이지만, 그것은 끔찍한 일을 끝 : sahf 및 푸시/EAX의 팝업 국기와 함께 절약. See it on Godbolt.

mov eax, ch은 어셈블하지 않으므로 asm 소스 출력에 버그가 있다고 생각합니다. (gcc와는 달리, clang/LLVM은 빌트인 어셈블러를 사용하며 실제로 LLVM IR에서 머신 코드로가는 길에 asm의 텍스트 표현을 거치지 않습니다. 기계어 코드를 해체하면 mov eax,ebp이 표시됩니다. bpl (또는 나머지 레지스터)에는 그 시점에서 유용한 값이 없으므로 버그라고 생각합니다. 아마도 mov al, ch 또는 movzx eax, ch을 원했을 것입니다.

+0

업데이트 : 해당 소스에서 clang3.9 및 4.0 크래시가 발생하면 clang5.0이이를 합리적으로 컴파일합니다. (adcx 만 사용하지만 각 체인의 캐리를 개별적으로 저장/복원하여 ILP를 활성화하기에 충분한 언 롤링을 사용합니다.) –

0

add_carryx _...에 대한 훨씬 더 인라인 된 코드를 생성하도록 GCC가 수정 될 때.루프 변종에는 비교 (하위 명령과 마찬가지로 C 및 O 플래그 수정) 및 증분 (C 및 O 플래그가 add 명령어와 같이 수정 됨)이 포함되어 있으므로 코드에주의해야합니다. 이러한 이유로

for(unsigned int i=0; i< MAX_ARRAY; i++){ 
     c1 = _addcarryx_u64(c1, res[i], a[i], (unsigned long long int*)&res[i]); 
     c2 = _addcarryx_u64(c2, res[i], b[i], (unsigned long long int*)&res[i]); 
    } 

, 코드에서 C1과 C2는 항상 pitifuly 처리 (저장 및 각 루프 반복에서 임시 레지스터 복원)됩니다. gcc가 생성 한 결과 코드는 좋은 이유로 인해 제공 한 어셈블리처럼 보입니다.

런타임의 관점에서 res [i]는 2 개의 add_carryx 명령어 사이의 즉각적인 종속이며 2 명령어는 독립적이지 않으며 프로세서에서 가능한 아키텍처 병렬 처리의 이점을 얻지 못합니다.

코드는 단지 예일 뿐이지 만 gcc가 수정 될 때 사용하는 것이 가장 좋은 예가 아닐 수도 있습니다.

큰 정수 계산에서 3 개의 숫자를 추가하는 것은 어려운 문제입니다. 벡터화가 도움이된다면 addcarryx를 사용하여 루프 변종을 병렬로 처리 할 수 ​​있습니다 (증분 및 비교 + 동일한 변수의 분기, 또 다른 어려운 문제).

+0

clang5.0은 유용 할 수있을 정도로 루프를 풀어줍니다. (https://godbolt.org/g/2NTfVs) 실제로 2 차 캐리 체인을 첫 번째에 종속시키는 것은 흥미로운 테스트입니다. 그러나 단방향 의존성이라는 점에 유의하십시오.'res [] + = a []'체인은'res [] + = b []'체인보다 먼저 실행될 수 있습니다. (레지스터에있는 동안 4 개의 res [] 값을 재사용한다.) –

+0

플래그를 사용하지 않고 루프를 돌리지 않고,'lea'와'jrcxz '를 사용하지 않는 한, 저장/복원시 반복을 피하기 위해 루프 언 롤링이 필요하다. ', 또는'loop' [그러나 불행히도 AMD를 제외하고는 효율적이지 않습니다] (https://stackoverflow.com/questions/35742570/why-is-the-loop-instruction-slow-couldnt-intel-have- –

+0

godbolts에 대한 링크 주셔서 감사합니다. 다른 컴파일러에서 생성 된 다른 코드를 보면 adcx는 adc 인 것처럼 사용되며 adox는 사용되지 않습니다. 몇 번의 반복을 풀면 2 종속성 체인은 인터리브 될 수 있고 pushf/popf는 루프 변형 시간에 두 플래그를 저장/복원하는 데 사용될 수 있습니다 ..... – Pierre

관련 문제