gpt4 book ai didi

c++ - 提取 __m128i 中每个 bool 字节的低位? bool 数组到打包位图

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:50:24 26 4
gpt4 key购买 nike

(编者注:这个问题最初是:应该如何访问 __m128i 对象的 m128i_i8 成员或一般成员?,尝试在 GCC 的定义上使用 MSVC 特定的方法__m128i。但这是一个 XY 问题,接受的答案是关于这里的 XY 问题。另一个答案确实回答了这个问题。)

我知道 Microsoft 建议不要直接访问这些对象的成员,但我需要设置它们和 documentation非常缺乏。

我继续收到错误“在‘(我的 var 名称)’中请求成员‘m128i_i8’,它是非类类型‘wirelabel {aka __vector(2) long long int}’”,我不知道理解,因为我包含了所有正确的 header 并且它确实识别 __m128i 变量。

注意 1:wirelabel 是 __m128i 的类型定义,即存在于标题中

typedef __m128i wirelabel 

Note2:使用 Note1 的原因在以下其他问题中有解释: tbb::cache_aligned_allocator: Getting "request for member...which is of non-class type" with __m128i. User error or bug?

注3:我使用的是编译器g++

注意 4:以下问题没有回答我的问题,但讨论了相关信息 Why should you not access the __m128i fields directly?

我也知道有一个 _mm_set_epi8 函数,但它要求您一次设置所有 8 位部分,目前我不适合这个选项。


接受的答案回答的问题:

编辑:我被问及为什么我认为我需要访问 __m128i 对象的 16 个 8 位部分中的每一个部分的更多细节,原因如下:我有一个 bool 大小为“n*128”的数组(n 是一个 size_t),我需要将它们存储在大小为“n”的“wirelabel”数组中。

现在因为 wirelabel 只是 __m128i 的别名/typedef(如果有差异请纠正我),128 个 bool 的“n”个索引中的每一个都可以存储在“wirelabel”数组中。

但是,为了做到这一点,我认为需要将每 8 位转换为它的有符号等价物,并将其存储在数组中每个“wirelabel”指针的正确 8 位索引中。

最佳答案

所以您的源数据是连续的?你应该使用 _mm_load_si128而不是乱搞 vector 类型的标量分量。


您真正的问题是打包 bool 的数组(x86 上 g++ 使用的 ABI 中每个元素 1 个字节)转换为位图。您应该使用 SIMD 执行此操作,而不是使用标量代码一次设置 1 位或字节。

pmovmskb ( _mm_movemask_epi8 ) 非常适合从输入的每个字节中提取一位。你只需要安排把你想要的位放到高位就可以了。

显而易见的选择是移位,但 vector 移位指令竞争与 pmovmskb 相同的执行端口在 Haswell(端口 0)上。 (http://agner.org/optimize/)。相反,添加 0x7F将产生 0x80 (高位设置)对于 1 的输入,但是 0x7F (高位清除)对于 0 的输入. (并且 x86-64 System V ABI 中的 bool 必须作为整数 0 或 1 存储在内存中,而不仅仅是 0 与任何非零值)。

为什么不 pcmpeqb反对_mm_set1_epi8(1) ? Skylake 运行 pcmpeqb在端口 0/1 上,但是 paddb在所有 3 个 vector ALU 端口 (0/1/5) 上。使用 pmovmskb 很常见关于 pcmpeqb/w/d/q 的结果, 不过。

#include <immintrin.h>
#include <stdint.h>

// n is the number of uint16_t dst elements
// We access n*16 bool elements from src.
void pack_bools(uint16_t *dst, const bool *src, size_t n)
{
// you can later access dst with __m128i loads/stores

__m128i carry_to_highbit = _mm_set1_epi8(0x7F);
for (size_t i = 0 ; i < n ; i+=1) {
__m128i boolvec = _mm_loadu_si128( (__m128i*)&src[i*16] );
__m128i highbits = _mm_add_epi8(boolvec, carry_to_highbit);
dst[i] = _mm_movemask_epi8(highbits);
}
}

因为我们想在写这个位图时使用标量存储,所以我们想要dstuint16_t出于严格别名的原因。使用 AVX2,您需要 uint32_t . (或者,如果您执行了 combine = tmp1 << 16 | tmp 以合并两个 pmovmskb 结果。但可能不会这样做。)

这会编译成这样的 asm 循环 ( with gcc7.3 -O3, on the Godbolt compiler explorer )

.L3:
movdqu xmm0, XMMWORD PTR [rsi]
add rsi, 16
add rdi, 2
paddb xmm0, xmm1
pmovmskb eax, xmm0
mov WORD PTR [rdi-2], ax
cmp rdx, rsi
jne .L3

所以这并不好(7 个融合域 uops -> 前端瓶颈在每 ~1.75 个时钟周期 16 个 bool 值)。 Clang 按 2 展开,并且每 1.5 个周期应管理 16 个 bool 值。

使用轮类 (pslld xmm0, 7) 只会在 Haswell 上每 2 个周期运行一次迭代,在端口 0 上出现瓶颈。

关于c++ - 提取 __m128i 中每个 bool 字节的低位? bool 数组到打包位图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49263507/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com