SSE内在函数算术错误

时间:2014-09-26 03:53:01

标签: c gcc intel sse simd

我一直在试验SSE内在函数,我似乎遇到了一个我无法弄清楚的奇怪错误。我正在计算两个浮点数组的内积,一次计算4个元素。

为了测试,我将两个数组的每个元素都设置为1,因此产品应该是== size。

它运行正常,但每当我运行大小为>的代码时〜68000000使用sse intrinsics的代码开始计算错误的内部产品。它似乎陷入了一定的数额而且从未超过这个数字。以下是一个示例运行:

joe:~$./test_sse 70000000
sequential inner product: 70000000.000000
sse        inner product: 67108864.000000
sequential          time: 0.417932
sse                 time: 0.274255

汇编:

gcc -fopenmp test_sse.c -o test_sse -std=c99

这个错误在我测试过的少数计算机中似乎是一致的。这是代码,或许有人可以帮我弄清楚发生了什么:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <omp.h>
#include <math.h>
#include <assert.h>

#include <xmmintrin.h>

double inner_product_sequential(float * a, float * b, unsigned int size) {

  double sum = 0;

  for(unsigned int i = 0; i < size; i++) {
    sum += a[i] * b[i];
  }

  return sum;

}

double inner_product_sse(float * a, float * b, unsigned int size) {

  assert(size % 4 == 0);

  __m128 X, Y, Z;

  Z = _mm_set1_ps(0.0f);

  float arr[4] __attribute__((aligned(sizeof(float) * 4)));

  for(unsigned int i = 0; i < size; i += 4) {

    X = _mm_load_ps(a+i);
    Y = _mm_load_ps(b+i);

    X = _mm_mul_ps(X, Y);
    Z = _mm_add_ps(X, Z);

  }

  _mm_store_ps(arr, Z);

  return arr[0] + arr[1] + arr[2] + arr[3];

}

int main(int argc, char ** argv) {

  if(argc < 2) {
    fprintf(stderr, "usage: ./test_sse <size>\n");
    exit(EXIT_FAILURE);
  }

  unsigned int size = atoi(argv[1]);

  srand(time(0));

  float *a = (float *) _mm_malloc(size * sizeof(float), sizeof(float) * 4);
  float *b = (float *) _mm_malloc(size * sizeof(float), sizeof(float) * 4);

  for(int i = 0; i < size; i++) {
    a[i] = b[i] = 1;
  }



  double start, time_seq, time_sse;


  start = omp_get_wtime();

  double inner_seq = inner_product_sequential(a, b, size);

  time_seq = omp_get_wtime() - start;


  start = omp_get_wtime();

  double inner_sse = inner_product_sse(a, b, size);

  time_sse = omp_get_wtime() - start;


  printf("sequential inner product: %f\n", inner_seq);
  printf("sse        inner product: %f\n", inner_sse);
  printf("sequential          time: %f\n", time_seq);
  printf("sse                 time: %f\n", time_sse);




  _mm_free(a);
  _mm_free(b);
}

1 个答案:

答案 0 :(得分:2)

您正在遇到单精度浮点数的精度限制。数字16777216(2 ^ 24),它是向量Z到达&#34;限制时的每个分量的值。内积,以32位浮点表示为十六进制0x4b800000或二进制0 10010111 00000000000000000000000,即23位尾数全为零(隐式前导1位),8位指数部分为151表示指数151 - 127 = 24.如果向该值添加1,则需要增加指数,但是添加的指数不能再在尾数中表示,因此在单精度浮点运算中2 ^ 24 + 1 = 2 ^ 24。

在顺序函数中没有看到这一点,因为你使用的是64位双精度值来存储结果,而且当我们在x86平台上工作时,内部很可能是一个80位过多的精度寄存器。使用

您可以强制在顺序代码中使用单精度,将其重写为

float sum;

float inner_product_sequential(float * a, float * b, unsigned int size) {
  sum = 0;

  for(unsigned int i = 0; i < size; i++) {
    sum += a[i] * b[i];
  }

  return sum;
}

,您会看到16777216.000000为最大计算值。