2010-06-01 5 views
5

두 개의 큰 (100000 개 이상의 요소) 벡터의 내적을 계산하기위한 SSE 내장 함수를 구현하는 간단한 프로그램을 작성했습니다. 이 프로그램은 두 가지 실행 시간, 일반적인 방법으로 계산 된 내부 제품 및 내장 함수를 사용하여 실행 시간을 비교합니다. 내부 제품을 계산하는 명령문 앞에 내부 루프를 삽입 할 때까지 모든 것이 잘 작동합니다.g ++ SSE intrinsics 딜레마 - 내장 "saturates"의 값

g++ -W -Wall -O2 -pedantic -march=i386 -msse intrinsics_SSE_innerProduct.C -o innerProduct 

기능 모두에서 위의 루프의 각각은, N의 총 실행 :

//this is a sample Intrinsics program to compute inner product of two vectors and compare Intrinsics with traditional method of doing things. 

     #include <iostream> 
     #include <iomanip> 
     #include <xmmintrin.h> 
     #include <stdio.h> 
     #include <time.h> 
     #include <stdlib.h> 
     using namespace std; 

     typedef float v4sf __attribute__ ((vector_size(16))); 

     double innerProduct(float* arr1, int len1, float* arr2, int len2) { //assume len1 = len2. 

      float result = 0.0; 
      for(int i = 0; i < len1; i++) { 
      for(int j = 0; j < len1; j++) { 
       result += (arr1[i] * arr2[i]); 
      } 
      } 

     //float y = 1.23e+09; 
     //cout << "y = " << y << endl; 
     return result; 
     } 

     double sse_v4sf_innerProduct(float* arr1, int len1, float* arr2, int len2) { //assume that len1 = len2. 

      if(len1 != len2) { 
      cout << "Lengths not equal." << endl; 
      exit(1); 
      } 

      /*steps: 
     * 1. load a long-type (4 float) into a v4sf type data from both arrays. 
     * 2. multiply the two. 
     * 3. multiply the same and store result. 
     * 4. add this to previous results. 
     */ 

      v4sf arr1Data, arr2Data, prevSums, multVal, xyz; 
      //__builtin_ia32_xorps(prevSums, prevSums); //making it equal zero. 
     //can explicitly load 0 into prevSums using loadps or storeps (Check). 

      float temp[4] = {0.0, 0.0, 0.0, 0.0}; 
      prevSums = __builtin_ia32_loadups(temp); 
      float result = 0.0; 

      for(int i = 0; i < (len1 - 3); i += 4) { 
      for(int j = 0; j < len1; j++) { 
      arr1Data = __builtin_ia32_loadups(&arr1[i]); 
      arr2Data = __builtin_ia32_loadups(&arr2[i]); //store the contents of two arrays. 
      multVal = __builtin_ia32_mulps(arr1Data, arr2Data); //multiply. 
      xyz = __builtin_ia32_addps(multVal, prevSums); 
      prevSums = xyz; 
      } 
     } 
      //prevSums will hold the sums of 4 32-bit floating point values taken at a time. Individual entries in prevSums also need to be added. 
      __builtin_ia32_storeups(temp, prevSums); //store prevSums into temp. 

      cout << "Values of temp:" << endl; 
      for(int i = 0; i < 4; i++) 
      cout << temp[i] << endl; 

      result += temp[0] + temp[1] + temp[2] + temp[3]; 

     return result; 
     } 

     int main() { 
      clock_t begin, end; 
      int length = 100000; 
      float *arr1, *arr2; 
      double result_Conventional, result_Intrinsic; 

//   printStats("Allocating memory."); 
      arr1 = new float[length]; 
      arr2 = new float[length]; 
//   printStats("End allocation."); 

      srand(time(NULL)); //init random seed. 
//   printStats("Initializing array1 and array2"); 
      begin = clock(); 
      for(int i = 0; i < length; i++) { 
     // for(int j = 0; j < length; j++) { 
      // arr1[i] = rand() % 10 + 1; 
       arr1[i] = 2.5; 
      // arr2[i] = rand() % 10 - 1; 
       arr2[i] = 2.5; 
     // } 
      } 
      end = clock(); 
      cout << "Time to initialize array1 and array2 = " << ((double) (end - begin))/CLOCKS_PER_SEC << endl; 
    //  printStats("Finished initialization."); 

    //  printStats("Begin inner product conventionally."); 
      begin = clock(); 
      result_Conventional = innerProduct(arr1, length, arr2, length); 
      end = clock(); 
      cout << "Time to compute inner product conventionally = " << ((double) (end - begin))/CLOCKS_PER_SEC << endl; 
    //  printStats("End inner product conventionally."); 

     // printStats("Begin inner product using Intrinsics."); 
      begin = clock(); 
      result_Intrinsic = sse_v4sf_innerProduct(arr1, length, arr2, length); 
      end = clock(); 
      cout << "Time to compute inner product with intrinsics = " << ((double) (end - begin))/CLOCKS_PER_SEC << endl; 
      //printStats("End inner product using Intrinsics."); 

      cout << "Results: " << endl; 
      cout << " result_Conventional = " << result_Conventional << endl; 
      cout << " result_Intrinsics = " << result_Intrinsic << endl; 
     return 0; 
     } 

