매우 짧은 정렬 된 배열을 통해 검색을 최적화하여 주어진 value
에 속한 버킷을 찾으려고합니다. 배열의 크기를 가정하면 8 복식, 나는 AVX의 내장 함수의 다음과 같은 순서로 올라와있다된다짧게 배열 된 double 배열을 통해 검색
_data = _mm256_load_pd(array);
temp = _mm256_movemask_pd(_mm256_cmp_pd(_data, _value, _CMP_LT_OQ));
pos = _mm_popcnt_u32(temp);
_data = _mm256_load_pd(array+4);
temp = _mm256_movemask_pd(_mm256_cmp_pd(_data, _value, _CMP_LT_OQ));
pos += _mm_popcnt_u32(temp);
놀랍게도 (I 내 머리의 명령 대기 시간 사양이없는 ..), 그것은 상태
for(i=0; i<7; ++i) if(array[i+1]>=value) break;
이 루프는 내가 매우 효율적인 코드로 찾았는지에 컴파일 : 빠른 코드는 다음과 같은 C의 루프 GCC에 의해 생성되는 것을
lea ecx, [rax+1]
vmovsd xmm1, QWORD PTR [rdx+rcx*8]
vucomisd xmm1, xmm0
jae .L7
lea ecx, [rax+2]
vmovsd xmm1, QWORD PTR [rdx+rcx*8]
vucomisd xmm1, xmm0
jae .L8
[... repeat for all elements of array]
그래서 4 지침 t 소요 o 버킷 1 개를 확인하십시오 (lea
, vmovsd
, vucomisd
, jae
). value
이 균일하게 퍼져 있다고 가정하면 평균적으로 value
에 대해 ~ 3.5 버킷을 확인해야합니다. 분명히 앞서 언급 한 AVX 코드를 능가하기에 충분합니다.
일반적인 경우에, 배열은 당연히 8 개보다 클 수 있습니다. 내가 루프 본문에 대해 다음 명령 시퀀스를 얻을
for(i=0; u<n-1; i++) if(array[i+1]>=value) break;
: 나는이 같은 C 루프 코드 경우
.L76:
mov eax, edx
.L67:
cmp eax, esi
jae .L77
lea edx, [rax+1]
mov ecx, edx
vmovsd xmm1, QWORD PTR [rdi+rcx*8]
vucomisd xmm1, xmm0
jb .L76
내가 루프를 풀다하는 GCC를 말할 수 있지만, 요점은 수 있다는 것입니다 요소 당 명령 수는 일정한 경계가있는 루프의 경우보다 커서 코드가 더 느립니다. 또한 vmovsd
주소 지정에 추가 rcx
레지스터를 사용하는 이유를 알 수 없습니다.
내가 수동으로 첫 번째 예처럼 보일 수있는 루프 조립을 수정할 수 있으며, 빠르게 작업 않습니다
.L76:
cmp edx, esi # eax -> edx
jae .L77
lea edx, [rdx+1] # rax -> rdx
vmovsd xmm1, QWORD PTR [rdi+rdx*8]
vucomisd xmm1, xmm0
jb .L76
하지만 난 그것을 GCC를 만들 수없는 것. 그리고 나는 그것이 가능하다는 것을 안다 - 첫번째 예제에서 생성 된 asm은 OK이다.
인라인 asm을 사용하는 대신에 어떻게해야하는지 아이디어가 있습니까? 아니면 더 나은 - 당신은 검색의 빠른 구현을 제안 할 수 있습니까?
+1 ** "귀하의 특정 프로세서 **에 대해"_I는 루프를 풀어주기 위해 gcc에게이 경우에는 코드가 더 느립니다 "라고 말할 수 있습니다. 나는 당신이 약간의 ** 당신의 최적화에서 너무 앞서 가고 있다고 생각한다 ** 위험은 ** 당신의 테스트 머신의 아키텍쳐와 가장 잘 어울린다 ** (어쩌면, 다른 것에서는 성능이 좋지 않다). 물론 CPU가 고정되어 있고 시간이 지남에 따라 변경되지 않는 임베디드 시스템 용으로 최적화하지 않은 경우. –
@Adriano 일반 경계가있는 루프의 풀린 코드는 상수 경계가있는 루프에 대해 생성 된 코드보다 느립니다. 나는 플랫폼에 많이 의존 할 것이라고 기대하지는 않습니다. 단지 2 명령어가 적습니다. 아니면 다른 것을 놓치고 있습니까? – angainor
gcc가 생성 된 것을 볼 수 없기 때문에 여기에서 추측합니다. 그러나 캐시 크기 (1 차 레벨 캐시), 분기 예측 또는 파이프 라인 내용을 확인합니다. –