- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
如果您有一个输入数组和一个输出数组,但是只想编写通过某个条件的那些元素,那么在AVX2中执行此操作的最有效方法是什么?
我在SSE中看到过这样做的地方:
(来自:https://deplinenoise.files.wordpress.com/2015/03/gdc2015_afredriksson_simd.pdf)
__m128i LeftPack_SSSE3(__m128 mask, __m128 val)
{
// Move 4 sign bits of mask to 4-bit integer value.
int mask = _mm_movemask_ps(mask);
// Select shuffle control data
__m128i shuf_ctrl = _mm_load_si128(&shufmasks[mask]);
// Permute to move valid values to front of SIMD register
__m128i packed = _mm_shuffle_epi8(_mm_castps_si128(val), shuf_ctrl);
return packed;
}
最佳答案
AVX2 + BMI2。查看我对AVX512的其他答案。 (更新:在64位版本中保存了pdep
。)
我们可以使用AVX2 vpermps
(_mm256_permutevar8x32_ps
)(或等效的整数vpermd
)来进行车道交叉变量改组。
由于BMI2 pext
(Parallel Bits Extract)为我们提供了所需操作的按位版本,因此我们可以动态生成掩码。
注意在AMD CPU上pdep
/ pext
的运行速度非常慢,例如Ryzen上的6微秒/ 18个周期的延迟和吞吐量。此实现将在AMD上可怕地执行。对于AMD,如果掩码输入是矢量掩码(而不是已经计算出的位掩码),则最好使用使用pshufb
或vpermilps
LUT的128位向量,或者注释中讨论的一些AVX2可变移位建议。从记忆里)。在Zen2之前的AMD仍然只有128位向量执行单元,而256位交叉通道改组很慢。因此,在当前的AMD上,128位向量对此非常有吸引力。
对于具有32位或更大元素的整数向量:1)_mm256_movemask_ps(_mm256_castsi256_ps(compare_mask))
。
或2)使用_mm256_movemask_epi8
,然后将第一个PDEP常数从0x0101010101010101更改为0x0F0F0F0F0F0F0F0F0F,以分散4个连续位的块。将乘以0xFFU的乘数更改为expanded_mask |= expanded_mask<<4;
或expanded_mask *= 0x11;
(未测试)。无论哪种方式,都将改组掩码与VPERMD一起使用,而不要与VPERMPS一起使用。
对于64位整数或double
元素,一切仍然有效。比较掩码恰好总是具有成对的32位元素对,因此所产生的混洗将每个64位元素的两半放在正确的位置。 (因此,您仍然使用VPERMPS或VPERMD,因为VPERMPD和VPERMQ仅可用于立即控制操作数。)
对于16位元素,您可能可以使用128位向量对其进行调整。
算法:
从一个压缩的3位索引常量开始,每个位置都有自己的索引。即[ 7 6 5 4 3 2 1 0 ]
,其中每个元素为3位宽。 0b111'110'101'...'010'001'000
。
使用pext
将所需的索引提取到整数寄存器底部的连续序列中。例如如果我们想要索引0和2,则pext
的控制掩码应为0b000'...'111'000'111
。 pext
将获取与选择器中的1位对齐的010
和000
索引组。选定的组被打包到输出的低位,因此输出将为0b000'...'010'000
。 (即[ ... 2 0 ]
)
有关如何从输入矢量掩码为0b111000111
生成pext
输入的信息,请参见注释的代码。
现在,我们与压缩LUT处于同一条船上:解压缩多达8个压缩索引。
当您将所有片段放在一起时,总共有三个pext
/ pdep
。我从想要的工作中倒退了,因此也可能最容易朝那个方向理解它。 (即从洗牌线开始,然后从那里开始向后工作。)
如果我们使用每个字节一个索引而不是打包的3位组,则可以简化解压缩。由于我们有8个索引,因此只有64位代码才有可能。
请参见this and a 32bit-only version on the Godbolt Compiler Explorer。我使用了#ifdef
,因此可以使用-m64
或-m32
进行最佳编译。 gcc浪费了一些指令,但是clang编写了非常不错的代码。
#include <stdint.h>
#include <immintrin.h>
// Uses 64bit pdep / pext to save a step in unpacking.
__m256 compress256(__m256 src, unsigned int mask /* from movmskps */)
{
uint64_t expanded_mask = _pdep_u64(mask, 0x0101010101010101); // unpack each bit to a byte
expanded_mask *= 0xFF; // mask |= mask<<1 | mask<<2 | ... | mask<<7;
// ABC... -> AAAAAAAABBBBBBBBCCCCCCCC...: replicate each bit to fill its byte
const uint64_t identity_indices = 0x0706050403020100; // the identity shuffle for vpermps, packed to one index per byte
uint64_t wanted_indices = _pext_u64(identity_indices, expanded_mask);
__m128i bytevec = _mm_cvtsi64_si128(wanted_indices);
__m256i shufmask = _mm256_cvtepu8_epi32(bytevec);
return _mm256_permutevar8x32_ps(src, shufmask);
}
# clang 3.7.1 -std=gnu++14 -O3 -march=haswell
mov eax, edi # just to zero extend: goes away when inlining
movabs rcx, 72340172838076673 # The constants are hoisted after inlining into a loop
pdep rax, rax, rcx # ABC -> 0000000A0000000B....
imul rax, rax, 255 # 0000000A0000000B.. -> AAAAAAAABBBBBBBB..
movabs rcx, 506097522914230528
pext rax, rcx, rax
vmovq xmm1, rax
vpmovzxbd ymm1, xmm1 # 3c latency since this is lane-crossing
vpermps ymm0, ymm1, ymm0
ret
-fno-unroll-loops
使其更易于阅读。如果幸运的话,它可能每4c维持一次迭代,与前端保持同步,但是如果我很幸运,认为clang无法解释
popcnt
对输出的错误依赖,因此它将成为
compress256
函数延迟的3/5的瓶颈。)
sub
用多条指令乘以0xFF。这需要一条额外的
mov
指令,但是最终结果是一个乘积,延迟为2。(Haswell在寄存器重命名阶段以零延迟处理
mov
。)
movmskps
,因此可以弹出掩码并将其用作LUT索引,但是可以保存pdep / imul / pexp。
set1()
/
vpsrlvd
/
vpand
可能更好。地点。 (32位广播负载在Intel CPU上不需要ALU uop)。但是,在Haswell上,可变移位是3 oups(在Skylake上只有1 uops)。
关于c++ - AVX2是最有效的基于 mask 包装的方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38985793/
我正在尝试优化一些矩阵计算,我想知道是否可以在编译时检测 SSE/SSE2/AVX/AVX2/AVX-512/AVX-128-FMA/KCVI[ 1] 是否由编译器启用?非常适合 GCC 和 Clan
我想仅使用avx而不是avx2来实现64位转置操作。它应该这样做: // in = Hh Hl Lh Ll // | X | // out = Hh Lh Hl Ll 这就是使
如果我有一个 AVX 寄存器,里面有 4 个 double 值,我想将它的反向存储在另一个寄存器中,是否可以用一个内部命令来实现? 例如:如果我在 SSE 寄存器中有 4 个 float ,我可以使用
最初我试图重现 Agner Fog 的微体系结构指南部分“YMM 和 ZMM 向量指令的预热期”中描述的效果,它说: The processor turns off the upper parts o
我的 C++ 代码使用 SSE,现在我想改进它以支持 AVX(当它可用时)。因此,我检测 AVX 何时可用并调用使用 AVX 命令的函数。我使用 Win7 SP1 + VS2010 SP1 和带有 A
我有一大块内存,比如说 256 KiB 或更长。我想计算整个 block 中 1 位的数量,或者换句话说:将所有字节的“总体计数”值相加。 我知道 AVX-512 有一个 VPOPCNTDQ inst
有多快 tensorflow-gpu与没有 AVX 和 AVX2 相比,有 AVX 和 AVX2 吗? 我试图使用谷歌找到答案,但没有成功。很难重新编译tensorflow-gpu对于 Windows
为什么avx sqrt(非压缩)指令有三个操作数? vsqrtsd xmm1, xmm2, xmm3 这是否意味着类似于 xmm1=xmm2=sqrt(xmm3)? 编辑:下面的详细答案但总之流水线的
我正在研究Intel intrinsics guide的展开和压缩操作。我对这两个概念感到困惑: 对于__m128d _mm_mask_expand_pd (__m128d src, __mmask8
我在 Intel Intrinsic Guide v2.7 中找不到它们。您知道 AVX 或 AVX2 指令集是否支持它们吗? 最佳答案 原始 AVX 指令集中没有分散或收集指令。 AVX2 添加了收
我正在尝试将函数转换为 AVX 版本。函数本身基本上只是比较浮点数并返回真/假取决于计算。 这是原始函数: bool testSingle(float* thisFloat, float* other
我遇到了 AVX 内部指令 _mm256_testc_pd() 的一个非常奇怪的行为。在这里你可以看到这个功能的描述 https://software.intel.com/sites/landingp
我有一个 256 位 AVX 寄存器,其中包含 4 个单精度复数,存储为实数、虚数、实数、虚数等。我目前正在将整个 256 位寄存器写回内存并在那里求和,但这似乎效率低下. 如何使用 AVX(或 AV
#include "stdio.h" #include "math.h" #include "stdlib.h" #include "x86intrin.h" void dd_m(double *cl
有没有办法对 AVX 寄存器进行水平异或——特别是对 256 位寄存器的四个 64 位组件进行异或? 目标是获得 AVX 寄存器的所有 4 个 64 位组件的异或。它本质上与水平添加( _mm256_
当我尝试使用 AVX 获取数据时,出现运行时错误 - 段错误: int i = 0; const int sz = 9; size_t *src1 = (size_t *)_mm_malloc(sz*
当我尝试使用 AVX 展开最简单的循环时,出现运行时错误 - 段错误: const int sz = 9; float *src = (float *)_mm_malloc(sz*
我想将两个 256 位 vector (__m256d) 合并为一个 256位 vector ,通过省略每个 64 位 double 的上半部分。 所以,如果在下面,a_i, b_i, ... 是 3
我测试了以下简单的功能 void mul(double *a, double *b) { for (int i = 0; i #include #include #include #defi
_mm_i32gather_epi32() 的当前英特尔内在函数指南将每个子词的计算地址描述为: addr := base_addr + SignExtend64(vindex[m+31:m]) *
我是一名优秀的程序员,十分优秀!