我正在开发一个性能至关重要的应用程序.我希望GCC将一些特定的调用转换为memset()作为带有重复前缀的指令,如"rep stos QWORD PTR es:[rdi],rax".当尺寸既已知又小时,GCC会自动执行此操作.
但是,GCC通过PLT调用memset()以随机长度调用memset(),这会导致分支错误预测,因为分支预测器缓存是冷的.
有没有办法迫使GCC做我想做的事情(在内联汇编之外)?请注意,我不希望整个程序出现此行为,仅针对某些特定的memset()调用.
在一个相关的主题上,我也对任何阻止GCC在cmovcc指令完成工作时分支的hack感兴趣(我知道使用&,+等代替&&).
非常感谢您的帮助.
@FrankH:
这基本上就是我最终做的事情.这是我的代码:
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
}
Run Code Online (Sandbox Code Playgroud)
请注意,您的示例适用于您的测试设置,但通常无法正常工作.GCC可以改变方向标志,因此需要一条cld指令.此外,你必须告诉gcc %rdi并且%rcx将被stos指令更改,并且由于gcc不允许你指定寄存器既是输入又是破坏,你必须使用笨拙的"+"语法(这也会破坏你的输入值) .
由于"cld"指令在Nehalem上具有4个周期的延迟,因此这不是最佳的.GCC在内部跟踪标志寄存器状态(AFAICT),因此每次都不需要发出该指令.
我不知道 GCC,但在较新版本的 MSVC 下,使用循环进行设置/复制强制使用REP STOS(并且它仍然允许对已知大小和自动矢量化进行优化),它可能在 GCC 下尝试一下。
检查 GCC 是否有类似于 的内置函数的替代方法__stosq,否则您可能需要进行内联汇编,但这在 GCC 下一点也不坏(而且它可能是最简单、最快的方法)。
你的第二个问题是通用的方式来真正得到一个好的答案,因为它取决于当前的情况,但是,GCC 在优化分支方面应该做得足够好,除了特定的极端情况(使用SETCC// MOVCC)FMOVCC。