如何访问__m128i对象的m128i_i8成员或一般成员?

时间:2018-03-13 18:35:21

标签: c++ sse

我意识到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位索引中。数组中的指针。如果您有任何问题,请发表评论

2 个答案:

答案 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位域。位域的顺序是实现定义的,但无论如何你都在使用英特尔内部,所以它将是小端的。