当前位置: 首页 > 知识库问答 >
问题:

比较Fortran和C++汇编程序的int=楼层(sqrt(…))

关飞翔
2023-03-14

我用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              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程序集看起来很简单。它只是增加了doubleint之间的转换,其他没有什么,但C++似乎做了更多的事情,这一点我不完全理解。

为什么C++汇编程序要复杂得多?我如何改进C++代码以获得匹配的性能?

共有1个答案

令狐弘益
2023-03-14

TL;DR:您被错误的默认值和与过时机器的兼容性所困扰:错误的默认值是浮点计算的gcc设置errno(尽管C语言不需要这样做),以及与没有比SSE2更好的SSE指令的x86机器的兼容性。如果希望生成体面的代码,请将-fno-math-errno-msse4添加到编译器标志中。

包含浮点硬件的现代处理器体系结构通常提供平方根计算作为基本操作(指令),如果平方根指令的操作数超出范围(负),则在浮点环境中设置错误标志。另一方面,旧的指令集体系结构可能没有浮点指令,或者没有硬件加速的平方根指令,因此C语言允许实现将errno设置在超出范围的参数上,但是errno作为线程本地内存位置实际上阻止了任何sane体系结构直接从平方根指令设置errno。为了获得良好的性能,gcc通过调用硬件指令(sqrtsd)内联了平方根计算,但是为了设置errno,它单独检查参数的符号,并且仅在参数为负的情况下调用库函数,因此库函数可以设置errno。是的,这是疯狂的,但反过来也是正常的。您可以通过在编译器标志中设置-fno-math-errno来避免这种没有人需要或想要的脑损伤。

最近的x86-64处理器拥有比AMD最初开发的x86-64(仅包含SSE2向量/浮点指令)更多的指令。在添加的指令中包括允许受控舍入/截断的浮点/整数转换指令,因此这不必在软件中实现。通过指定支持这些指令的目标,例如使用-msse4编译器标志,可以让gcc使用这些新指令。请注意,如果生成的程序运行在不支持这些指令的目标上,这将导致生成的程序出错,因此生成的程序的可移植性会较低(尽管这不会明显降低源代码的可移植性)。

 类似资料:
  • 我试图理解这个简单的C程序: 当它在汇编代码中时: 第三线和第四线发生了什么 为什么必须使用另外两个寄存器(edi和eax)来代替rsp DWORD PTR[rbp-4]实际发生了什么

  • 本文向大家介绍Javascript的比较汇总,包括了Javascript的比较汇总的使用技巧和注意事项,需要的朋友参考一下 在Javascript应用过程中会遇到各式各样的比较,今天给大家整理了三种情况,一起来学习下。 1.两个对象的比较 Javascript的比较中参杂了一些比较奇怪的特性,我们来看一些比较简单的比较。 由上面的结果可以看出比较两个原始值跟比较对象规则似乎有点不同,比较两个对象值

  • Fortran 语言为科大理学院本科课程。编者自 2000 年起对天文与应用物理系本科生讲授,由于国内尚没有一本完整综合了Fortran90与Fortran77的教材,故编写了此教程用于教学,主要内容源自于如下国内外最新 Fortran90 和经典 Fortran77 教材,并参考了 Internet 上诸多网页,在此向各位原作者(恕不详列)致谢。编者感谢研究生李会民的帮助。

  • 12.2 C语言程序的汇编输出 在Turbo C++或Borland C++编程环境下,我们可TCC或BCC行命令把一个C语言的源程序转换成汇编语言的源程序。通过阅读汇编语言程序可以很准确地知道C语言语句的功能是如何实现的,这样,可为将来学习《编译原理》课程中的"寄存器调度"和"代码生成"等相关知识打下良好的基础。 C语言源程序转换的命令格式如下: TCC -S t1.cpp 或 BCC -S t

  • 1.2.1编程实例 为了对Fortran程序有一个初步了解,下面先介绍几个简单的Fortran源程序。 a) 基本语句 [例1.1] 输入两个数,求算数平均和几何平均值。[e_121_01.f][e_121_01.f90] [计算例] 1.0 2.0 ←键盘输入(a,b)值 1.500000 1.414214 ←计算结果输出至屏幕 程序説明: 程序中第1行是注释行,对程序起说明作用。F77注释行是

  • 博客基本建好了, 未来我们还有更多的工作要做: 将view封装到类中 重写增删改查代码 重写前段模板 重新设计数据库关系 自定义留言板(连接数据库) 添加注册/登陆功能(Form) 做成社区(更多设计思考) ... 更多学习资源: Django Django 官网, 最全面的 Django 知识还是要看官方文档, 我认为写的非常棒 Django Book 这个是很早的一本 Django 教程, 虽