2014-01-12 1 views
3

나는 s0128, s0256, s0512, s1024, s2048s4096 서명 정수 유형 및 f0128에 대한 모든 기존의 비트, 변화, 논리, 비교, 연산과 연산 기능을 제공하기 위해 - 64 어셈블리 언어로 코드 라이브러리를 쓰고 있어요 f0256, f0512, f1024, f2048f4096 부동 소수점 유형.큰 정수를 부호 확장하는 데 가장 효율적인 코드는 무엇입니까?

이제는 유형 변환 루틴을 작성 중이며 사소한 것이지만 예상했던 것보다 훨씬 많은 지침이 필요합니다. 이 작업을 더 쉽게하기 위해 뭔가 빠져 있어야만하는 것처럼 느껴지지만 지금까지는 아무런 운이 없었습니다.

s0256 결과 낮은 128 비트

단순히 s0128 입력 인수의 사본이며, s0256 결과의 상위 128 비트의 모든 비트는 s0128 입력의 최상위 비트에 설정해야 논의.

간단합니다. 하지만 지금까지 제가 s0256s0128으로 변환하는 데 최선의 방법이 있습니다. 첫 번째 4 줄을 무시하고 (오류 확인 만하는) 마지막 두 줄을 남겨 둡니다 (오류가없는 함수에서 돌아옴 (rax == 0)). 가운데 5 줄은 문제의 알고리즘입니다. [조건부] 점프 지시를 피하십시오.

.text 
.align 64 
big_m63: 
.quad -63, -63      # two shift counts for vpshaq instruction 

big_s0256_eq_s0128: # (s0256* arg0, const s0128* arg1); # s0256 = s0256(s0128) 
    orq  %rdi, %rdi    # is arg0 a valid address ??? 
    jz   error_argument_invalid # nope 
    orq  %rsi, %rsi    # is arg1 a valid address ??? 
    jz   error_argument_invalid # nope 

    vmovapd (%rsi), %xmm0   # ymm0 = arg1.ls64 : arg1.ms64 : 0 : 0 
    vmovhlps %xmm0, %xmm0, %xmm1  # ymm1 = arg1.ms64 : arg1.ms64 : 0 : 0 
    vpshaq  big_m63, %xmm1, %xmm1 # ymm1 = arg1.sign : arg1.sign : 0 : 0 
    vperm2f128 $32, %ymm1, %ymm0, %ymm0 # ymm1 = arg1.ls64 : arg1.ms64 : sign : sign 
    vmovapd %ymm0, (%rdi)   # arg0 = arg1 (sign-extended to 256-bits) 

    xorq  %rax, %rax    # rax = 0 == no error 
    ret         # return from function 

이 루틴

모든 명령이 어떤 명령의 병렬 실행을 방지 이전 명령의 결과를 요구 또한 비 최적이다.

부호 확장을 사용하여 오른쪽 시프트하는 것이 더 좋은 지시가 있습니까? 내가 왜 (많은 SIMD 명령어가 다양한 목적으로 즉각적인 8 비트 피연산자를 가졌는지) 알지 못하더라도 shift-count를 지정하기 위해 즉각적인 바이트를 허용하는 vpshaq과 같은 명령어를 찾을 수 없습니다. 또한 Intel은 vpshaq을 지원하지 않습니다. 죄송합니다.

하지만보세요! StephenCanon은 아래의이 문제에 대한 훌륭한 해결책을 가지고 있습니다! 굉장해! 그 솔루션은 위의 명령어보다 하나 더 많은 명령어를 가지고 있지만 vpxor 명령어는 첫 번째 vmovapd 명령어 다음에 놓을 수 있으며 위의 5 명령어 버전보다 더 많은 사이클을 효과적으로 가져 가면 안됩니다. 브라보!

.text 
.align 64 
big_s0256_eq_s0128: # (s0256* arg0, const s0128* arg1); # s0256 = s0256(s0128) 
    orq  %rdi, %rdi    # is arg0 a valid address ??? 
    jz   error_argument_invalid # nope 
    orq  %rsi, %rsi    # is arg1 a valid address ??? 
    jz   error_argument_invalid # nope 

    vmovapd (%rsi), %xmm0   # ymm0 = arg1.ls64 : arg1.ms64 : 0 : 0 
    vpxor  %xmm2, %xmm2, %xmm2  # ymm2 = 0 : 0 : 0 : 0 
    vmovhlps %xmm0, %xmm0, %xmm1  # ymm1 = arg1.ms64 : arg1.ms64 : 0 : 0 
    vpcmpgtq %xmm1, %xmm2, %xmm1  # ymm1 = arg1.sign : arg1.sign : 0 : 0 
    vperm2f128 $32, %ymm1, %ymm0, %ymm0 # ymm1 = arg1.ls64 : arg1.ms64 : sign : sign 
    vmovapd %ymm0, (%rdi)   # arg0 = arg1 (sign-extended to 256-bits) 

    xorq  %rax, %rax    # rax = 0 == no error 
    ret         # return from function 

