我正在开发一个性能至关重要的应用程序。我希望 GCC 将对 memset() 的一些特定调用转换为带有重复前缀的指令,例如“rep stos QWORD PTR es:[rdi],rax”。当大小已知且很小时,GCC 会自动执行此操作。
但是,GCC 通过 PLT 调用 memset() 将调用映射到具有随机长度的 memset(),这会导致分支预测错误,因为分支预测器缓存是冷的。
有没有办法强制 GCC 做我想做的事情(在内联汇编之外)?请注意,我不希望整个程序出现这种行为,只对某些特定的 memset() 调用。
在一个相关主题上,我也对任何阻止 GCC 在 cmovcc 指令完成工作时分支的 hack 感兴趣(我知道使用 &、+ 等而不是 &&)。
非常感谢您的帮助。
@弗兰克:
这基本上就是我最终所做的。这是我的代码:
static finline void app_zero(void *dst, uint32_t size, uint32_t count)
{
// Warning: we tell gcc to use 'dst' both as source and destination here.
// This does not cause problems because we don't reuse 'dst'.
#ifdef APP_ARCH_X86
#define STOS(X,Y) do { \
int c = (size/Y)*count; \
__asm__ __volatile__("cld; xor %%eax, %%eax; rep stos"X"\n\n" \
: "+D"(dst), "+c"(c) :: "rax", "flags"); \
} while (0)
if (size % 8 == 0) STOS("q", 8);
else if (size % 4 == 0) STOS("l", 4);
else if (size % 2 == 0) STOS("w", 2);
else STOS("b", 1);
#undef STOS
#else
memset(dst, 0, size*count);
#endif
}
请注意,您的示例在您的测试设置中有效,但通常不会有效。GCC可以改变方向标志,所以cld
需要一条指令。此外,您必须告诉 gcc%rdi
并且%rcx
将被stos
指令更改,并且由于 gcc 不允许您指定寄存器既是输入又是被破坏的,因此您必须使用笨拙的"+"
语法(这也会破坏您的输入值) .
由于“cld”指令在 Nehalem 上具有 4 个周期的延迟,因此这不是最佳的。GCC 在内部跟踪标志寄存器状态 (AFAICT),因此它不需要每次都发出该指令。