c - 使用__builtin_expected进行边界检查



我有这个函数,给定一个格雷码,返回下一个格雷码。您可以在此处找到有关其工作原理的更完整说明。问题是我想使这个增量函数模块化,以便递增对应于UINT_MAX的格雷码返回对应于0的格雷码(分别是最高有效位和0)。由于这不是默认行为,因此我为此特殊情况添加了检查。以下是完整的算法:

unsigned next_gray(unsigned gray)
{
    static const unsigned msb
        = 1u << (CHAR_BITS - sizeof(unsigned) - 1u);
    // gray is odd
    if (__builtin_parity(gray))
    {
        if (__builtin_expect(gray == msb, false))
        {
            return 0u;
        }
        else
        {
            unsigned y = gray & -gray;
            return gray ^ (y << 1u);
        }
    }
    // gray is even
    return gray ^ 1;
}

所以,实际的问题实际上是关于分支预测的。我经常读到,只有当一个分支真的有可能被选择或真的不太可能被选择时才使用__builtin_expect,常见的例子是在没有错误的情况下加速程序。

考虑到我不是在处理错误情况,我不确定使用 __builtin_expect 进行这样的边界检查是否是一个好主意。这是使用__builtin_expect的好地方,还是增加最大值是一种足以欺骗分支预测的常见操作?

注意:与往常一样,评论和答案突出显示了我的问题中不清楚的内容:)

我将提供更多上下文:这个函数是库的一部分,为了成为一个库而开发,而不是被任何实际项目使用。因此,添加__builtin_expect意味着我希望人们主要增加其他值而不是最大值;手头没有任何实际项目,我想知道这是否是一个安全的假设。

自GCC在线文档:

您可以使用__builtin_expect为编译器提供分支预测信息。一般来说,你应该更喜欢使用实际的配置文件反馈(-fprofile-arcs),因为程序员在预测他们的程序实际表现方面是出了名的糟糕。但是,在某些应用程序中,很难收集此数据。

这是使用__builtin_expect的好地方,还是增加最大值是一种足以欺骗分支预测的常见操作?

这一切都取决于您的应用。 如果gray的值是均匀分布的,那么它将是 1 分(UINT_MAX+1),但你能肯定地说吗? 这就是为什么文档建议使用 -fprofile-arcs .

gcov维基百科文章实际上包含一个很好的简单示例,介绍如何使用-fprofile-arcsgcov来获取信息以做出明智的决定。

更新:

如果您无法配置文件,那么在所有条件相同的情况下,边缘情况gray == msb不太可能,因此使用__builtin_expect可能是安全的。 但是,如果您因为不知道库将如何使用而无法进行分析,这听起来更像是悲观而不是优化。 如果我使用你的库并且总是传入gray这样它等于msb,那么你的库对我来说就不会那么快了。 编写时未考虑特定应用程序的通用库通常会尝试对一般情况有益,或者不对输入做出任何假设。 这就是为什么你会看到不同的malloc实现,如jemalloc和tcmalloc。 两者都针对非常具体的用例进行了优化,如果您以不像优化的方式使用它,它就不会做得那么好。 此外,这篇博客文章可能会引起您的兴趣。

相关内容

  • 没有找到相关文章

最新更新