我有这个函数,给定Gray code,返回下一个格雷码。您可以找到有关其工作原理的更完整说明here。问题是我想使这个增量函数模块化,以便递增对应于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
意味着我希望人们大多增加其他值,然后增加最大值;没有任何实际项目,我想知道这是否是一个安全的假设。
答案 0 :(得分:0)
您可以使用__builtin_expect
为编译器提供分支预测信息。 一般情况下,您应该更倾向于使用实际的配置文件反馈(-fprofile-arcs
),因为程序员在预测程序实际执行情况方面非常糟糕。但是,有些应用程序会使用这些数据很难收集。
这是一个使用__builtin_expect的好地方,还是将最大值递增一个足够常见的操作来欺骗分支预测?
这一切都取决于您的申请。如果gray
的值均匀分布,那么它将是(UINT_MAX+1)
中的1,但你能说肯定吗?这就是docs建议使用-fprofile-arcs
。
gcov wikipedia article实际上包含一个很简单的示例,说明如何使用-fprofile-arcs
和gcov
来获取信息以做出明智的决定。
更新
如果您无法进行分析,那么所有条件相同的边缘情况gray == msb
都不太可能,因此您使用__builtin_expect
可能安全。但是,如果您无法了解原因,因为您不知道您的图书馆将如何使用,这听起来更像pessimization而不是优化。如果我使用你的图书馆,并且总是传递gray
使其等于msb
,那么你的图书馆对我来说就不会那么快。考虑到特定应用程序而编写的通用库通常会尝试对平均情况有所帮助,或者不对输入做任何假设。这就是为什么您会看到malloc
的不同实现,例如jemalloc和tcmalloc。两者都针对非常具体的用例进行了优化,如果您使用的方式与其优化的方式不同,那么它也不会这样做。 this blog article也可能对你有用。