intel avx2 中是否有 movemask 指令的逆指令?

2024-02-27

movemask 指令采用 __m256i 并返回 int32,其中每个位(前 4、8 或所有 32 位,具体取决于输入向量元素类型)是相应向量元素的最高有效位。

我想做相反的事情:取 32 (其中只有 4、8 或 32 个最低有效位有意义),并获得 __m256i,其中每个 int8、int32 或 int64 大小的块的最高有效位设置为原始值少量。

基本上,我想从压缩位掩码转换为可被其他 AVX2 指令(例如 maskstore、maskload、mask_gather)用作掩码的位掩码。

我无法快速找到执行此操作的指令,所以我在这里询问。 如果没有一条指令具有该功能,您是否可以想到一种巧妙的技巧,可以用很少的指令实现这一目标?

我当前的方法是使用 256 个元素的查找表。 我想在没有发生太多其他事情的循环中使用此操作,以加快速度。请注意,我对实现此操作的长多指令序列或小循环不太感兴趣。


AVX2 或更早版本中没有单一指令。 (AVX512可以直接使用位图形式的掩码,and有一条将掩码扩展为向量的指令)。

  • 4 位 -> 4 个 qword在 YMM 寄存器中:这个答案:LUT 很好,ALU 也很好
  • 8 位 -> 8 个双字在 YMM 寄存器中:这个答案(或这个没有 AVX2 https://stackoverflow.com/questions/48811369/how-to-use-bits-in-a-byte-to-set-dwords-in-ymm-register-without-avx2-inverse-o). ALU.
  • 16 位 -> 16 个字:这个答案与vpbroadcastw / vpand / vpcmpeqw
  • 32 位 -> 32 字节:
    如何执行 _mm256_movemask_epi8 (VPMOVMSKB) 的逆运算? https://stackoverflow.com/questions/21622212/how-to-perform-the-inverse-of-mm256-movemask-epi8-vpmovmskb
    Also 将 32 位解压缩为 32 字节 SIMD 向量的最快方法 https://stackoverflow.com/questions/24225786/fastest-way-to-unpack-32-bits-to-a-32-byte-simd-vector.

  • 8 位 -> 8 个字节或字(无 AVX2):如何使用 x86 SIMD 将 8 位位图高效转换为 0/1 整数数组 https://stackoverflow.com/questions/52098873/how-to-efficiently-convert-an-8-bit-bitmap-to-array-of-0-1-integers-with-x86-sim/52105856#52105856尽管没有 SSSE3 的掩码的 8 位或 16 位广播可能会花费多次洗牌,但相当便宜。

    注意使用技巧_mm_min_epu8(v, _mm_set1_epi8(1))
    instead of _mm_cmpeq_epi8 得到 0/1而不是 0/FF。

  • 16 位 -> 16 个字节(带 SSE2 或 SSSE3),或AVX-512: 将 16 位掩码转换为 16 字节掩码 https://stackoverflow.com/questions/67201469/convert-16-bits-mask-to-16-bytes-mask/67203617#67203617.
    (还有 BMI2 为unsigned __int128、纯 C++ 乘法 bithack 和获取 0/1 而不是 0/-1 的 AVX-512 示例)

  • 8 位 -> 8 字节:如果您一次只需要 8 位,标量乘法技巧可能会更好:如何从 8 个布尔值中创建一个字节(反之亦然)? https://stackoverflow.com/questions/8461126/how-to-create-a-byte-out-of-8-bool-values-and-vice-versa/51750902#51750902.


对于您的情况,如果您从内存加载位图,则将其直接加载到 ALU 策略的向量寄存器中应该可以很好地工作,即使对于 4 位掩码也是如此。

如果您将位图作为计算结果,那么它将位于整数寄存器中,您可以轻松地将其用作 LUT 索引,因此如果您的目标是 64 位元素,那么这是一个不错的选择。否则,可能仍然会使用 ALU 来处理 32 位或更小的元素,而不是使用巨大的 LUT 或执行多个块。


在从整数位掩码到矢量掩码的廉价转换成为可能之前,我们必须等待 AVX-512 的掩码寄存器。 (和kmovw k1, r/m16,编译器隐式生成int => __mmask16)。有一个 AVX512 insn 用于从掩码设置向量(VPMOVM2D zmm1, k1, ,以及不同元素大小的其他版本),但您通常不需要它,因为过去使用掩码向量的所有内容现在都使用掩码寄存器。也许如果您想计算满足某些比较条件的元素? (你会在哪里使用pcmpeqd / psubd生成并累加 0 或 -1 个元素的向量)。但标量popcnt在面具结果上会是一个更好的选择。

但请注意vpmovm2d要求面罩位于 AVX512 中k0..7掩码寄存器。获取它需要额外的指令,除非它来自向量比较结果,并且移动到掩码寄存器的指令需要 Intel Skylake-X 和类似 CPU 上端口 5 的微指令,因此这可能是一个瓶颈(特别是如果您进行任何洗牌) )。特别是如果它在内存中启动(加载位图)并且您只需要每个元素的高位,那么即使 256 位和 512 位 AVX512 指令可用,您也可能最好使用广播加载 + 变量移位。

