【发布时间】:2021-07-06 20:19:37
【问题描述】:
我在 Fortran 和 C++ 中分别实现了一个函数:
#include <math.h>
void dbl_sqrt_c(double *x, double *y){
*y = sqrt(*x - 1.0);
return;
}
pure subroutine my_dbl_sqrt(x,y) bind(c, name="dbl_sqrt_fort")
USE, INTRINSIC :: ISO_C_BINDING
implicit none
real(kind=c_double), intent(in) :: x
real(kind=c_double), intent(out) :: y
y = sqrt(x - 1d0)
end subroutine my_dbl_sqrt
我在编译器资源管理器中比较了它们:
Fortran:https://godbolt.org/z/froz4rx97
C++:https://godbolt.org/z/45aex99Yz
按照我阅读汇编程序的方式,它们的作用基本相同,但 C++ 会检查 sqrt 的参数是否为负,而 Fortran 不会。我使用 googles benchmark 比较了它们的性能,但它们非常匹配:
--------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------
bm_dbl_c/8 2.07 ns 2.07 ns 335965892
bm_dbl_fort/8 2.06 ns 2.06 ns 338643106
这是有趣的部分。如果我把它变成基于整数的函数:
void int_sqrt_c(int *x, int *y){
*y = floor(sqrt(*x - 1.0));
return;
}
和
pure subroutine my_int_root(x,y) bind(c, name="int_sqrt_fort")
USE, INTRINSIC :: ISO_C_BINDING
implicit none
integer(kind=c_int), intent(in) :: x
integer(kind=c_int), intent(out) :: y
y = floor(sqrt(x - 1d0))
end subroutine my_int_root
那么这就是他们开始分歧的地方:
--------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------
bm_int_c/8 3.05 ns 3.05 ns 229239198
bm_int_fort/8 2.13 ns 2.13 ns 328933185
Fortran 代码似乎并没有因为这种变化而明显变慢,但 C++ 代码却减慢了 50%。这似乎相当大。这些是程序集:
Fortran:https://godbolt.org/z/axqqrc5E1
C++:https://godbolt.org/z/h7K75oKbn
Fortran 程序集看起来非常简单。它只是增加了double 和int 之间的转换,其他不多,但C++ 似乎做得更多,我不完全理解。
为什么 C++ 汇编器要复杂得多?如何改进 C++ 代码以实现匹配性能?
【问题讨论】:
-
你被糟糕的默认设置和与过时机器的兼容性所困扰:糟糕的默认设置是 gcc 设置
errno用于浮点计算(尽管 C 语言不需要这样做),以及与 x86 的兼容性没有比 SSE2 更好的 SSE 指令的机器。如果您想生成体面的代码,请将-fno-math-errno -msse4添加到compiler flags -
这项工作几乎完美:bm_int_c/8 2.08 ns; bm_int_fort/8 2.09 ns 如果你写一个答案,我会接受。
标签: c++ performance assembly x86-64