我一直在试验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);
}
答案 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
为最大计算值。