也可能(对于 0/1 结果而不是 0/-1)是来自常量的零屏蔽负载,例如_mm_maskz_mov_epi8(mask16, _mm_set1_epi8(1)). https://godbolt.org/z/1sM8hY8Tj https://godbolt.org/z/1sM8hY8Tj


对于 64 位元素,掩码只有 4 位,因此查找表是合理的。您可以通过加载来压缩 LUTVPMOVSXBQ ymm1, xmm2/m32. (_mm256_cvtepi8_epi64) http://felixcloutier.com/x86/PMOVSX.html。这将为您提供 (1pmovsx不方便用作具有内在函数的窄负载.

特别是如果您已经将位图存储在整数寄存器(而不是内存)中,则vpmovsxbqLUT 在 64 位元素的内部循环中应该表现出色。或者,如果指令吞吐量或洗牌吞吐量是瓶颈,请使用未压缩的 LUT。这可以让您(或编译器)使用掩码向量作为其他内容的内存操作数,而不需要单独的指令来加载它。


32 位元素的 LUT:可能不是最佳的,但您可以这样做

对于 32 位元素,8 位掩码可为您提供 256 个可能的向量,每个向量有 8 个元素长。 256 * 8B = 2048 字节,即使对于压缩版本来说,这也是相当大的缓存占用空间(加载vpmovsxbd ymm, m64).

要解决此问题,您可以将 LUT 拆分为 4 位块。大约需要 3 条整数指令才能将一个 8 位整数拆分为两个 4 位整数(mov/and/shr)。然后使用 128b 向量的未压缩 LUT(对于 32 位元素大小),vmovdqa低半部分和vinserti128高的一半。您仍然可以压缩 LUT,但我不会推荐它,因为您需要vmovd / vpinsrd / vpmovsxbd,这是 2 次洗牌(因此您可能会成为 uop 吞吐量的瓶颈)。

Or 2x vpmovsxbd xmm, [lut + rsi*4] + vinserti128英特尔的情况可能更糟。


ALU 替代方案:适用于 16/32/64 位元素

当整个位图适合每个元素时:广播它,并使用选择器掩码,并针对同一常量(可以在循环中多次使用此常量时保留在寄存器中)。

vpbroadcastd  ymm0,  dword [mask]            ; _mm256_set1_epi32
vpand         ymm0, ymm0,  setr_epi32(1<<0, 1<<1, 1<<2, 1<<3, ..., 1<<7)
vpcmpeqd      ymm0, ymm0,  [same constant]   ; _mm256_cmpeq_epi32
      ; ymm0 =  (mask & bit) == bit
      ; where bit = 1<<element_number

掩码可以来自带有 vmovd + vpbroadcastd 的整数寄存器,但是如果广播负载已经在内存中,那么广播负载就很便宜,例如从掩码数组应用于元素数组。我们实际上只关心该双字的低 8 位,因为 8x 32 位元素 = 32 个字节。 (例如,您从vmovmaskps)。对于 16x 16 位元素的 16 位掩码,您需要vpbroadcastw。要首先从 16 位整数向量中获取这样的掩码,您可以vpacksswb两个向量在一起(保留每个元素的符号位),vpermq将元素按车道内打包后的顺序排列,然后vpmovmskb.

对于 8 位元素,您需要vpshufb the vpbroadcastd结果将相关位放入每个字节中。看如何执行 _mm256_movemask_epi8 (VPMOVMSKB) 的逆运算? https://stackoverflow.com/questions/21622212/how-to-perform-the-inverse-of-mm256-movemask-epi8-vpmovmskb。但对于 16 位及更宽的元素,元素数量

