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 指令的逆指令? 的相关文章

  • 使用 AVX 内在函数代替 SSE 并不能提高速度 - 为什么?

    我已经使用 Intel 的 SSE 内在函数相当长一段时间了 并取得了良好的性能提升 因此 我希望 AVX 内在函数能够进一步加速我的程序 不幸的是 直到现在情况并非如此 可能我犯了一个愚蠢的错误 所以如果有人能帮助我 我将非常感激 我使用
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • Intel 64 和 IA-32 上的 MESI 有何意义

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

    在linux x86 中分页 每个进程都有它自己的页面目录 页表遍历从 CR3 指向的页目录开始 每个进程共享内核页目录内容 假设三个句子是正确的 假设某个进程进入内核 模式并更新他的内核页目录内容 地址映射 访问 权利等 问题 由于内核地
  • CPU寄存器和多任务处理

    我目前正在学习汇编 我很困惑 CPU 寄存器如何与多任务一起工作 所以在多任务系统中 CPU可以随时暂停某个程序的执行并运行另一个程序 那么在这一步中寄存器值是如何保存的呢 寄存器是压入堆栈还是以其他方式 CPU 寄存器如何与多任务一起工作
  • 将 lambda 表达式传递给 lambda 参数 c++11

    我想做这样的事情 int main auto f some variables take lambda function something with lambda function f other variables variables
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代
  • 这种没有推送寄存器的交换有多安全?

    我对汇编非常陌生 下面的代码应该通过两个不同的函数交换两个整数 首先使用swap c然后使用swap asm 但我怀疑 我是否需要push 我的意思是保存 汇编代码之前寄存器的每个值和pop稍后 就在返回之前 main 换句话说 如果我返回
  • 避免 gcc 函数序言开销?

    我最近遇到了很多 gcc 在 x86 上生成非常糟糕的代码的函数 它们都符合以下模式 if some condition do something really simple and return else something comple
  • GCC 5 及更高版本中的 AVX2 支持

    我编写了以下类 T 来加速操作 使用 AVX2 的 字符集 然后我发现它不起作用 gcc 5 及更高版本当我使用 O3 时 谁能帮我追踪到一些编程结构 已知不适用于最新的编译器 系统 该代码的工作原理 底层结构 bits 是一个 256 字
  • movzbl(%rdi, %rcx, 1), %ecx 在 x86-64 汇编中意味着什么?

    我想我明白 movzbl rdi rcx 1 ecx 意思是 将零扩展字节移至长整型 并表示将 ecx 扩展为 32 位 但我不完全确定语法 rdi rcx 1 指的是什么 我在某处看到该语法指的是 Base Index Scale 但我找
  • 将字段中的位扩展到掩码中所有(重叠+相邻)集位的最快方法?

    假设我有 2 个名为 IN 和 MASK 的二进制输入 实际字段大小可能是 32 到 256 位 具体取决于用于完成任务的指令集 每次调用时两个输入都会改变 Inputs IN 1100010010010100 MASK 000111101
  • Clang 编译器 (x86):80 位长双精度

    我正在尝试在 x86 Windows 平台上使用本机 80 位长双精度 海湾合作委员会选项 mlong double 80 https gcc gnu org onlinedocs gcc x86 Options html似乎不适用于 cl
  • ICC 中的 -O3 会扰乱内在函数,使用 -O1 或 -O2 或相应的手动汇编即可

    这是后续这个问题 http stackoverflow com questions 49791664 o2 in icc messes up assembler fine with o1 in icc and all optimizatio
  • 如何使用 __m128i 执行元素左移?

    我发现 SSE 移位指令只能在所有元素上移位相同的量 mm sll epi32 mm slli epi32 这些会移动所有元素 但移动量相同 http software intel com sites products documentat
  • 比“add esp, 4”更小的指令

    又是我 我的程序中有很多 add esp 4 我正在尝试减小它的大小 是否有任何更小的指令可以替代 add esp 4 pop edx 或者您不介意破坏的任何其他整数寄存器 这就是现代编译器实际上所做的 https stackoverflo
  • 如何反汇编、修改然后重新组装 Linux 可执行文件?

    无论如何 这可以做到吗 我使用过 objdump 但它不会产生我所知道的任何汇编器都可以接受的汇编输出 我希望能够更改可执行文件中的指令 然后对其进行测试 我认为没有任何可靠的方法可以做到这一点 机器代码格式非常复杂 比汇编文件还要复杂 实
  • 如果默认禁用 A20 线,如何在 0xFFFFFFF0 处访问 BIOS ROM?

    我正在阅读有关 A20 线的信息http wiki osdev org A20 Line http wiki osdev org A20 Line 这似乎表明 A20 线默认被禁用 在Pentium上 如果硬复位后立即输出的地址为0xFFF
  • 32位进程在64位操作系统上可以访问多少内存?

    在 Windows 上 正常情况下 32 位进程只能访问 2GB RAM 或通过 boot ini 文件中的特殊开关访问 3GB 在 64 位操作系统上运行 32 位进程时 有多少可用内存 是否有任何特殊的开关或设置可以改变这种情况 默认
  • mfence 和 asm 易失性 ("" : : : "内存") 的区别

    据我了解 mfence是硬件内存屏障 而asm volatile memory 是编译器障碍 但是 可以asm volatile memory 用来代替 mfence 我感到困惑的原因是这个链接 http gcc gnu org ml gc

随机推荐

  • 如何使用 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 个最低有效位有意义 并获得