X86 使用字节分隔符快速提取可变大小字段的SIMD

X86 使用字节分隔符快速提取可变大小字段的SIMD,x86,simd,avx,avx2,X86,Simd,Avx,Avx2,假设我们想要从由可变长度字段组成的输入流中提取字段。我们只知道每个字段的最大宽度,每个字段以一个值为1的字节结尾。我们希望将压缩字段提取为固定格式,其中每个字段都有其最大宽度(如果输入字段小于最大宽度,则为零填充) 每个字段的最小宽度为一个字节 例如,我们希望接收两个字段的值。第一个最大宽度为3字节,第二个最大宽度为2字节 假设我们有一个输入向量{X,1,1},所以我们知道第一个字段的值是{X,1},第二个字段的值是{1}。所以在这种情况下,结果向量应该等于{0,X,1,0,1} 或者,我们有一

假设我们想要从由可变长度字段组成的输入流中提取字段。我们只知道每个字段的最大宽度,每个字段以一个值为
1
的字节结尾。我们希望将压缩字段提取为固定格式,其中每个字段都有其最大宽度(如果输入字段小于最大宽度,则为零填充)

每个字段的最小宽度为一个字节

例如,我们希望接收两个字段的值。第一个最大宽度为3字节,第二个最大宽度为2字节

假设我们有一个输入向量{X,1,1},所以我们知道第一个字段的值是{X,1},第二个字段的值是{1}。所以在这种情况下,结果向量应该等于{0,X,1,0,1}

或者,我们有一个输入向量{1,1},所以得到的向量应该等于{0,0,1,0,1}


我想我知道一种使用查找表的方法。问题是,如果我们决定一次处理超过64位的数据,我们最终会得到太大的查找表。

一个合理的方法是使用矢量化的
cmp
查找所有的1,然后
movmskb
将这些结果作为位图放入通用寄存器,然后使用该值查找一个
pshufb
掩码,该掩码根据位图将字节扩展到字段中

此技术还可以处理“最大字段带”限制,而无需任何成本,因为该行为内置于查找表中的洗牌掩码中

现在您将无法获取完整的32字节
ymm
寄存器将创建一个32位位图并直接查找,因为它需要类似于128 GB的查找表1的内容,这是不可行的(或者至少速度非常慢)。实际上,您需要处理一些固定数量的输出字节,以保持表的大小合理,例如8到16个字节之间。最佳值可能取决于您在紧循环中执行此操作的次数以及对周围代码的缓存压力成本

假设你仍然想要更快的速度。您可以查看字段长度的实际分布,如果一些“典型”排列占主导地位,您可以使用一种乐观算法,该算法获取位图,将其哈希到较小的位数,然后在一级无序排列控制表中查找该值,该表中仅包含“预期”字段长度的条目。同时,执行另一次查找,以验证实际完整位图是否与与第一级表关联的预期完整位图匹配

当您在第一级表中点击时,您将按照上面的步骤进行,并进行一次大的(16或32字节)无序移动,否则您将返回到上面的几个较小的查找。散列需要类似于预期值的“完美散列”,这样就不会发生冲突

您可以在运行时计算查找表,或将其作为常量嵌入二进制文件本身



1。。。即使你真的创建了这样一个怪物查找表,你也会遇到这样的限制:
pshufb
在两个16字节的通道中工作,而不是在整个32字节寄存器中工作。

我不太确定我是否理解你要实现的目标,但这似乎可以用一系列的函数来完成,问题是输入向量的形状可能不同。对于上面的例子,它可以是{1,1},{X,1,1},{X,X,1,1},{1,X,1},等等中的一个。所以为了使用置换,我需要先生成一个掩码,但我不知道如何有效地进行。我困惑的是你怎么知道向量中字节的解释是什么。您需要以某种方式将该信息传递给函数;为什么不把它作为所需的掩码传递呢?每次我们在输入向量中看到“1”,我们就知道这是当前字段的最后一个字节。所以{1,1}意味着第一个'1'指向第一个字段的最低有效字节(LSB),第二个'1'指向第二个字段的LSB。另一方面,如果我们有{X,1,Y,1},这意味着我们应该用{X,1}填充第一个字段的两个字节,用{Y,1}填充第二个字段的两个字节。因此,对于每个宽度为N字节的字段,我们应该期望输入向量中有一个宽度为[1;N]的值。该算法应该以最小的延迟工作,并且不需要检测任何类型的错误。谢谢。我在考虑相同的解决方案,除了最常见模式的第二个LUT。我将做一些基准测试并发布结果。您也可以尝试计算洗牌掩码,而不是在向量寄存器或通用寄存器中查找它(您可以使用
pdep
,这非常有帮助)。类似于Peter在中所示的内容—但由于您是按字节进行计算,因此计算解决方案需要按比例放大(当然LUT解决方案也有同样的问题),这使得计算成本更高。@AlexeyR。和@Bee:使用
pshufb
一次处理16个字节可能更快。这与pcmpeqb/pmovmsk/pshufb的问题非常相似。关于这个问题的答案+评论有一些有趣的想法,可以压缩LUT以权衡额外指令与大小+缓存未命中。@Peter Cordes-这与我的建议不同吗?我留下了一个问题,你是否可以使用32字节的宽度,而不是一个单一的车道,因为作为第一步,你真的需要看看你将如何生成你的洗牌面具。如果您必须在一个大表中为每个车道进行查找,但之后您没有一次处理32个字节,那么16个字节就可以了。另一方面,如果你能找到一种有效生成掩码的方法,那么一次尝试32字节有助于减轻存储和洗牌端口的压力。是的,同意。我主要只是想评论一下,以链接到IPv4答案的
pshufb
,该答案有一些实际的工作代码,用于执行几乎与