vpbroadcastd/q甚至不花费任何 ALU 微指令,它是在加载端口中完成的。 (b and w是加载+随机播放)。即使您的掩码打包在一起(32 或 64 位元素的每个字节一个),它可能仍然更有效vpbroadcastd代替vpbroadcastb. The x & mask == maskcheck 不关心广播后每个元素的高字节中的垃圾。唯一担心的是缓存行/页面分割。


如果您只需要符号位,则可变移位(Skylake 更便宜)

变量混合和屏蔽加载/存储仅关心屏蔽元素的符号位。

一旦您将 8 位掩码广播到双字元素,这只是 1 uop(在 Skylake 上)。

vpbroadcastd  ymm0, dword [mask]

vpsllvd       ymm0, ymm0, [vec of 24, 25, 26, 27, 28, 29, 30, 31]  ; high bit of each element = corresponding bit of the mask

;vpsrad        ymm0, ymm0, 31                          ; broadcast the sign bit of each element to the whole element
;vpsllvd + vpsrad has no advantage over vpand / vpcmpeqb, so don't use this if you need all the bits set.

vpbroadcastd与从内存加载一样便宜(Intel CPU 和 Ryzen 上根本没有 ALU uop)。 (更窄的广播,例如vpbroadcastb y,mem在 Intel 上采用 ALU shuffle uop,但在 Ryzen 上可能不行。)

Haswell/Broadwell 上的可变移位稍微昂贵(3 uops,有限的执行端口),但与 Skylake 上的立即计数移位一样便宜! (端口 0 或 1 上有 1 个 uop。)在 Zen 3 之前的 AMD 上,它们不会花费额外的 uop,但速度很慢(3c 延迟和正常移位 uop 吞吐量的 1/4)。在 Zen 1 上,这是特别糟糕的,因为 256 位操作通常以 2 uop 运行。但这并不是一场灾难,特别是如果其他微指令可以在同一端口上使用其他执行单元,同时它们占用额外的周期(如果可能的话,我不知道)。在 Zen 3 及更高版本上,它们的性能与 Skylake 上一样,延迟为 1c,吞吐量为 0.5c。

See the x86 /questions/tagged/x86标记 wiki 以获取性能信息,尤其是Agner Fog 的 insn 表 https://agner.org/optimize/ and https://uops.info/ https://uops.info/.

对于 64 位元素,请注意算术右移仅适用于 16 位和 32 位元素大小。如果您希望将 4 位 -> 64 位元素的整个元素设置为全零/全一,请使用不同的策略。

使用内在函数:

// AVX2, most efficient on Skylake and Zen 3 and later
// if you just need the MSBs set.  Otherwise still use and/cmpeq
__m256i bitmap2vecmask(int m) {
    const __m256i vshift_count = _mm256_set_epi32(24, 25, 26, 27, 28, 29, 30, 31);
    __m256i bcast = _mm256_set1_epi32(m);
    __m256i shifted = _mm256_sllv_epi32(bcast, vshift_count);  // high bit of each element = corresponding bit of the mask
    return shifted;

    // use _mm256_and and _mm256_cmpeq if you need all bits set, not two shifts.
    // would work but not worth it: return _mm256_srai_epi32(shifted, 31);             // broadcast the sign bit to the whole element
}

在循环内部,LUT 可能值得缓存占用空间,具体取决于循环中的指令组合。特别是对于 64 位元素大小,其缓存占用空间不大,但甚至对于 32 位也可能如此。


另一种选择,而不是变量移位,是使用 BMI2 将每个位解压缩为一个字节,并在高位中使用该掩码元素,然后vpmovsx:

; 8bit mask bitmap in eax, constant in rdi

pdep      rax, rax, rdi   ; rdi = 0b1000000010000000... repeating
vmovq     xmm0, rax
vpmovsxbd ymm0, xmm0      ; each element = 0xffffff80 or 0

; optional
;vpsrad    ymm0, ymm0, 8   ; arithmetic shift to get -1 or 0

如果您在整数寄存器中已经有掩码(您必须在其中vmovq / vpbroadcastd无论如何,单独),那么即使在可变计数移位便宜的 Skylake 上,这种方式也可能更好。

