不幸的是我有一个AMD打桩机cpu,它似乎与AVX指令有问题:
使用256位AVX寄存器进行存储器写操作非常慢。测量的吞吐量比之前的型号(Bulldozer)慢5到6倍,比两个128位写入慢8到9倍。
根据我自己的经验,我发现mm256内在函数比mm128要慢得多,我假设是因为上述原因。
我真的想为最新的指令集AVX编写代码,同时仍然能够以合理的速度在我的机器上测试构建。有没有办法迫使mm256内在函数使用SSE指令?我正在使用VS 2015。
如果没有简单的方法,那么艰难的方式呢。将<immintrin.h>
替换为包含我自己的内在函数定义的自定义标题,可以对其进行编码以使用SSE?不确定这是多么合理,在我完成这项工作之前,如果可能的话,更喜欢更简单的方法。
答案 0 :(得分:6)
使用Agner Fog的Vector Class Library并将其添加到Visual Studio中的命令行:-D__SSE4_2__ -D__XOP__
。
然后使用AVX大小的矢量,例如Vec8f
八个浮点数。在没有AVX启用的情况下进行编译时,它将使用文件vectorf256e.h
来模拟具有两个SSE寄存器的AVX。例如,Vec8f
继承自Vec256fe
,其开头如下:
class Vec256fe {
protected:
__m128 y0; // low half
__m128 y1; // high half
如果使用/arch:AVX -D__XOP__
进行编译,则VCL将改为使用文件vectorf256.h
和一个AVX寄存器。然后你的代码适用于AVX和SSE,只需更改编译器开关。
如果您不想使用XOP
,请不要使用-D__XOP__
。
正如Peter Cordes在他的回答中所指出的,如果你的目标只是为了避免256位加载/存储,那么你可能仍然需要VEX编码指令(虽然不清楚除了某些特殊情况外这会有所不同) 。您可以使用像
这样的矢量类来实现Vec8f a;
Vec4f lo = a.get_low(); // a is a Vec8f type
Vec4f hi = a.get_high();
lo.store(&b[0]); // b is a float array
hi.store(&b[4]);
然后使用/arch:AVX -D__XOP__
进行编译。
另一种选择是使用Vecnf
然后执行
//foo.cpp
#include "vectorclass.h"
#if SIMDWIDTH == 4
typedef Vec4f Vecnf;
#else
typedef Vec8f Vecnf;
#endif
并像这样编译
cl /O2 /DSIMDWIDTH=4 foo.cpp /Fofoo_sse
cl /O2 /DSIMDWIDTH=4 /arch:AVX /D__XOP__ foo.cpp /Fofoo_avx128
cl /O2 /DSIMDWIDTH=8 /arch:AVX foo.cpp /Fofoo_avx256
这将创建三个带有一个源文件的可执行文件。您可以使用/c
编译它们而不是链接它们,而是创建一个CPU调度程序。我使用了XOP
和avx128,因为除了AMD之外,我认为没有充分的理由使用avx128。
答案 1 :(得分:3)
您不想使用SSE说明。你想要的是256b商店作为两个独立的128b商店,仍然使用VEX编码的128b指令。即128b AVX vmovups
。
gcc有-mavx256-split-unaligned-load
和...-store
个选项(例如-march=sandybridge
的一部分启用),大概也适用于推土机系列(-march=bdver2
是打桩机)。当编译器知道 对齐的内存时,解决问题。
您可以使用像
这样的宏覆盖正常的256b存储内在函数// maybe enable this for all BD family CPUs?
#if defined(__bdver2) | defined(PILEDRIVER) | defined(SPLIT_256b_STORES)
#define _mm256_storeu_ps(addr, data) do{ \
_mm_storeu_ps( ((float*)(addr)) + 0, _mm256_extractf128_ps((data),0)); \
_mm_storeu_ps( ((float*)(addr)) + 4, _mm256_extractf128_ps((data),1)); \
}while(0)
#endif
gcc为Piledriver(__bdver2
)定义-march=bdver2
(Bulldozer版本2)。
您可以对(对齐)_mm256_store_ps
执行相同的操作,或者只使用未对齐的内在函数。
编译器将_mm256_extractf128(data,0)
优化为简单的强制转换。即它应该只编译到
vmovups [rdi], xmm0 ; if data is in xmm0 and addr is in rdi
vextractf128 [rdi+16], xmm0, 1
然而,testing on godbolt shows that gcc and clang are dumb,并提取到寄存器和然后存储。 ICC正确生成双指令序列。