gpt4 book ai didi

gcc - 强制 GCC 在 memset() 调用中使用重复前缀

转载 作者:行者123 更新时间:2023-12-03 23:15:46 25 4
gpt4 key购买 nike

我正在开发一个性能至关重要的应用程序。我希望 GCC 将对 memset() 的某些特定调用转换为带有重复前缀的指令,例如“rep stos QWORD PTR es:[rdi],rax”。当大小既已知又很小时,GCC 会自动执行此操作。

然而,GCC 通过 PLT 调用 memset() 将调用映射到具有随机长度的 memset(),这会导致分支预测错误,因为分支预测器缓存是冷的。

有没有办法强制 GCC 做我想做的事情(内联汇编之外)?请注意,我不希望整个程序出现这种行为,只希望某些特定的 memset() 调用出现这种行为。

在一个相关的主题上,我也对在 cmovcc 指令完成这项工作时阻止 GCC 分支的任何 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) 所以它不需要每次都发出那个指令。

最佳答案

如果要强制执行此操作,为什么要排除内联汇编作为选项?

#define my_forced_inline_memset(dst, c, N) \
__asm__ __volatile__( \
"rep stosq %%rax, (%%rdi)\n\t"
: : "D"((dst)), "a"((c)), "c"((N)) : "memory");

在演示程序中使用它,例如:
int main(int argc, char **argv)
{
my_forced_inline_memset(argv[0], 0, argc);
return 0;
}

为我创建了这个程序集:
00000000004004b0 <main>:
4004b0: 89 f9 mov %edi,%ecx
4004b2: 31 c0 xor %eax,%eax
4004b4: 48 8b 3e mov (%rsi),%rdi
4004b7: f3 ab repz stos %rax,%es:(%rdi)
4004b9: c3 retq

这不是为什么 GCC 选择做不同的解释,但正如所说,如果你想强制你可以的行为,并且如果你明确知道你需要它的地方,那么调用某种特殊定义的方法并没有错你自己的 memset 吗?

注: repz stos %rax,(%rdi) (或 Intel 语法 QWORD PTR 等效)与 memset() 不同因为 memset() 的粒度是一个字节。以上与 memset(..., c, N * 8) 相同因为那个。记住这一点。

编辑:如果您将代码编写为:
#include <stdint.h>                        // for uintptr_t
#define my_forced_inline_memset(dst, c, N) \
__asm__ __volatile__( \
"rep stos %1, (%0)\n\t" \
:: "D"((dst)), "a"((uintptr_t)(c)), "c"((N)/sizeof(uintptr_t)) \
: "memory");

它为 32 位和 64 位编译。

关于gcc - 强制 GCC 在 memset() 调用中使用重复前缀,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10761950/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com