String 如何在SIMD/AVX2代码中连接有界长度字符串

String 如何在SIMD/AVX2代码中连接有界长度字符串,string,concatenation,simd,avx,String,Concatenation,Simd,Avx,我在AVX2 uint8x32寄存器中存储了32个长度为1到4的字符串,其中长度,字节0,字节1,字节2,字节3各一个寄存器。我想连接所有字符串并将它们密集地写入内存。如果所有字符串的长度都相等,那么这将很简单:我将使用pshufb将字节洗牌到它们的目标位置,并使用一些blend调用将字节0/字节1/字节2/字节3寄存器混合在一起。(或者,我可以使用vpunpack*说明。尚未解决…) 然而,可变长度方面使这变得更加困难:每个输出字节的来源现在是长度的一个重要函数。我不知道如何在AVX2代码中有

我在AVX2 uint8x32寄存器中存储了32个长度为1到4的字符串,其中
长度
字节0
字节1
字节2
字节3
各一个寄存器。我想连接所有字符串并将它们密集地写入内存。如果所有字符串的长度都相等,那么这将很简单:我将使用
pshufb
将字节洗牌到它们的目标位置,并使用一些
blend
调用将
字节0
/
字节1
/
字节2
/
字节3
寄存器混合在一起。(或者,我可以使用
vpunpack*
说明。尚未解决…)

然而,可变长度方面使这变得更加困难:每个输出字节的来源现在是长度的一个重要函数。我不知道如何在AVX2代码中有效地实现这一点。帮忙

一句话:我希望重新实现以下函数,使用(尽可能快的)向量代码而不是标量代码编写:

()


int concat_字符串(char*dst、uuum256i len_v、uuum256i字节0_v、uuuuum256i字节1_v、uuuuuum256i字节2_v、uuuuuuum256i字节3_v){
charlen[32];
字符字节0[32];
字符字节1[32];
字符字节2[32];
字符字节3[32];
_mm256_存储_si256(重新解释铸造(len),len_v);
_mm256_存储_si256(重新解释转换(字节0),字节0_v);
_mm256_存储_si256(重新解释转换(字节1),字节1_v);
_mm256_存储_si256(重新解释转换(字节2),字节2_v);
_mm256_存储_si256(重新解释转换(字节3),字节3_v);
int pos=0;
对于(int i=0;i<32;++i){
dst[pos+0]=字节0[i];
dst[pos+1]=字节1[i];
dst[pos+2]=字节2[i];
dst[pos+3]=字节3[i];
pos+=len[i];
}
返回pos;
}

帮助?

有点相关:对于基于每元素谓词的固定宽度32位元素(查找pshufb控制向量与使用
pext
计算向量,您可能会使用AVX2)。但是字符串数据具有字节粒度,并且在向量之间交错,因此任何较宽的向量存储都需要一些洗牌(yes
vpunpack*
+
vpermq
或类似)。也许在此期间做一些有条件的事情,基于
vpcmpgtb len,set1(1)
?顺便说一句,您的字符串格式似乎不太方便处理。除非这真的对你用它们做的其他事情有好处,否则你能把长度隐式化吗?对于长度为1的字符串,字节1..3应
0
。或者长度为4的字符串没有字节
0
。(IDK,如果这有帮助的话,一个庞大的无序控制向量查找表会因为缓存未命中而变得很糟糕。)也许显式长度确实有帮助。任何给定字节的最终位置是前面长度的前缀和,加上其字符串内的偏移量。显示了一些洗牌的想法。对于一般的字节水平和,
psadbw
与8字节组中的
setzero()向量相对。如果您可以将其转换为
pshufb
掩码,然后按正确的顺序混合,则短字符串的尾随字节可能会被免费覆盖?可能以128位块为单位工作是一个不错的选择,或者在高半部独立工作,然后与memcpy重新组合?在
vpunpack
之后,将4个字符串分别转换为一个16字节的子向量,另一个候选者可能是一系列
vps[l,r]lv[d,q]
,带有一些巧妙计算的指数。这可以将4个字符串压缩到寄存器一半内的一个连续区域,但恐怕没有128位移位和非立即移位计数。

int concat_strings(char* dst, __m256i len_v, __m256i byte0_v, __m256i byte1_v, __m256i byte2_v, __m256i byte3_v) {
    char len[32];
    char byte0[32];
    char byte1[32];
    char byte2[32];
    char byte3[32];
    _mm256_store_si256(reinterpret_cast<__m256i*>(len), len_v);
    _mm256_store_si256(reinterpret_cast<__m256i*>(byte0), byte0_v);
    _mm256_store_si256(reinterpret_cast<__m256i*>(byte1), byte1_v);
    _mm256_store_si256(reinterpret_cast<__m256i*>(byte2), byte2_v);
    _mm256_store_si256(reinterpret_cast<__m256i*>(byte3), byte3_v);

    int pos = 0;
    for (int i = 0; i < 32; ++i) {
        dst[pos + 0] = byte0[i];
        dst[pos + 1] = byte1[i];
        dst[pos + 2] = byte2[i];
        dst[pos + 3] = byte3[i];
        pos += len[i];
    }
    return pos;
}