2010-12-04 12 views
3

내가 이것에 머리말을 붙이겠습니다. 나는 ASM에 대한 경험이 극히 제한되어 있으며, SIMD에 대한 경험은 매우 적습니다.AltiVec에 MMX/SSE 명령어 포팅하기

그러나 나는 다음과 같은 MMX/SSE 내가 PPC/셀 프로세서에서 사용하기 위해 알티 벡 지침에서 포트에 싶으면, 최적화 된 코드를 가지고 발생합니다.

이것은 아마 큰 ..이 코드 몇 줄을 비록, 내가 여기에 무슨 일이 해결하려고 노력 문제의 끝이 없었습니다 부탁드립니다.

원래 기능 :

static inline int convolve(const short *a, const short *b, int n) 
{ 
    int out = 0; 
    union { 
     __m64 m64; 
     int i32[2]; 
    } tmp; 
    tmp.i32[0] = 0; 
    tmp.i32[1] = 0; 
    while (n >= 4) { 
     tmp.m64 = _mm_add_pi32(tmp.m64, 
           _mm_madd_pi16(*((__m64 *)a), 
              *((__m64 *)b))); 
     a += 4; 
     b += 4; 
     n -= 4; 
    } 
    out = tmp.i32[0] + tmp.i32[1]; 
    _mm_empty(); 

    while (n --) 
     out += (*(a++)) * (*(b++)); 
    return out; 
} 

나는이를 다시 작성하는 방법에 대한 모든 팁은 알티 벡 지침을 사용할 수 있나요?

내 첫 번째 시도 (매우 잘못된 시도는) 다음과 같이 보입니다 .. 그러나 그것은 완전히 (또는 원격으로) 올바른 아니에요.

static inline int convolve_altivec(const short *a, const short *b, int n) 
{ 
    int out = 0; 
    union { 
     vector unsigned int m128; 
     int i64[2]; 
    } tmp; 

    vector unsigned int zero = {0, 0, 0, 0}; 

    tmp.i64[0] = 0; 
    tmp.i64[1] = 0; 
    while (n >= 8) { 
     tmp.m128 = vec_add(tmp.m128, 
           vec_msum(*((vector unsigned short *)a), 
              *((vector unsigned short *)b), zero)); 

     a += 8; 
     b += 8; 
     n -= 8; 
    } 
    out = tmp.i64[0] + tmp.i64[1]; 
#endif 
    while (n --) 
     out += (*(a++)) * (*(b++)); 
    return out; 
} 

답변

3

당신은 멀리 아니에요 - 나는 코드를 조금 정리, 몇 가지 사소한 문제가 고정 된 테스트 장치를 추가하고 확인을 지금 작동하는 것 같다 :

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <altivec.h> 

static int convolve_ref(const short *a, const short *b, int n) 
{ 
    int out = 0; 
    int i; 

    for (i = 0; i < n; ++i) 
    { 
     out += a[i] * b[i]; 
    } 

    return out; 
} 

static inline int convolve_altivec(const short *a, const short *b, int n) 
{ 
    int out = 0; 
    union { 
     vector signed int m128; 
     int i32[4]; 
    } tmp; 

    const vector signed int zero = {0, 0, 0, 0}; 

    assert(((unsigned long)a & 15) == 0); 
    assert(((unsigned long)b & 15) == 0); 

    tmp.m128 = zero; 

    while (n >= 8) 
    { 
     tmp.m128 = vec_msum(*((vector signed short *)a), 
          *((vector signed short *)b), tmp.m128); 

     a += 8; 
     b += 8; 
     n -= 8; 
    } 

    out = tmp.i32[0] + tmp.i32[1] + tmp.i32[2] + tmp.i32[3]; 

    while (n --) 
     out += (*(a++)) * (*(b++)); 

    return out; 
} 

int main(void) 
{ 
    const int n = 100; 

    vector signed short _a[n/8 + 1]; 
    vector signed short _b[n/8 + 1]; 

    short *a = (short *)_a; 
    short *b = (short *)_b; 

    int sum_ref, sum_test; 

    int i; 

    for (i = 0; i < n; ++i) 
    { 
     a[i] = rand(); 
     b[i] = rand(); 
    } 

    sum_ref = convolve_ref(a, b, n); 
    sum_test = convolve_altivec(a, b, n); 

    printf("sum_ref = %d\n", sum_ref); 
    printf("sum_test = %d\n", sum_test); 

    printf("%s\n", sum_ref == sum_test ? "PASS" : "FAIL"); 

    return 0; 
} 
+1

브릴리언트. 폴 고마워. 나는 'zero'배열의 벡터 타입을 signed int 타입 (m128 변수의 그것과 일치하도록)으로 수정해야만했다. 그렇지 않으면 절대적인 처리 (그리고 성능 측면에서의 논란)를 해왔다. 이것은 나를 SIMD 확장에 대해 더 알고 싶습니다. –

+0

@Tim Kane : 대단하다. 다행이다. 이제 제로 벡터에 얼핏보기에 - 지금 고쳤습니다. AltiVec은 정말 멋지지만, 안타깝게도 지금은 나가고 있습니다. 예를 들어 Intel의 AVX와 AMD의 SSE5와 같이 흥미 진진한 SIMD가 있습니다. –

1

(경고 : 내 알티 벡 경험의 모든 박스 360/PS3 작업에서 온다 - 나는 그들이 다른 알티 벡 플랫폼에서 얼마나 다양한 모르겠어요).

먼저 포인터 정렬을 확인해야합니다. 대부분의 벡터로드 (및 저장) 조작은 16Y이트 정렬 주소에서 이루어집니다. 그렇지 않은 경우 대개 경고없이 계속 진행되지만 예상 한 데이터를 얻지 못합니다.

것이 가능 (하지만 느린)의 정렬되지 않은로드를 할 수 있지만, 당신은 기본적으로 데이터 이전과 이후 조금 읽고 그들을 결합해야합니다. Apple's Altivec page을 참조하십시오. 또한 lvlxlvrx로드 명령어를 사용한 다음 OR 연산을 수행하기 전에이 작업을 수행했습니다.


다음은 사용자의 곱셈과 덧셈이 같은지 확실하지 않습니다. 나는 _mm_madd_pi16 또는 vec_msum 중 어느 것도 사용하지 않았기 때문에 동등하지는 않습니다. 디버거를 단계별로 실행하여 동일한 입력 데이터에 대해 동일한 출력을 제공해야합니다. 또 다른 가능한 차이점은 오버플로를 다르게 처리 할 수 ​​있다는 것입니다 (예 : 모듈러 대 포화).


마지막으로 적어도, 당신은 그래서 노동 조합은 4의 int를 보유해야하고, 당신은 마지막에 모두 4 요약한다 대신 2의 시간에서 4의 int를 계산하고 없습니다.