我正在尝试用C学习SSE内在函数的绳索。我有一段代码,我在其中加载双数据的双组分向量,向其中添加内容然后尝试将其存储回内存。 一切正常:我可以将数据加载到SEE寄存器中,我可以对那些SSE寄存器中的数据进行操作,但是当我尝试将处理后的数据写回原始数组时(我在第一次读取数据时)我得到了一个分段错误。
任何人都可以就此问题向我提出建议 - 这让我感到疯狂。
double res[2] __attribute__((aligned(16)));
for(int k=0; k<n; k++){
int i=0;
for(; i+1<n; i+=2)
{
__m128d cik = _mm_load_pd(&C[i+k*n]);
int j = 0;
for(; j+1<n; j+=2)
{
__m128d aTij = _mm_load_pd(&A_T[j+i*n]);
__m128d bjk = _mm_load_pd(&B[j+k*n]);
__m128d dotpr = _mm_dp_pd(aTij, bjk,2);
cik = _mm_add_pd(cik, dotpr);
}
_mm_store_pd(res, cik);
//C[i+k*n] = res[0];
}
}
正如我上面所说,除了我将结果存储到一维数组“C”之外,一切都在这个代码中工作,我从那里开始读取我的数据。 也就是说,当我删除
前面的评论标志时//C[i+k*n] = res[0];
我遇到了分段错误。
我怎样才能从C读取_mm_load_pd的对齐内存版本(所以C必须在内存中对齐!),而回写它不起作用? “C”必须对齐,正如您所见,“res”也必须对齐。
免责声明:我的原始代码已阅读
_mm_store_pd(&C[i+k*n], cik);
也产生了一个分段错误,我开始在我尝试解决问题的过程中引入“res”并进行显式对齐。
附录
A,B,C声明如下:
buf = (double*) malloc (3 * nmax * nmax * sizeof(double));
double* A = buf + 0;
double* B = A + nmax*nmax;
double* C = B + nmax*nmax;
尝试使用posix_memalign解决方案
为了在写入原始一维数组时解决分段错误问题,我现在使用缓冲区来处理相应的矩阵。但是,当尝试写回C_buff时,这仍然是segfauls!
double res[2] __attribute__((aligned(16)));
double * A_T;
posix_memalign((void**)&A_T, 16, n*n*sizeof(double));
double * B_buff;
posix_memalign((void**)&B_buff, 16, n*n*sizeof(double));
double * C_buff;
posix_memalign((void**)&C_buff, 16, n*n*sizeof(double));
for(int y=0; y<n; y++)
for(int x=0; x<n; x++)
A_T[x+y*n] = A[y+x*n];
for(int x=0; x<n; x++)
for(int y=0; y<n; y++)
B_buff[y+x*n] = B[y+x*n];
for(int x=0; x<n; x++)
for(int y=0; y<n; y++)
C_buff[y+x*n] = C[y+x*n];
for(int k=0; k<n; k++){
int i=0;
for(; i+1<n; i+=2)
{
__m128d cik = _mm_load_pd(&C_buff[i+k*n]);
int j = 0;
for(; j+1<n; j+=2)
{
__m128d aTij = _mm_load_pd(&A_T[j+i*n]);
__m128d bjk = _mm_load_pd(&B_buff[j+k*n]);
__m128d dotpr = _mm_dp_pd(aTij, bjk,2);
cik = _mm_add_pd(cik, dotpr);
}
_mm_store_pd(&C_buff[i+k*n], cik);
//_mm_store_pd(res, cik);
//C_buff[i+k*n] = res[0];
//C_buff[i+1+k*n] = res[1];
}
}
答案 0 :(得分:1)
删除_mm_store_pd(&C_buff[i+k*n], cik);
后,整个循环将被优化并删除。编译器推断整个for循环不会导致任何有意义的工作并将其删除。这就是为什么你不再出现分段错误的原因
我确定分段错误是由于数组的大小。
根据您的示例考虑这个简单的程序:
#include <stdio.h>
#include "emmintrin.h"
int main(){
int n = 15;
int y,x,k,i,j;
double * A;
posix_memalign((void**)&A, 16, n*n*sizeof(double));
double * B;
posix_memalign((void**)&B, 16, n*n*sizeof(double));
double * C;
posix_memalign((void**)&C, 16, n*n*sizeof(double));
for(y=0; y<n; y++)
for(x=0; x<n; x++)
A[x+y*n] = 0.1;
for(x=0; x<n; x++)
for(y=0; y<n; y++)
B[y+x*n] = 0.1;
for(x=0; x<n; x++)
for( y=0; y<n; y++)
C[y+x*n] = 0.1;
for( k=0; k<n; k++){
i=0;
for(; i+1<n; i+=2)
{
__m128d cik = _mm_load_pd(&C[i+k*n]);
j = 0;
for(; j+1<n; j+=2)
{
__m128d aTij = _mm_load_pd(&A[j+i*n]);
__m128d bjk = _mm_load_pd(&B[j+k*n]);
__m128d dotpr = _mm_add_pd(aTij, bjk);
cik = _mm_add_pd(cik, dotpr);
}
_mm_store_pd(&C[i+k*n], cik);
}
}
printf("C[15]: %f\n", C[15]);
printf("C[14]: %f\n", C[14]);
这给出了分段错误,因为n是奇数。现在将n = 15更改为n = 16,一切都将按预期运行。因此,将数组填充为偶数(甚至更好,填充到高速缓存行的大小 - > 64字节== 8个DP元素或16个SP元素)将防止此类问题,并将导致更好的性能。
答案 1 :(得分:0)
即使使用__attribute__((aligned(32)))
,我也会遇到同样的错误(错误率为50%)。然后我使用以下函数来获得%100的对齐几率(a应该是2的幂):
void * malloc_float_align(size_t n, unsigned int a/*alignment*/, float *& output)
{
void * adres=NULL;
void * adres2=NULL;
adres=malloc(n*sizeof(float)+a);
size_t adr=(size_t)adres;
size_t adr2=adr+a-(adr&(a-1u)); // a valid address for a alignment
adres2=(void * ) adr2;
output=(float *)adres2;
return adres; //pointer to be used in free()
}
然后在main中使用:
int main()
{
float * res=NULL;
void * origin=malloc_float_align(1024,32u,res);
//use res for sse/avx
free(origin); // actual allocation is more than 1024 elements
return 0;
}
当然这是用c ++编写的,所以你只需改变一些函数参数样式就可以了。
答案 2 :(得分:0)
一个简单的技巧是执行ASSERT并查看它是否触发:
ASSERT( ((size_t)(&C_buff[i+k*n]) & 0xF) == 0);
当地址未与SSE对齐时,ASSERT将触发。 默认情况下,64位构建应提供16B对齐。 如果您计划使用32位代码,请使用上述align_malloc函数之一。 您需要使用相关的align_free,否则您将崩溃。