나는 확실하지 않다, 또한 메모리에서 그 두 개의 64 비트 시프트 수를 읽을 수있다 필요로하지 않는 :

완전성과 쉽게 비교를 위해

, 여기에 최신 StephenCanon 향상과 코드 코드의 속도를 약간 올리십시오. 좋은.

답변

3

지나치게 복잡합니다. rax에 로그인 한 후 ymm0에 결과를 어셈블하는 대신 2 개의 64b 스토어를 수행하십시오. 명령어가 하나 줄어들고 종속성 체인이 훨씬 짧아집니다.

목적지 유형이 커지면 물론, 더 넓은 매장 (AVX)을 사용하는 것이 좋습니다. AVX2를 사용하면 vbroadcastq을 사용하여 더 효율적으로 표시 할 수 있지만 기본 AVX를 타겟팅하는 것처럼 보입니까?

대부분의 알고리즘에서 ~ 512b 정수가되면 곱셈과 같은 수퍼 선형 연산의 비용이 부호 확장과 같은 연산에서 모든 마지막 사이클을 빠르게 쥐어 짜는 실행 시간을 완전히 압도한다는 점에 유의해야합니다. 값.그것은 좋은 운동이지만, 시간의 궁극적하지 가장 생산적인 사용은 한 번 구현은 "충분"입니다


더 생각 후에, 나는 다음과 같은 제안이 있습니다.

vmovhlps %xmm0, %xmm0, %xmm1 // could use a permute instead to stay in integer domain. 
vpxor  %xmm2, %xmm2, %xmm2 
vpcmpgtq %xmm1, %xmm2, %xmm2 // generate sign-extension without shift 

이것은이를 (a) 일정한로드를 요구하지 않고 (b) Intel과 AMD 모두에서 일하는 덕목. 0을 생성하는 xor는 추가 명령어처럼 보입니다. 그러나 실제로이 zeroing idiom은 최근 프로세서에서 실행 슬롯을 요구하지 않습니다 .


AVX2를 대상으로하는 경우

FWIW는,이처럼 작성할 수 있습니다

vmovdqa (%rsi),  %xmm0 // { x0, x1, 0, 0 } 
vpermq $0x5f, %ymm0, %ymm1 // { 0, 0, x1, x1 } 
vpxor %ymm2, %ymm2, %ymm2 // { 0, 0, 0, 0 } 
vpcmpgtq %ymm1, %ymm2, %ymm2 // { 0, 0, s, s } s = sign extension 
vpor  %ymm2, %ymm0, %ymm0 // { x0, x1, s, s } 
vmovdqa %ymm0,  (%rdi) 

불행하게도, 나는 vpermq가 AMD로 볼 수 있다고 생각하지 않습니다.

+0

'AVX2'가 아직 제공되지 않았다는 것을 알지 못했습니다. 내가 테스트해야하는 CPU는'FX8150'과'FX8350'입니다. 나는이 CPU가 가지고있는 명령을 기꺼이 받아 들일 것이다. 예, 지원하는 데이터 유형은's0128','s0256','s0512','s1024','s2048','s4096' 및'f0128','f0256','f0512','f1024'입니다. , 'f2048','f4096' 부동 소수점. 더 큰 크기의 경우 부호 비트로 채울 64 비트 청크의 수가 커질수록 최상의 접근 방식이 변경됩니다. 그래서'SIMD''ymm' 레지스터에서 가능한 한 많은 것을하려고했는데, 더 큰 데이터 타입에 대해 더 효율적일 것입니다. – honestann

+0

@honestann : 맞습니다. 큰 데이터 유형의 경우, 예제에서와 같이 AVX로 signbit를 표시하고 싶을 것입니다. 128-> 256의 특별한 경우 (여기에서 사용)는 GPR을 저장하기 위해 조금 더 세련되었습니다. AVX2는 Intel "Haswell"마이크로 아키텍처에서 사용할 수 있지만 아직 AMD 부품에서는 사용할 수 없으므로 선택의 여지가 없습니다. –

+0

@StevenCanon : 또한 캐시의 단일 64 바이트 줄에 다른 값을 반복적으로 쓰는 것으로 인해 CPU 회로가 캐시 회로를 지연시키는 것에 대해 걱정했습니다. 모든 병합 작업을 완료해야만 캐시에 대한 단일 캐시 라인 (특히 연속 명령)이 캐시 회로에 추가 지연을 부과 할 수 있습니다. 나는 이런 상황을 처리하기 위해 캐시 회로를 설계해야하는 불쌍한 놈이되는 것을 싫어한다. – honestann