我必须将YMM寄存器中的低3个double存储到大小为3的未对齐double数组中(也就是说,无法写入第4个元素)。但是有点顽皮,我想知道AVX内部_mm256_storeu2_m128d
是否可以解决问题。我有
reg = _mm256_permute4x64_pd(reg, 0b10010100); // [0 1 1 2]
_mm256_storeu2_m128d(vec, vec + 1, reg);
并通过clang编译给出
vmovupd xmmword ptr [rsi + 8], xmm1 # reg in ymm1 after perm
vextractf128 xmmword ptr [rsi], ymm0, 1
如果storeu2
具有类似于memcpy
的语义,那么它无疑会触发未定义的行为。但是使用生成的指令,这将不会出现比赛条件(或其他潜在问题)吗?
也欢迎将YMM存储到3号数组中的其他方法。
除了英特尔作为文档发布的内容外,实际上没有针对英特尔内部函数AFAIK的正式规范。例如他们的内在指导。还有白皮书中的示例等;例如需要工作的示例是GCC / c知道必须使用__m128
定义__attribute__((may_alias))
的一种方式。
全部都在一个线程中,完全同步,因此绝对没有“竞争条件”。在您的情况下,存储的顺序无关紧要(假设它们不与__m256d reg
对象本身重叠!这等效于重叠的memcpy问题。)您正在做的事情可能像两个indeterminately sequenced内存到重叠的目的地:它们肯定以一种顺序或另一种顺序发生,并且编译器可以选择两者之一。
存储顺序的可观察到的区别是性能:如果您想在之后不久进行SIMD重装,则如果16字节重装从一个16字节存储中获取其数据,而不是与之重叠,则存储转发将更好地工作。两家商店。
但是,一般来说,重叠的商店可以提高性能;存储缓冲区将吸收它们。但是,这意味着其中之一是未对齐的,并且越过缓存行边界会更加昂贵。
但是,仅此而已:Intel's intrinsics guide does list an "operation" section for that compound intrinsic:
操作
MEM[loaddr+127:loaddr] := a[127:0] MEM[hiaddr+127:hiaddr] := a[255:128]
因此,它严格地被定义为首先是低地址存储(第二个arg;我认为您倒过来了)。
您的路线要花费1个过路点洗牌+ vmovups + vextractf128 [mem], ymm, 1
。根据其编译方式,这两个存储区都必须在洗牌之后才能开始。 (尽管看起来像是clang可能避免了该问题)。
在Intel CPU上,vextractf128 [mem], ymm, imm
的前端成本为2 uops,not
在Zen 2之前的AMD CPU上,交叉道洗牌超过1 uop,因此_mm256_permute4x64_pd
的价格比必要的昂贵。
您只想存储输入向量的低通道和高通道的低元素
。最便宜的随机播放是Zen上的vextractf128 xmm, ymm, 1
-1 uop / 1c延迟(无论如何,它将YMM向量分成两个128位的一半)。它与Intel上的任何其他跨界洗牌一样便宜。您希望编译器生成的asm可能就是这个,它仅需要AVX1。 AVX2对此没有任何有用的说明。
vextractf128 xmm1, ymm0, 1 ; single uop everywhere
vmovupd [rdi], xmm0 ; single uop everywhere
vmovsd [rdi+2*8], xmm1 ; single uop everywhere
所以您需要这样的东西,应该可以有效地编译。
_mm_store_pd(vec, _mm256_castpd256_pd128(reg)); // low half
__m128d hi = _mm256_extractf128_pd(reg, 1);
_mm_store_sd(vec+2, hi);
// or vec[2] = _mm_cvtsd_f64(hi);
[vmovlps
(_mm_storel_pi
)也可以,但是使用AVX VEX编码不会节省任何代码大小,并且需要更多的转换才能使编译器满意。
不幸的是,没有vpextractq [mem], ymm
,只有XMM源,因此无济于事。
如评论中所讨论,是的,您可以执行vmaskmovps
,但是不幸的是,它效率不如我们在所有CPU上所希望的那样。在AVX512使掩盖的负载/存储成为一等公民之前,最好洗牌并存储2个存储。或者填充您的数组/结构,以便您至少可以暂时踩到以后的内容。
Zen具有2 uop vmaskmovpd ymm
负载,但非常
vmaskmovpd
存储(42 uop,YMM每11个周期1个)。或Zen +和Zen2为18或19 uops,6个周期的吞吐量。 如果您完全关心Zen,请避免使用vmaskmov
。[在Intel Broadwell和更早的版本上,根据vmaskmov
测试,Agner's Fog's存储为4 oups,因此融合域uop比我们从shuffle + movups + movsd得到的多1。但是,尽管如此,Haswell和更高版本仍然管理1时钟吞吐量,因此,如果这是一个瓶颈,那么它将超过2家商店的2周期吞吐量。对于256位存储,即使没有屏蔽,SnB / IvB当然也需要2个周期。
[在Skylake上,vmaskmov mem, ymm, ymm
is only 3 uops(Agner Fog列出了4,但是他的电子表格是手动编辑的,以前是错误的。我认为可以假定uops.info的自动测试是正确的。这很有意义;这很有意义; Skylake-client是基本与Skylake-AVX512相同的内核,只是没有实际启用AVX512。因此,他们可以通过将vmaskmov mem, ymm, ymm
解码为测试到掩码寄存器(1 uop)+掩码存储(另外2 oups,无需微融合)来实现。
因此,如果您只关心Skylake及更高版本,并且可以分摊将掩码加载到矢量寄存器中的成本(可重用于加载和存储,则vmaskmovpd
实际上是相当不错的。]
vmaskmovpd
甚至可以有效地存储到屏蔽的重新加载;我认为英特尔在其优化手册中提到了一些相关内容。