(编者注:这个问题原来是:如何访问__m128i对象的m128i_i8成员或一般成员?,尝试在GCC的__m128i
定义上使用MSVC特定的方法。但这是一个XY问题,公认的答案是关于这里的XY问题。另一个答案 * 确实 * 回答了这个问题。)
我意识到微软建议不要直接访问这些对象的成员,但我需要设置它们,而documentation非常缺乏。
我继续得到错误“request for member 'm128i_i8' in '(my var name)',which is of non-class type 'wirelabel {aka __vector(2)long long int}'”,我不明白,因为我已经包含了所有正确的头,它确实识别__m128i变量。
注1:wirelabel是__m128i的typedef,即存在于标头中
typedef __m128i wirelabel
注2:使用注1的原因在以下其他问题中解释: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位部分中的每一个,这是为什么:我有一个大小为'n*128'的bool
数组(n是一个size_t),我需要将这些存储在大小为'n'的'wirelabel'数组中。
现在,因为wirelabel只是__m128i的别名/typedef(如果有区别请纠正我),128个布尔值的'n'个索引中的每一个都可以存储在'wirelabel'数组中。
然而,为了做到这一点,我认为需要将每8位转换为它的有符号等价物,并将其存储在数组中每个“无线标签”指针的正确8位索引中。
2条答案
按热度按时间k75qkfdt1#
所以你的源数据是连续的?你应该使用
_mm_load_si128
,而不是乱用向量类型的标量分量。真实的的问题是将一个
bool
数组(x86上g++使用的ABI中每个元素1个字节)打包成位图。您应该使用SIMD来执行 * 此操作,而不是使用标量代码来一次设置1位或字节。pmovmskb
(_mm_movemask_epi8
)非常适合提取输入的每个字节的一位。你只需要把你想要的比特安排到高比特。显而易见的选择是移位,但是向量移位指令与Haswell上的
pmovmskb
竞争相同的执行端口(端口0)。(http://agner.org/optimize/)。相反,添加0x7F
将为1
的输入产生0x80
(高位设置),但为0
的输入产生0x7F
(高位清除)。(x86-64 System V ABI中的bool
必须以整数0或1的形式存储在内存中,而不是简单的0与任何非零值)。为什么不使用
pcmpeqb
来对抗_mm_set1_epi8(1)
?Skylake在端口0/1上运行pcmpeqb
,但在所有3个向量ALU端口(0/1/5)上运行paddb
。但是,在pcmpeqb/w/d/q
的结果上使用pmovmskb
是非常常见的。由于我们希望在写入此位图时使用标量存储,因此出于严格的别名原因,我们希望
dst
位于uint16_t
中。对于AVX 2,您需要uint32_t
。(或者,如果您使用combine = tmp1 << 16 | tmp
来合并两个pmovmskb
结果。但可能不要这样做)。为了处理严格的锯齿问题,如果你想以后用不同的C类型访问你的掩码位图,你可以使用
memcpy
来存储,如another Q&A所示。这将编译成一个asm循环,如下图所示(使用gcc7.3 -O3,在Godbolt编译器浏览器上)
因此,这并不美妙(7个熔丝域uop->前端瓶颈,每1.75个时钟周期16个布尔值)。Clang展开2,并且每1.5个周期应管理16个布尔。
使用移位(
pslld xmm0, 7
)只能在Haswell上每2个周期运行一次迭代,在端口0上遇到瓶颈。这在Skylake和以后不是问题;移位可以在更多端口上运行**,因此_mm_slli_epi32(v, 7)
在那里是好的**,并且避免了需要向量常量。另请参见提取__m128i中每个布尔字节的低位?bool数组到压缩位图wko9yo5t2#
创建一个匿名联合,其中包含一个
_m128i
成员和一个要设置其成员的其他类型的数组。类型双关在C中是法律的的,并且在g++,clang++和MSVC中作为扩展支持。如果要设置单个位,可以将另一个成员声明为struct
位字段。位字段的顺序是由实现定义的,但您使用的是Intel内部的,因此它将是little-endian。