我已经实现了一个SSE4.2版本的memcpy,但我似乎无法在Xeon V3上击败_intel_fast_memcpy。我在聚集例程中使用我的例程,其中每个位置的数据在4到15个字节之间变化。我在这里和英特尔的网站上看过很多帖子都没有运气。我应该看看什么是好的来源?
答案 0 :(得分:1)
你可以用16B加载和存储来收集,然后只是重叠但是最后有很多垃圾字节?
// pseudocode: pretend these intrinsics take void* args, not float
char *dst = something;
__m128 tmp = _mm_loadu_ps(src1);
_mm_storeu_ps(dst, tmp);
dst += src1_size;
tmp = _mm_loadu_ps(src2);
_mm_storeu_ps(dst, tmp);
dst += src2_size;
...
重叠存储是高效的(并且L1缓存可以很好地吸收它们),而现代CPU应该很好地处理这个问题。未对齐的货物/商店足够便宜,我认为你不能打败这个。 (假设平均页面分割负载量。即使你有超过平均缓存行分割负载量,但它可能不会成为问题。)
这意味着内部循环内没有条件分支来决定复制策略,或任何掩码生成或任何东西。你需要的只是一个额外的12B或者你的收集缓冲区末尾的东西,以防最后一个副本只应该是4B。 (您还需要收集的元素不在页面末尾的16B范围内,以下页面未映射或不可读。)
如果阅读过你收集的元素的结尾是一个问题,那么加载vpmaskmov
实际上可能是一个好主意。如果你的元素是4B对齐的,那么读取最后3个字节总是很好。您仍然可以在dst缓冲区中使用普通的16B矢量存储。
我使用_ps
次加载,因为movups
比movupd
或movdqu
短1个字节,但执行相同(请参阅Agner Fog's microarch pdf和其他链接x86代码wiki。(clang甚至会在movaps
使用movups
/ _mm_store_si128
。)
re:您的评论:请勿使用旧版SSE maskmovdqu
。最大的问题是它只能作为商店使用,所以它无法帮助您避免在您收集的对象之外阅读。 It's slow,它绕过缓存(它是一个NT存储),当你重新加载这些数据时,它会非常慢。
AVX版本(vmaskmov
和vpmaskmov
)不是这样的,因此将代码转换为使用maskmovdqu
可能会大幅放缓。
相关:我刚刚发布了关于using vmovmaskps
for the end of unaligned buffers的Q& A。我收到了一些有趣的回复。显然,它通常不是解决任何问题的最佳方法,即使我(聪明的IMO)生成掩码的策略非常有效。
MOVMASKPS是其中一个“当时似乎是一个好主意”的事情AFAICT。我从来没用过它。 - Stephen Canon