这是我第一次使用SSE内在函数.我试图将一段简单的代码转换为使用英特尔SSE内在的更快的版本(直到SSE4.2).我好像遇到了一些错误.
代码的标量版本是:(简单矩阵乘法)
void mm(int n, double *A, double *B, double *C)
{
int i,j,k;
double tmp;
for(i = 0; i < n; i++)
for(j = 0; j < n; j++) {
tmp = 0.0;
for(k = 0; k < n; k++)
tmp += A[n*i+k] *
B[n*k+j];
C[n*i+j] = tmp;
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的版本:我已经包含了 #include <ia32intrin.h>
void mm_sse(int n, double *A, double *B, double *C)
{
int i,j,k;
double tmp;
__m128d a_i, b_i, c_i;
for(i = 0; i < n; i++)
for(j = 0; j < n; j++) {
tmp = 0.0;
for(k = 0; k < n; k+=4)
a_i = __mm_load_ps(&A[n*i+k]);
b_i = __mm_load_ps(&B[n*k+j]);
c_i = __mm_load_ps(&C[n*i+j]);
__m128d tmp1 = __mm_mul_ps(a_i,b_i);
__m128d tmp2 = __mm_hadd_ps(tmp1,tmp1);
__m128d tmp3 = __mm_add_ps(tmp2,tmp3);
__mm_store_ps(&C[n*i+j], tmp3);
}
}
Run Code Online (Sandbox Code Playgroud)
我哪里错了?我收到这样的几个错误:
mm_vec.c(84):错误:类型"int"的值不能分配给类型为"__m128d"的实体a_i = __mm_load_ps(&A [n*i + k]);
这就是我编译的方式:icc -O2 mm_vec.c -o vec
有人可以帮我准确地转换这段代码.谢谢!
更新:
根据您的建议,我做了以下更改:
void mm_sse(int n, float *A, float *B, float *C)
{
int i,j,k;
float tmp;
__m128 a_i, b_i, c_i;
for(i = 0; i < n; i++)
for(j = 0; j < n; j++) {
tmp = 0.0;
for(k = 0; k < n; k+=4)
a_i = _mm_load_ps(&A[n*i+k]);
b_i = _mm_load_ps(&B[n*k+j]);
c_i = _mm_load_ps(&C[n*i+j]);
__m128 tmp1 = _mm_mul_ps(a_i,b_i);
__m128 tmp2 = _mm_hadd_ps(tmp1,tmp1);
__m128 tmp3 = _mm_add_ps(tmp2,tmp3);
_mm_store_ps(&C[n*i+j], tmp3);
}
}
Run Code Online (Sandbox Code Playgroud)
但现在我似乎遇到了分段错误.我知道这可能是因为我没有正确访问阵列A,B,C的数组下标.我对此很新,不知道如何处理这个问题.
请帮我确定处理此代码的正确方法.
Fle*_*exo 10
您看到的错误是因为您在函数名称中有太多下划线,例如:
__mm_mul_ps
Run Code Online (Sandbox Code Playgroud)
应该:
_mm_mul_ps // Just one underscore up front
Run Code Online (Sandbox Code Playgroud)
所以C编译器假设他们返回,int因为它没有看到声明.
除此之外,还有其他问题 - 您似乎将调用混合到同一指令的double和single float变体.
例如,你有:
Run Code Online (Sandbox Code Playgroud)__m128d a_i, b_i, c_i;
但是你打电话:
Run Code Online (Sandbox Code Playgroud)__mm_load_ps(&A[n*i+k]);
返回一个__m128不是__m128d- 你想打电话:
_mm_load_pd
Run Code Online (Sandbox Code Playgroud)
代替.同样,对于其他指令,如果您希望它们在双精度对上工作.
如果你看到无法解释的分段错误和SSE代码,我会倾向于猜测你有内存对齐问题 - 传递给SSE内在函数(大多数为1)的指针需要16字节对齐.您可以在代码中使用简单的断言来检查它,或者在调试器中检查它(如果指针正确对齐,您希望指针的最后一位为0).
如果它没有正确对齐,你需要确保它是正确的.对于未使用new/ 分配的内容,malloc()您可以使用编译器扩展(例如使用gcc)执行此操作:
float a[16] __attribute__ ((aligned (16)));
Run Code Online (Sandbox Code Playgroud)
如果你的gcc版本有一个足够大的最大对齐支持这个,还有一些关于堆栈对齐的注意事项.对于动态分配的存储,您需要使用特定于平台的扩展,例如posix_memalign,分配合适的存储:
float *a=NULL;
posix_memalign(&a, __alignof__(__m128), sizeof(float)*16);
Run Code Online (Sandbox Code Playgroud)
(我认为使用C++ 11可能会有更好,更便携的方法,但我还不能100%肯定).
1有一些指令允许您对未对齐的加载和存储进行操作,但与对齐的加载相比,它们非常慢,并且如果可能的话,值得避免.