如果您的掩码在内存中开始,则另一个 ALU 方法(vpbroadcastd直接进入向量)可能更好,因为广播负载非常便宜。

注意pdepZen 1 和 Zen 2 上有 6 个依赖的 uop(18c 延迟、18c 吞吐量,或更糟,具体取决于位),因此即使您的掩码确实以整数寄存器开始,这种方法在 Ryzen 上也很糟糕。 Zen 3及更高版本有专用pext/pdep硬件并像英特尔一样高效地运行它们,作为单个微指令。

(未来的读者,请随意编辑此内容的内在函数版本。编写 asm 更容易,因为输入量少得多,并且 asm 助记符更易于阅读(不傻)_mm256_到处乱七八糟)。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

intel avx2 中是否有 movemask 指令的逆指令? 的相关文章

  • 使用 CMake 链接 .s 文件

    我有一个我想使用的 c 函数 但它是用Intel编译器而不是gnu C编译器 我在用着cmake构建程序 我实际上正在使用ROS因此rosmake但基础是cmake所以我认为这更多是一个 cmake 问题而不是ROS问题 假设使用构建的文件
  • x86 分页如何工作?

    这个问题旨在填补有关该主题的优质免费信息的真空 我相信一个好的答案将适合一个大的 SO 答案 或者至少适合几个答案 主要目标是为初学者提供足够的信息 以便他们可以自己阅读本手册 并能够理解与分页相关的基本操作系统概念 建议指南 answer
  • 比较堆栈中的两个值? [复制]

    这个问题在这里已经有答案了 我卡住了 在我的汇编代码中 我想比较两个值 堆 x86 语法 AT T cmpl 4 ebp 4 ebp 错误 cmp 的内存引用太多 我认为不可能根据乘数和 ebp 来比较两个值 有什么建议 您可以使用 CMP
  • 如何正确确定Intel处理器的-march和-mtune?

    我目前正在从源代码构建一个对我来说性能至关重要的软件 因此 我想对其进行优化 以便在我的特定 Intel CPU 上运行 构建过程要求我设置 march 和 mtune 标志 如果在我的处理器节点上我使用 gcc march native
  • 早期的BIOS怎么能使用CALL呢?

    我纯粹是出于爱好原因 试图理解 PC 中的一些低级代码 我为随机的旧千兆字节 MB 下载了一个过时的 BIOS ROM 映像 https www gigabyte com Motherboard GA 8I845GE775 G rev 10
  • 跨 AVX 通道的最佳方式是什么?

    有些问题具有类似的标题 但我的问题涉及其他地方未涵盖的一个非常具体的用例 我有 4 个 128d 寄存器 x0 x1 x2 x3 我想将它们的内容重新组合在 5 个 256d 寄存器 y0 y1 y2 y3 y4 中 以准备其他计算 on
  • gcc 如何知道内联汇编中使用的寄存器大小?

    我有内联汇编代码 define read msr index buf asm volatile rdmsr d buf 1 a buf 0 c index 使用该宏的代码 u32 buf 2 read msr 0x173 buf 我发现反汇
  • elf .rel.text 部分中 R_386_32/R_386_PC32 的含义

    为了理解重定位的概念 我编写了一个简单的 chk c 程序 如下所示 1 include
  • C/C++ 中的简单“Hello World”内联汇编语言程序

    我使用 devcpp 和 borland c 编译器 asm mov ax 4 I O Func mov bx 1 Output func mov cx name address of the string mov dx 6 length
  • 为什么当设置为 TLS 选择器时,ES 和 DS 在 64 位内核上最终会归零?

    下面的 32 位程序调用set thread area 2 http linux die net man 2 set thread area在 GDT 中创建一个条目 该条目旨在用于 TLS 通常将结果选择器放入FS or GS并成功使用
  • gcc 编译器开关 (-mavx -mavx2 -mavx512f) 到底有什么作用?

    我在 C C 代码中明确使用了英特尔 SIMD 内在扩展 为了编译代码 我需要在命令行上指定 mavx mavx512 或类似的内容 我对这一切都很满意 然而 从阅读 gcc 手册页来看 并不清楚这些命令行标志是否也告诉 gcc 编译器尝试
  • orpd等SSE2指令有什么意义?

    The orpd指令是 压缩双精度浮点值的按位逻辑或 这不是做完 全相同的事情吗por 按位逻辑或 如果是这样 拥有它还有什么意义呢 请记住 SSE1orps https www felixcloutier com x86 orps首先 实
  • 如何在汇编语言中换行打印多个字符串

    我试图在汇编中的不同行上打印多个字符串 但使用我的代码 它只打印最后一个字符串 我对汇编语言非常陌生 所以请耐心等待 section text global start start mov edx len mov edx len1 mov
  • NASM 轮班操作员

    您将如何在寄存器上进行 NASM 中的位移位 我读了手册 它似乎只提到了这些操作员 gt gt lt lt 当我尝试使用它们时 NASM 抱怨移位运算符处理标量值 您能解释什么是标量值并举例说明如何使用 gt gt and lt lt 另外
  • Visual Studio 2017 上的简单装配程序

    386 model flat c stack 100h printf PROTO arg1 Ptr Byte data msg1 byte Hello World 0Ah 0 code main proc INVOKE printf ADD
  • 是否有一个 x86 内在函数可以生成从内存中的 32 位浮点值到 512 位寄存器的 AVX512 广播操作?

    该指令存在 vbroadcastss zmm m32 但似乎没有内在的东西来生成它 我可以将其编码为 static inline m512 mybroadcast float x m512 v asm inline vbroadcastss
  • Intel 64 和 IA-32 上的 MESI 有何意义

    MESI 的要点是保留共享内存系统的概念 然而 对于存储缓冲区 事情就变得复杂了 一旦数据到达 MESI 实现的缓存 下游内存就会保持一致 然而 在此之前 每个核心可能对内存位置 X 中的内容存在分歧 具体取决于每个核心的本地存储缓冲区中的
  • 如何知道寄存器是否是“通用寄存器”?

    我试图了解寄存器必须具备什么标准才能被称为 通用寄存器 我相信通用寄存器是一个可以用于任何用途的寄存器 用于计算 将数据移入 移出等 并且是一个没有特殊用途的寄存器 现在我读到了ESP寄存器是通用寄存器 我猜是ESP寄存器可以用于任何事情
  • 设置 IRQ 映射

    我正在遵循一些教程和参考文献来尝试设置我的内核 我在教程中遇到了一些不熟悉的代码 但根本没有解释它 这是我被告知映射的代码16 IRQs 0 15 到 ISR 地点32 47 void irq remap void outportb 0x2
  • 为什么 FMA _mm256_fmadd_pd() 内在函数有 3 个 asm 助记符:“vfmadd132pd”、“231”和“213”?

    有人可以向我解释一下为什么融合乘法累加指令有 3 种变体 vfmadd132pd vfmadd231pd and vfmadd213pd 而只有一个 C 内在函数 mm256 fmadd pd 为了简单起见 在 AT T 语法中 有什么区别

