2012-04-16 8 views
6

벡터 내장 함수를 사용하여 개인용 이미지 처리 라이브러리를 다시 작성하여 SIMD 기능을 사용하는 방법을 배우고 있습니다. 하나 개의 기본 기능은 간단한 "배열 +=,"즉 임의의 배열 길이에 대한임의의 배열 길이에 대한 SIMD 배열 추가

void arrayAdd(unsigned char* A, unsigned char* B, size_t n) { 
    for(size_t i=0; i < n; i++) { B[i] += A[i] }; 
} 

, 명백한 SIMD 코드 (16 정렬 가정)입니다 같은 것입니다 :

size_t i = 0; 
__m128i xmm0, xmm1; 
n16 = n - (n % 16); 
for (; i < n16; i+=16) { 
    xmm0 = _mm_load_si128((__m128i*) (A + i)); 
    xmm1 = _mm_load_si128((__m128i*) (B + i)); 
    xmm1 = _mm_add_epi8(xmm0, xmm1); 
    _mm_store_si128((__m128i*) (B + i), xmm1); 
} 
for (; i < n; i++) { B[i] += A[i]; } 

는하지만 할 수 있습니다 모두 SIMD 지침이 추가 되었습니까? 나는이하려고 생각 : 여분의 요소에 대한

__m128i mask = (0x100<<8*(n - n16))-1; 
_mm_maskmoveu_si128(xmm1, mask, (__m128i*) (B + i)); 

,하지만 정의되지 않은 동작에 그 결과를 것인가? mask은 배열 경계를 지나서 실제로 액세스 할 수 없음을 보장해야합니다 (필자는 생각합니다). 다른 방법으로 여분의 요소를 먼저 처리하는 것이지만 배열을 n-n16으로 정렬해야합니다.

벡터화 된 루프와 같은 다른 최적의 패턴이 있습니까?

+0

. 그러나 에필로그는 속도 측면에서 중요하지 않습니다. – Walter

답변

4

하나의 옵션은 배열을 16 바이트의 배수로 채우는 것입니다. 그런 다음 128 비트로드/추가/저장을 수행하고 관심있는 지점 다음의 결과를 무시하십시오.

큰 배열의 경우 byte by "epilog"의 오버 헤드는 매우 작습니다. 루프를 풀기 것은 더 같은 성능을 향상시킬 수 있습니다 :

for (; i < n32; i+=32) { 
    xmm0 = _mm_load_si128((__m128i*) (A + i)); 
    xmm1 = _mm_load_si128((__m128i*) (B + i)); 
    xmm2 = _mm_load_si128((__m128i*) (A + i + 16)); 
    xmm3 = _mm_load_si128((__m128i*) (B + i + 16)); 
    xmm1 = _mm_add_epi8(xmm0, xmm1); 
    xmm3 = _mm_add_epi8(xmm2, xmm3); 
    _mm_store_si128((__m128i*) (B + i), xmm1); 
    _mm_store_si128((__m128i*) (B + i + 16), xmm3); 
} 
// Do another 128 bit load/add/store here if required 

을하지만 일부 프로파일 링을하지 않고 말을하기 어렵다.

끝 부분에 정렬되지 않은로드/저장소를 수행 할 수도 있습니다 (16 바이트 이상 있다고 가정).하지만 큰 차이는 없을 것입니다. 예 : 당신이 20 바이트가 있다면 당신은/저장 0 일 부하를 상쇄 할 다른 정렬되지 않은로드/추가/저장 (_mm_storeu_si128, __mm_loadu_si128을) 당신은 _mm_maskmoveu_si128를 사용할 수 4.

을 상쇄 할 수 있지만, 당신은 XMM 레지스터에 마스크를 얻을 필요 , 그리고 당신의 샘플 코드는 작동하지 않을 것이다. 마스크 레지스터를 모든 FF로 설정 한 다음 Shift를 사용하여 정렬하십시오. 결국, 정렬되지 않은로드/추가/저장보다 속도가 느려질 것입니다.

이 뭔가처럼 될 것입니다 : 당신이 배열의 길이는 항상 16 바이트의 배수 인 코드에 그 (아마도 적은 요소가 실제로 사용 되더라도) 때문에,이 에필로그가 온다 결코 보장 할 수

mask = _mm_cmpeq_epi8(mask, mask); // Set to all FF's 
mask = _mm_srli_si128(mask, 16-(n%16)); // Align mask 
_mm_maskmoveu_si128(xmm, mask, A + i); 
+0

실제로는 마스크를 찾아보기 테이블에 넣을 것입니다. "에필로그 (epilog)"루프보다 여전히 느릴 것이라고 생각합니까? –

+0

@reve_etrange : 속도가 느리지는 않겠지 만 두 가지 솔루션을 측정하지 않으면 알기가 어렵습니다. 시도 해봐. –

+0

나는 그것을 줄 것이다. 하지만 합법적 인 메모리 액세스입니까? 마스크의 * 값이 배열 범위 위반을 일으킬 수 있기 때문입니다. –