【问题标题】:Branch-free implementation of f(x) := if x == 0 then 0 else (x * log(x))f(x) 的无分支实现:= if x == 0 then 0 else (x * log(x))
【发布时间】:2012-11-04 09:30:06
【问题描述】:

我有这个 C 函数:

double f(int x)
{
    if (x <= 0)
        return 0.0;
    else
        return x * log(x);
}

我在一个紧密的循环中调用它,并且想摆脱分支以查看它是否可以提高性能。

我不能用这个:

double f(int x)
{
    return x * log(x);
}

因为它在 x == 0 时返回 NaN(大约 25% 的时间是这样)

有没有其他方法来实现它,使它在x == 0时返回0,但仍然摆脱分支?

(我不太关心负输入,因为这些是错误,而零则不是。)

【问题讨论】:

  • ?: 也是分支,但如果你想摆脱if-else,那么你可以使用它
  • 嗯,你确实有一个分段函数。很难想象如何在没有条件的情况下评估它。但是请检查您的程序集,它可能会通过条件移动替换分支。
  • 此计算的性能将完全由log 主导。因此,您想要那里的分支,因为如果您要丢弃答案,您不想调用log。
  • 对数在 x86-64 上实现为函数。不分支但调用昂贵的函数不太可能总是带来比分支更多的东西并且偶尔不调用它。
  • 为了测试性能,只需将函数替换为return x * log(x)即可。当然,这给出了错误的答案,但它并不比您可能想出的任何无分支代码慢。因此,除非它比您拥有的速度快得多,否则您可以停止。没有必要真正想出无分支代码,因为您已经确定它无济于事。

标签: c optimization nan logarithm branch-prediction


【解决方案1】:

首先注意log(1) = 0。然后你可以把问题写成 x * log(y),如果 x

像 y = (x > 0)*x + (x

double f(int x) {
    return x * log((x > 0)*x + (x <= 0));
}

这仅取决于 log(1) 和四个整数操作是否比分支差。

【讨论】:

  • 有效。正如你所说,它可能比分支慢很多。不过,它确实为 OP 的问题提供了一个完美的答案,所以 +1。
  • 我很好奇 log(1) 是否是一个提早退出的特例。尽管如此,这两个比较、加法和乘法可能需要比一个分支更长的时间。
  • 当然,这段代码是否真的是无分支的,这取决于编译器,但它有一个很好的特性,看起来可能是这样的;-)
  • 但是编译器会从哪里得到一个分支呢?来自比较运算符?
  • @Hodapp:正确。 x86 有 setle 和 setg 条件指令,ARM 允许所有指令是条件的,但不是每个架构都这样做。
【解决方案2】:

编译器扩展可以在这里提供帮助。在 GCC 中,您可以这样做:

if(__builtin_expect(x > 0, 1)) {
    return x * log(x);
}
return 0.0;

GCC 将生成支持x &gt; 0 == 1 分支的机器代码。

如果您不关心负数,那么您可以将x == 0 视为不太可能的分支:

if(__builtin_expect(x == 0, 0)) {
    return 0.0;
}
return x * log(x);

如果您不在 GCC 上,您应该检查编译器的文档,看看它是否提供了类似的功能。

请注意,它仍然不是无分支的。只是可能的分支需要更少的时间。

【讨论】:

  • 当 x 为 0“大约 25% 的时间为真”时,我认为偏爱 x &gt; 0 没有帮助。
  • @PascalCuoq 如果x 25% 的时间0。所以这就是这里的青睐。至少我是这么理解这个问题的。
  • 这消除了更常见情况下的跳跃,但不是分支,就像我 understand the term 一样。它可能仍然有助于 OP,所以我赞成。
【解决方案3】:

任何无分支代码都必须包含x * log(x) 的计算,以涵盖“正常”情况。

因此,在尝试提出无分支代码之前,请单独测量x * log(x) 的速度。除非它比您拥有的代码快得多,否则这里没有什么重要的收获。我怀疑它不会。

【讨论】:

    猜你喜欢
    • 2020-11-04
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    • 2020-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-19
    相关资源
    最近更新 更多