我意识到Microsoft建议不要直接访问这些对象的成员,但是我需要设置它们并且documentation非常缺乏。
我继续收到错误"请求'(我的名字)'中的成员'm128i_i8',这是非类型'wirelabel {aka __vector(2)long long int}' "我不明白,因为我已经包含了所有正确的标题,它确实识别了__m128i变量。
注1:wirelabel是__m128i的typedef,即标题中存在
typedef __m128i wirelabel
注2:使用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;#*; n * 128' (n是size_t)我需要将这些存储在' wirelabel'的数组中。尺寸为' n'。现在因为对于__m128i而言,wirelabel只是一个别名/ typedef(如果存在差异,请纠正我),每个' n' 128个bool的索引可以存储在' wirelabel'阵列。但是,为了做到这一点,我认为需要将每个8位转换为它的带符号等效值,并将其存储在每个' wirelabel'中的正确的8位索引中。数组中的指针。如果您有任何问题,请发表评论
答案 0 :(得分:4)
所以您的源数据是连续的?您应该使用_mm_load_si128
而不是使用矢量类型的标量组件。
你真正的问题是将一个bool
数组(x86上g ++使用的ABI中每个元素1个字节)打包成一个位图。您应该使用SIMD this ,而不是使用标量代码一次设置1位或字节。
pmovmskb
(_mm_movemask_epi8
)非常适合每字节输入一位。你只需要安排将你想要的位置到高位。
显而易见的选择是移位,但向量移位指令在Haswell(端口0)上竞争与pmovmskb
相同的执行端口。 (http://agner.org/optimize/)。相反,添加0x7F
将为0x80
的输入生成1
(高位设置),但0x7F
输入0
(高位清除) 。 (并且x86-64 System V ABI中的bool
必须以整数0或1存储在内存中,而不是简单地存储为0与任何非零值。
为什么不pcmpeqb
反对_mm_set1_epi8(1)
? Skylake在端口0/1上运行pcmpeqb
,但在所有3个向量ALU端口(0/1/5)上运行paddb
。不过,对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);
}
}
因为我们想在编写此位图时使用标量存储,所以出于严格别名的原因,我们希望dst
位于uint16_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 - &gt;前端瓶颈在16个bool每~1.75个时钟周期)。 Clang展开2,每1.5个循环应该管理16个bool。
使用班次(pslld xmm0, 7
)只会在Haswell上每2个周期运行一次,在端口0上出现瓶颈。
答案 1 :(得分:0)
创建一个包含_m128i
成员的匿名联合,以及要设置其成员的另一种类型的数组。类型惩罚在C中是合法的,并且作为g ++,clang ++和MSVC中的扩展支持。如果要设置单个位,可以将另一个成员声明为struct
位域。位域的顺序是实现定义的,但无论如何你都在使用英特尔内部,所以它将是小端的。