gpt4 book ai didi

c++ - 在比较中,GCC 似乎更喜欢小的即时值。有没有办法避免这种情况?

转载 作者:行者123 更新时间:2023-12-01 13:48:02 24 4
gpt4 key购买 nike

首先是一个微不足道的数学事实:给定整数 nm , 我们有 n < m当且仅当 n <= m - 1 .

GCC 似乎更喜欢较小绝对值的即时值。因此,当 m已知并且满足其他条件,编译器在等效比较表达式中选择一个最小化绝对值的表达式。例如,它更喜欢 n <= 1000n < 1001和 GCC 9.2 翻译了这个

bool f(uint32_t n) {
return n < 1001;
}

进入此 x86汇编代码
f(unsigned int):
cmpl $1000, %edi
setbe %al
ret

这可能有很好的性能原因,但这不是我的问题。我想知道的是: 有没有办法强制 GCC 保持原始比较? 更具体地说,我不担心可移植性,因此,GCC 细节(选项、编译指示、属性等)对我来说是可以的。但是,我正在寻找 constexpr友好的解决方案似乎排除了内联 asm .最后,我的目标是 C++17,它不包括 std::is_constant_evaluated 之类的东西。 . (话虽如此,请尽管不顾我的限制自由地提供答案,因为它可能对其他人仍然有用。)

你可能会问我为什么要做这样的事情。开始了。据我所知(如果我错了,请 纠正我 )这种行为可能是对 x86_64 的“悲观”在以下示例中:
bool g(uint64_t n) {
n *= 5000000001;
return n < 5000000001;
}

由 GCC 6.2 翻译成
g(unsigned long):
movabsq $5000000001, %rax
imulq %rax, %rdi
movabsq $5000000000, %rax
cmpq %rax, %rdi
setbe %al
ret

x86_64 ,使用 64 位立即数的计算有一些限制,可能意味着将这些值加载到寄存器中。在上面的例子中,这发生了两次:常量 50000000015000000000存储在 rax用于乘法和比较。如果 GCC 保留 C++ 代码中出现的原始比较(即针对 5000000001),则不需要第二个 movabs .

这也意味着代码大小的惩罚,我猜这被认为是一个问题,并且更新版本的 GCC(例如 9.2)产生了这个:
g(unsigned long):
movabsq $5000000001, %rax
imulq %rax, %rdi
subq $1, %rax
cmpq %rax, %rdi
setbe %al
ret

因此 10 字节长 movabs被 4 字节长的 subq 取代操作说明。无论如何, subq似乎也没有必要。

最佳答案

这是一个 C++20 解决方案,遗憾的是,我不能使用

#include <cstdint>
#include <type_traits>

template <class U>
bool less_asm(U n, U m) noexcept {
bool r;
asm("cmp%z[m]\t{%[m], %[n]|%[n], %[m]}"
: "=@ccb"(r) : [n]"r"(n), [m]"re"(m) : "cc");
return r;
}

template <class U>
constexpr bool less(U n, U m) noexcept {
if (std::is_constant_evaluated())
return n < m;
return less_asm(n, m);
}

static_assert(less(uint64_t(0), uint64_t(1)));

bool g(uint64_t n) {
n *= 5000000001;
return less<uint64_t>(n, 5000000001);
}

GCC 9.2 (with -O2 -std=c++2a) 生成这个:
g(unsigned long):
movabsq $5000000001, %rax
imulq %rax, %rdi
cmpq %rax, %rdi
setc %al
ret

更新:下面的代码片段显示了两个改进:
  • 它适用于 C++17,但需要 GCC-9.1 或更高版本。 (感谢 Peter Cordes ' 建议使用 __builtin_constant_p() 而不是 std::is_constant_evaluated() 。以前的 GCC 版本提示 asm 出现在 constexpr 函数中。)
  • 它在范围内引入较少的名称。 (通过使用 lambda 而不是 less_asm 。)

  • template <class U>
    constexpr bool less(U n, U m) noexcept {
    if (__builtin_constant_p(n < m))
    return n < m;
    return [&]{
    bool r;
    asm("cmp\t{%[m], %[n]|%[n], %[m]}"
    : "=@ccb"(r) : [n]"r"(n), [m]"re"(m) : "cc");
    return r;
    }();
    }

    关于c++ - 在比较中,GCC 似乎更喜欢小的即时值。有没有办法避免这种情况?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59117603/

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