나는 다음과 같은 g을 사용하여이 구축 ++ 호출 : 내가 더 가기 전에, 여기에 코드입니다^2 번. 그러나 arr1과 arr2 (두 부동 소수점 벡터)의 값이 2.5이고 배열의 길이가 100,000이므로 두 경우의 결과는 6.25e + 10이어야합니다. 내가 얻는 결과는 다음과 같습니다

결과 :
result_Conventional = 6.25e + 10
result_Intrinsics = 5.36871e + 08

이 전부는 아닙니다. intrinsics를 사용하는 함수에서 반환 된 값은 위의 값에서 "포화"하는 것 같습니다. 배열의 요소와 다른 크기에 다른 값을 넣으려고했습니다. 그러나 배열 내용과 1000 이상의 모든 크기에 대해 1.0을 초과하는 값은 위의 값과 같습니다.

처음에는 SSE 내의 모든 작업이 부동 소수점이지만 부동 소수점은 e + 08 정도의 숫자를 저장할 수 있어야했기 때문에 생각했습니다.

내가 잘못 될 수있는 곳을 찾으려고하고 있지만 알아낼 수없는 것 같습니다. g ++ 버전을 사용하고 있습니다 : g ++ (GCC) 4.4.1 20090725 (Red Hat 4.4.1-2).

모든 도움을 받으실 수 있습니다.

감사합니다.
Sriram.

답변

5

float은 6.25e + 10을 저장할 수 있지만 그 정확도는 몇 자릿수입니다.

많은 수의 작은 숫자를 한꺼번에 합쳐 큰 숫자를 만들 때 더 작은 숫자가 큰 숫자의 가장 낮은 정밀도의 자릿수보다 작은 지점에 도달하므로 추가합니다 아무 효과가 없다.

비 내장 버전에서이 동작이 발생하지 않는 이유는 부동 소수점의 실제 저장소보다 더 높은 정밀도를 사용하는 레지스터에 변수가 보유되어있을 가능성이 있습니다 (잘림되지 않음). result 루프의 모든 반복마다 float의 정밀도로 생성 된 어셈블러 코드를 살펴 봐야합니다.

+0

그러나 내부 루프 ((int j = 0, j Sriram

+0

그의 대답을 다시 읽으십시오. 그는 값이 이미 클 때 발생한다고 말합니다. 그래서 2.5e08으로 시작한 다음 2를 더하면.5는 아무런 효과가 없을 수 있습니다. 차이가있을 경우 double로 바꾸고 확인해야합니다. – Puppy

+0

방금 ​​"결과"를 double로 바꿨습니다. 다른 점이 없다. 내가 얻는 출력은 위에서 언급 한 출력과 같습니다. – Sriram