10
简短的问题是,如果我有一个函数需要两个向量。一个是输入,另一个是输出(没有别名)。我只能对其中一个,我应该选择哪一个?未对齐的负载与未对齐的存储
较长的版本是,考虑功能,
void func(size_t n, void *in, void *out)
{
__m256i *in256 = reinterpret_cast<__m256i *>(in);
__m256i *out256 = reinterpret_cast<__m256i *>(out);
while (n >= 32) {
__m256i data = _mm256_loadu_si256(in256++);
// process data
_mm256_storeu_si256(out256++, data);
n -= 32;
}
// process the remaining n % 32 bytes;
}
如果in
和out
排列了32个字节,那么就没有使用vmovdqu
代替vmovdqa
的处罚。最糟糕的情况是两者都未对齐,四分之一的加载/存储将跨越缓存线边界。
在这种情况下,我可以通过在进入循环之前首先处理几个元素来将它们中的一个与高速缓存行边界对齐。但是,问题是我应该选择哪一个?未对齐的加载和存储之间,哪一个更糟?
看看一些memcpy的实现;我认为这有一个通常的方式,但我忘记了它是什么。虽然也许这取决于你在做什么。对齐的加载将避免缓存行边界,所以没有加载使用延迟惩罚(如果指针增量是可预测的,则不太相关,因为OOO可以使加载地址远远超出余下的循环)。由于在对象外部读取通常是安全的,但是写入不是,如果可以避免清理循环的完整标量版本,那么这可能会影响决策。 –
我对此进行了一些测试,并确定至少在我测试过的处理器(Pentium 4,Core 2,Sandy Bridge和Haswell)上,对齐输入向量明显快于对齐输出向量。你的旅费可能会改变。我不愿意将此作为答案发布,因为我不再有测试代码,不想再次编写测试代码并再次运行测试,也没有官方参考指向任何类型的文档。所以有一个upvote代替! :-) –
@CodyGray无论如何感谢。我一直在研究这个问题的一些测试。到目前为止,我可以告诉的只是“它取决于” –