使用SIMD指令处理N字节数据时(一次至少阅读16个字节),通常我只是简单地在缓冲区的末尾添加填充,这样我就可以安全地取整要读取的16字节块的数量。但是,这次我需要处理外部代码准备的数据,因此理论上可能发生最后16字节数据向量部分地福尔斯在所分配的存储器范围之外的情况。
例如,假设我存储了22个字节的数据,从1FFF FFE 4开始:
1FFF FFE0: 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B 0C
1FFF FFF0: 0D 0E 0F 10 11 12 13 14 15 16 00 00 00 00 00 00
然后,我要处理16 x 16字节以上的数据,从1FFFFFE 4开始,如下所示:
MOV RDX, 1FFFFFE4
MOV RCX, 2
@MAIN:
VMOVDQU XMM0, [RDX]
... data processing
ADD RDX, 16
LOOP @MAIN
最后一次迭代将从1FFFFFF 4读取16个字节,而我在那里只有6个有效字节的数据,其余10个字节可能超出了分配的内存范围(特别是20000000的最后4个字节)。
在最后一次读取部分超出分配的内存范围的情况下(虽然不太可能,但也有可能),或者如果VMOVDQU参数的第一个字节有效,上面的代码会因访问冲突而失败吗?有人能在英特尔64 SDK中指出这方面的确切规则吗?
如果它可能失败,除了以更慢但更安全的方式(逐字节而不是16 × 16字节)处理数据末尾之外,还有其他解决方案吗?这是我以前在这种情况下所做的,但这基本上意味着代码加倍(一个SIMD和一个慢速代码用于同一任务),这是额外的工作和潜在的错误。
由于访问冲突不太可能发生,我也在考虑捕获异常,以安全的方式加载数据,然后跳回-这可以保持代码简单,因为算法本身将保持不变,只需要添加一小段代码,以更安全的方式加载数据,只在非常罕见的情况下执行。在代码下面,但我不知道如何在汇编中捕获异常,也不知道时间损失是否小到有意义:
VMOVDQU XMM0, [RDX]
@DATALOADED:
... data processing
ADD RDX, 16
... the rest of the algorithm
@EXCEPTION: // jumps here if the VMOVDQU fails with access violation, happens rarely anyway
...load data in XMM0 in a safer way
JMP @DATALOADED
我正在等待任何其他可以保持代码简单的建议。
1条答案
按热度按时间wbgh16ku1#
下面是我处理这个问题的方法,我使用了一个部分重叠的最终迭代(加上一个可选的初始向量循环对齐)。
这种方法的优点是最后几个元素可以在单个额外的循环迭代中处理。
缺点是:
a[i] = b[i] + c[i]
,但不要用于a[i] += b[i]
。如果可以使用别名,则很容易修改代码以捕获a == b || a == c
情况并使用回退我还加入了一个可选的内存位置对齐;这里我选择了输出。我认为这对于AVX不是特别必要,但它使用了相同的技术,如果您适应SSE 2或AVX 512,可能会很方便。
我用C++编写了这篇文章,其中包含英特尔内部函数,但如果您想将汇编器输出应用到ASM中,它的可读性非常强。