在C中,如果我有一个看起来像
的函数调用// main.c
...
do_work_on_object(object, arg1, arg2);
...
// object.c
void do_work_on_object(struct object_t *object, int arg1, int arg2)
{
if(object == NULL)
{
return;
}
// do lots of work
}
然后编译器会在main.o中生成很多东西来保存状态,传递参数(在这种情况下希望在寄存器中),并恢复状态。
但是,在链接时,可以观察到arg1和arg2未在快速返回路径中使用,因此清理和状态恢复可能会短路。链接器是否会自动执行此类操作,或者是否需要启用链接时优化(LTO)才能使此类工作正常工作?
(是的,我可以检查反汇编的代码,但我对编译器和链接器的行为以及多种体系结构感兴趣,所以希望从别人的经验中学习。)
假设分析显示此函数调用值得优化,我们是否应该期望以下代码明显更快(例如,无需使用LTO)?
// main.c
...
if(object != NULL)
{
do_work_on_object(object, arg1, arg2);
}
...
// object.c
void do_work_on_object(struct object_t *object, int arg1, int arg2)
{
assert(object != NULL) // generates no code in release build
// do lots of work
}
答案 0 :(得分:2)
由于编译器/链接器对此的支持并不普遍,因此您可以以一种获得大部分好处的方式编写代码,但代价是将函数的逻辑拆分为两个位置。
如果你有一个几乎不需要任何代码的快速路径,但经常发生这种情况,那就把它放在一个标题中,这样它就会被内联,然后调用函数的其余部分(你私有的,所以它可以假设内联部分中的任何检查已经完成。)
e.g。 par2处理数据块的例程具有galois16因子为零时的快速路径。 (dst[i] += 0 * src[i]
是无操作,即使*
是Galois16中的乘法,+=
是GF16加法(即按位异或))。
注意the commit in question如何将旧函数重命名为InternalProcess
,并添加一个检查快速路径的新template<class g> inline bool ReedSolomon<g>::Process
,否则调用InternalProcess
。 (以及制作一堆不相关的空格更改,以及一些ifdefs
...它最初是2006年的CVS提交。)
提交中的评论声称修复速度总体提高了8%。
答案 1 :(得分:0)
设置或清理状态代码都不能短路,因为生成的编译代码是静态的,并且它不知道程序执行时会发生什么。因此编译器将始终必须设置整个参数堆栈。
考虑两种情况:一个object
是nil
,另一个不是。汇编代码如何知道是否将其余参数放在堆栈上?特别是因为调用者负责将参数放在适当的位置(堆栈或注册表)。