根据英特尔软件开发人员手册(第14.9节),AVX放宽了内存访问的对齐要求.如果数据直接加载到处理指令中,例如
vaddps ymm0,ymm0,YMMWORD PTR [rax]
Run Code Online (Sandbox Code Playgroud)
加载地址不必对齐.但是,如果使用专用的对齐加载指令,例如
vmovaps ymm0,YMMWORD PTR [rax]
Run Code Online (Sandbox Code Playgroud)
必须对齐加载地址(为32的倍数),否则会引发异常.
令我困惑的是内在函数的自动代码生成,在我的例子中是gcc/g ++(4.6.3,Linux).请查看以下测试代码:
#include <x86intrin.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define SIZE (1L << 26)
#define OFFSET 1
int main() {
float *data;
assert(!posix_memalign((void**)&data, 32, SIZE*sizeof(float)));
for (unsigned i = 0; i < SIZE; i++) data[i] = drand48();
float res[8] __attribute__ ((aligned(32)));
__m256 sum = _mm256_setzero_ps(), elem;
for (float *d = data + OFFSET; d < data + SIZE - 8; d += 8) {
elem = _mm256_load_ps(d);
// …Run Code Online (Sandbox Code Playgroud) 我正在生成sse/avx指令,目前我必须使用未对齐的加载和存储.我在浮点/双数组上操作,我永远不知道它是否会对齐.所以在矢量化它之前,我希望有一个pre和可能的post循环,它关注未对齐的部分.然后,主矢量化循环在对齐的部分上操作.
但是我如何确定阵列何时对齐?我可以查看指针值吗?应该何时预循环停止和循环后启动?
这是我的简单代码示例:
void func(double * in, double * out, unsigned int size){
for( as long as in unaligned part ){
out[i] = do_something_with_array(in[i])
}
for( as long as aligned ){
awesome avx code that loads operates and stores 4 doubles
}
for( remaining part of array ){
out[i] = do_something_with_array(in[i])
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:我一直在考虑它.从理论上讲,指向第i个元素的指针应该是可分的(类似于&a [i]%16 == 0)2,4,16,32(取决于它是否是双倍以及它是sse还是avx).所以第一个循环应该掩盖不可分割的元素.
实际上我将尝试编译器编译指示和标志输出,以查看编译器产生了什么.如果没有人给出一个好的答案,我会在周末发布我的解决方案(如果有的话).
我对OpenMP中的新对齐选项有疑问.这是在使用它的背景下#pragma omp simd aligned(a:n)
假设我有一个整数数组,我使用posix_memalign分配,所以我知道数组开始时让我们说32字节边界.现在让我说我想对该数组中的每个值进行平方.我能说......么...
int* array = { some array of length len aligned to 32 bytes };
#pragma omp simd aligned(array:32)
for(int i = 0; i < len; i++)
array[i] *= array[i];
Run Code Online (Sandbox Code Playgroud)
这是一个安全的假设吗?或者对齐也暗示我在数组中使用的大小数据类型(int)是32个字节的倍数?有点像gcc中的属性((aligned(32))将使宽度类型至少为32个字节.
为什么以下代码会导致未对齐的AVX指令(MOVUPD而不是MOVAPD)?我在Visual Studio 2015上编译了这个.如何告诉编译器我的数据确实是对齐的?
const size_t ALIGN_SIZE = 64;
const size_t ARRAY_SIZE = 1024;
double __declspec(align(ALIGN_SIZE)) a[ARRAY_SIZE];
double __declspec(align(ALIGN_SIZE)) b[ARRAY_SIZE];
//Calculate the dotproduct
__m256d ymm0 = _mm256_set1_pd(0.0);
for (int i = 0; i < ARRAY_SIZE; i += 8)
{
__m256d ymm1 = _mm256_load_pd(a + i);
__m256d ymm2 = _mm256_load_pd(b + i);
__m256d ymm3 = _mm256_mul_pd(ymm1, ymm2);
ymm0 = _mm256_add_pd(ymm3, ymm0);
__m256d ymm4 = _mm256_load_pd(a + i + 4);
__m256d ymm5 = _mm256_load_pd(b + i + 4);
__m256d ymm6 = _mm256_mul_pd(ymm4, ymm5); …Run Code Online (Sandbox Code Playgroud)