由于使用函数指针代替if-block而导致的效率损失

时间:2017-11-16 21:14:05

标签: performance fortran intel-fortran

假设我们有一个Fortran函数(例如数学优化算法)作为输入,另一个Fortran函数:

myOptimizer(func)

现在,根据用户的选择,输入功能可以来自几个不同功能的列表。这个选择列表可以通过if-block实现:

if (userChoice=='func1') then
    myOptimizer(func1)
elseif (userChoice=='func2') then
    myOptimizer(func2)
elseif (userChoice=='func3') then
    myOptimizer(func3)
end if

或者,我也可以定义函数指针,并将其写为,

if (userChoice=='func1') then
    func => func1
elseif (userChoice=='func2') then
    func => func2
elseif (userChoice=='func3') then
    func => func3
end if
myOptimizer(func)

根据我使用带有O2标志的英特尔Fortran编译器2017的测试,第二种实现方式恰好比几个因素慢(比if-block实现慢4-5倍)。从软件开发的角度来看,我非常喜欢第二种方法,因为它会产生更加简洁和清晰的代码,至少在我的问题中,有一个固定的工作流程,具有不同的工作流输入功能。但是,在这个问题上,表现同样重要。

在所有Fortran代码中,间接函数调用是否会导致性能下降?或者它是编译器相关的问题?有没有使用间接函数调用而没有性能损失的解决方案?其他语言如C / C ++怎么样?

1 个答案:

答案 0 :(得分:1)

这是一个纯粹的猜测,基于编译器通常如何工作以及可能解释4-5x性能差异的原因。

在第一个版本中,编译器可能会将myOptimizer()内嵌到每个调用网站中,其中func1func2func3内联到优化程序中,因此当它运行时没有实际的函数指针或函数调用发生。

间接函数调用并不比现代x86硬件上的常规函数​​调用昂贵得多。这是缺乏内联真正的伤害,特别是对于FP代码。在函数调用周围溢出/重新加载所有浮点寄存器是很昂贵的,特别是如果函数相当小的话。

即。你可能会伤到的是你的第二个版本说服编译器不要撤消间接。在C / C ++中也是如此。

手持编译器使快速asm可能意味着你必须以第一种方式编写它,除非有一个可以使用的配置文件引导优化选项,这可能会使编译器意识到这是一个热点,值得更加努力与源写的第二种方式。 (对不起,我不使用Fortran,我只知道英特尔C / C ++编译器的一些选项,从http://gcc.godbolt.org/上查看其asm输出与gcc和clang)

要查看我的假设是否正确,请检查编译器生成的asm。如果第一个版本实际上没有将函数指针传递给myOptimizer的独立定义,但是第二个版本确实存在,那可能就是它的所有内容。

有关查看编译器输出的更多信息,请参阅How to remove "noise" from GCC/clang assembly output?。 Matt Godbolt的CppCon2017演讲:“What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”是阅读编译器输出的好介绍以及你可能想要的原因。