随机推荐

  • 如何使用 sbt 跳过 javadoc 依赖项下载

    1 Javadoc 工件往往需要太多的空间和时间来下载 例如 scala library 2 10 2 sources jar是 1 Mb 但是scala library 2 10 2 javadoc jar是 34 Mb 2 大多数情况下
  • Mercurial - 同时比较多个变更集?

    为了比较我们使用 hg diff c
  • 定时器速度问题WPF

    我正在使用计时器来滚动列表 每毫秒间隔我都会启动 Tick 事件 事件虽然动画看起来很慢 repeatTimer Tick this new EventArgs repeatLeftTimer Interval TimeSpan FromM
  • 这些对象字面量有什么区别?

    我创建了两个对象 第一个正在按预期工作 let working constructor function console log working let notworking constructor console log notworki
  • 将代码拆分为多个文件时出现 LNK2019 && LNK1120 错误

    我的代码存储在main cpp文件包含void main 函数和类MyClass我现在想将其拆分为另一个文件 IDE 是 Microsoft Visual Studio 2008 Professional myclass h include
  • 如何在 OS X 静态库中包含 nib?

    我看过几篇关于 iOS 的文章讨论这个主题 但是一两次提到 OS X 只是说构建一个框架而不是静态库 我找不到有合适框架说明的帖子 我已将项目创建为静态库 并相应地对整个项目进行了编码 现在 我只想将我的框架放入演示应用程序中 但它抱怨缺少
  • java.lang.IllegalStateException:片段已添加

    我在使用目标 SDK 4 3 编译和运行的 Android 应用程序时遇到问题 该应用程序有两个 Activity 一个 MainActivity 也是启动器 Activity 和一个 SecondActivity 两者都使用 Fragme
  • 结构体末尾的空数组是C标准吗?

    我注意到在开源项目中经常使用结构末尾的空数组 typedef struct A void arr A 我想知道这是C标准吗 或者只适合 gcc 编译器 从 C99 开始 它现已成为 C 标准 C99 之前的编译器可能不支持它 旧的方法是声明
  • 为什么聚合物的 flex 属性/类不起作用?

    浏览器 Firefox v35 操作系统 Linux Ubuntu 14 Polymer v1 4 正在关注 Rob Dodson 的 Polycasts 大多数视频都提到使用 flex flexbox 来实现响应式设计 然而 我很难让它发
  • 如何使用 jQuery 将表格中的制表符顺序从水平重新分配为垂直?

    如何使用 jQuery 设置带有输入元素的表格的 Tab 键顺序 以便 Tab 键顺序为垂直 每列下方 而不是默认的水平方法 下面的数字代表我想要的跳位顺序 我希望 jQuery 代码能够独立于表中的行数和列数工作 示例表 不幸的是呈现为图
  • 通过IdHTTP读取并保存部分文件流

    我想通过文件流从 HTTP 服务器下载文件 并且只读取 并保存到文件 前几行 例如 100 行 读取前 100 行后 文件流必须结束 所以我不想下载或阅读整个文件 您可以在下面找到我到目前为止所拥有的内容 该网站只是一个例子 有人可以引导我
  • 使用numpy.数字或替代数组上的binary_repr - Python

    使用以下代码我尝试将数字列表转换为二进制数但出现错误 import numpy as np lis np array 1 2 3 4 5 6 7 8 9 a np binary repr lis width 32 运行程序后的错误是 回溯
  • r 包插入符号-使用并行时打印迭代

    无论如何 我们可以在使用时打印迭代caret train并行功能 我知道有一个名为 verbose 的选项 但如果我使用多核 它似乎不会打印任何内容 我找到了解决方案 我们需要的只是通过 makeCluster 函数注册核心 library
  • C# 中的 System.Threading.Timer 似乎不起作用。每3秒运行速度非常快

    我有一个计时器对象 我希望它每分钟运行一次 具体来说 它应该运行一个OnCallBack方法并在 a 时变得不活动OnCallBack方法正在运行 一旦OnCallBack方法完成后 它 aOnCallBack 重新启动计时器 这是我现在所
  • 如何在cmake中使用调试符号构建依赖共享库?

    我的代码是这样组织的 cpp main cpp 从调用代码dataStructures and common CMakeLists txt topmostCMakeLists 文件 build common CMakeLists txt 应
  • Android Java - 创建 Cronjob

    我想要制作一个在后端运行的 Cronjob 并启动一个方法 30 分钟 如果函数返回 true 或其他 Cronjob 将创建一个状态栏通知 在 Android 中这可能吗 如果是的话 用哪个函数 非常感谢 安卓系统报警管理器 http d
  • 如何让 CreateProcess/CreateProcessW 在路径 > MAX_PATH 字符中执行进程

    我试图让 CreateProcess 或 CreateProcessW 执行名称 http msdn microsoft com en us library ms682425 aspx http msdn microsoft com en
  • 限制可排序的容器/父级

    好的 我又来了 和 RubaXa 一起玩Sortable http rubaxa github io Sortable 插件 希望他就在这附近 因为这个插件相当复杂 一些发现 我花了一些时间才完全理解这个机制 但我认为我是对的 Case 1
  • Windows 命令提示符中的别名

    我已经添加了notepad exe到我的环境变量中的路径 现在在命令提示符下 notepad exe filename txt打开filename txt 但我想做的只是np filename txt打开文件 我尝试使用DOSKEY np
  • intel avx2 中是否有 movemask 指令的逆指令?

    movemask 指令采用 m256i 并返回 int32 其中每个位 前 4 8 或所有 32 位 具体取决于输入向量元素类型 是相应向量元素的最高有效位 我想做相反的事情 取 32 其中只有 4 8 或 32 个最低有效位有意义 并获得