我正在尝试使用Streaming SIMD Extensions重写光线跟踪器。我的原始光线跟踪器使用内联汇编和 movups 指令将数据加载到xmm寄存器中。我已经读过编译器内在函数并不比内联汇编慢得多(我怀疑我甚至可以通过避免未对齐的内存访问来获得速度),而且更加可移植,所以我试图迁移我的SSE代码以使用xmmintrin.h中的内部函数。受影响的主要类是vector,它看起来像这样:
#include "xmmintrin.h"
union vector {
__m128 simd;
float raw[4];
//some constructors
//a bunch of functions and operators
} __attribute__ ((aligned (16)));
我之前已经读过g ++编译器会自动地沿着内存边界分配结构,这些结构等于最大成员变量的大小,但是这似乎没有发生,并且对齐的属性没有帮助。我的研究表明,这可能是因为我在堆栈上分配了一大堆函数局部向量,并且在x86中无法保证堆栈上的对齐。有没有办法强制这种对齐?我应该提一下,这是在32位机器上的本机x86 Linux下运行,而不是Cygwin。我打算在此应用程序中进一步实现多线程,因此将违规的矢量实例声明为静态不是一种选择。如果需要,我愿意增加矢量数据结构的大小。
答案 0 :(得分:6)
最简单的方法是std::aligned_storage
,它将对齐作为第二个参数。
如果您还没有,可以查看Boost's version。
然后你可以建立你的联盟:
union vector {
__m128 simd;
std::aligned_storage<16, 16> alignment_only;
}
最后,如果它不起作用,你总是可以创建自己的小班:
template <typename Type, intptr_t Align> // Align must be a power of 2
class RawStorage
{
public:
Type* operator->() {
return reinterpret_cast<Type const*>(aligned());
}
Type const* operator->() const {
return reinterpret_cast<Type const*>(aligned());
}
Type& operator*() { return *(operator->()); }
Type const& operator*() const { return *(operator->()); }
private:
unsigned char* aligned() {
if (data & ~(Align-1) == data) { return data; }
return (data + Align) & ~(Align-1);
}
unsigned char data[sizeof(Type) + Align - 1];
};
它将分配比所需更多的存储空间,但这种方式可以保证对齐。
int main(int argc, char* argv[])
{
RawStorage<__m128, 16> simd;
*simd = /* ... */;
return 0;
}
幸运的是,如果编译器检测到对齐是正确的,那么编译器可能能够优化掉指针对齐的东西。
答案 1 :(得分:3)
几个星期前,我在大学期间重新编写了一个旧的光线跟踪任务,将其更新为在64位Linux上运行并使用SIMD指令。 (旧版本偶然在DOS下运行486,让你知道我上次做什么用的时候)。
很可能有更好的方法,但这就是我做的......
typedef float v4f_t __attribute__((vector_size (16)));
class Vector {
...
union {
v4f_t simd;
float f[4];
} __attribute__ ((aligned (16)));
...
};
反汇编我编译的二进制文件显示它确实正在使用 movaps 指令。
希望这有帮助。
答案 2 :(得分:2)
通常你需要的只是:
union vector {
__m128 simd;
float raw[4];
};
即。联盟本身不需要额外的__attribute__ ((aligned (16)))
。
这在我使用过的几乎所有编译器上都有预期的效果,当天回到gcc 2.95.2是个明显的例外,在某些情况下用于搞乱堆栈对齐。
答案 3 :(得分:1)
我一直使用__m128
使用此联合技巧,它适用于Mac上的GCC和Windows上的Visual C ++,因此这必须是您使用的编译器中的错误。
其他答案包含了很好的解决方法。
答案 4 :(得分:0)
如果需要N个这些对象的数组,请分配vector raw[N+1]
,并使用vector* const array = reinterpret_cast<vector*>(reinterpret_cast<intptr_t>(raw+1) & ~15)
作为数组的基址。这将永远是一致的。