Fortran的精度和速度与C

Fortran的精度和速度与C,c,fortran90,gfortran,C,Fortran90,Gfortran,这个问题可能已经讨论过几百次了。我不是想说 任何一种语言都是好是坏。我只是想学习如何加速我的C代码。 这里有两个代码来计算π 第一个是Fortran90: program calcpi implicit none integer :: i real*8 :: pi pi=0.0 do i = 0,1000000000 pi = pi + 1.0/(4.0*i+1.0) pi = pi - 1.0/(4.0*i+3.0) end do pi = pi * 4.0 write(*,

这个问题可能已经讨论过几百次了。我不是想说 任何一种语言都是好是坏。我只是想学习如何加速我的C代码。 这里有两个代码来计算π

第一个是Fortran90:

program calcpi
implicit none
integer :: i
real*8 :: pi

pi=0.0
do i = 0,1000000000
   pi = pi + 1.0/(4.0*i+1.0)
   pi = pi - 1.0/(4.0*i+3.0)
end do

pi = pi * 4.0

write(*,*) pi

end program calcpi
第二个是C:

#include<stdio.h>
#define STEPCOUNTER 1000000001
int main(int argc, char * argv[])
{
long i;
double pi=0;
#pragma omp parallel for reduction(+: pi)
for ( i=0 ; i < STEPCOUNTER; i++){
   /*pi/4=1/11/3+1/51/7+...
   To avoid the need to continually change
   the sign (s=1; in each step s=s*-1 ),
   we add two elements at the same time.*/

   pi+=1.0/(i*4.0+1.0);   
   pi-=1.0/(i*4.0+3.0);   
//   pi = pi +  1.0/(i*4.0+1.0);
//   pi = pi -  1.0/(i*4.0+3.0);
}

 pi=pi*4.0;
 printf("Pi=%lf\n",pi);
return 0;
}
CPU是Intel(R)Xeon(R)CPU 5160@3.00GHz

下面是运行每段代码所需的时间:

[oz@centos ~]$ time ./calcpi.c.o 
Pi=3.141593

real    0m33.270s
user    0m33.261s
sys     0m0.000s
[oz@centos ~]$ time ./calcpi.fort.o 
   3.1415926553497115     

real    0m27.220s
user    0m27.208s
sys     0m0.001s
Fortran大约快20%。 我的问题是,在保持稳定性和准确性的前提下,什么是最好的编译器标志

(是的,我知道man gcc,我想知道用户的意见)

谢谢你的意见

结果,在没有OpenMP pragma的情况下:

[oz@centos ~]$ time ./calcpi.c.o 
Pi=3.141593

real    0m32.892s
user    0m32.885s
sys     0m0.001s
其他结果,在不更改代码本身的情况下:

$ gcc -O2 calcpi.c -o calcpi.c.o
$ time ./calcpi.c.o 
Pi=3.141593

real    0m21.085s
user    0m21.078s
sys     0m0.000s
$ gfortran -O2 calcpi.c -o calcpi.c.o
$ time ./calcpi.fort.o 
   3.1415926553497115     

real    0m26.892s
user    0m26.888s
sys     0m0.000s

通过以双精度进行所有计算,修改Fortran程序,使其与C版本相对应:


program calcpi
  implicit none
  integer :: i
  integer, parameter :: p = selected_real_kind(15)
  real(p) :: pi

  pi=0.0_p
  do i = 0,1000000000
     pi = pi + 1.0_p/(4.0_p*i+1.0_p)
     pi = pi - 1.0_p/(4.0_p*i+3.0_p)
  end do

  pi = pi * 4.0_p

  write(*,*) pi

end program calcpi
在Xeon X3450(2.67 GHz)上的x86_64-linux-gnu上使用GCC 4.4.3使用-O2进行编译时,我得到了以下计时结果:

$ time ./calcpi_c Pi=3.141593 real 0m13.903s user 0m13.860s sys 0m0.010s $ time ./calcpi_fort 3.1415926530880767 real 0m13.876s user 0m13.840s sys 0m0.000s $time./calcpi\u c Pi=3.141593 real 0m13.903s 用户0m13.860s sys 0m0.010s $time./calcpi_fort 3.1415926530880767 实际0m13.876s 用户0m13.840s 系统0m0.000s
看,它们或多或少是无法区分的。对于这样一个简单的例子,这与我们所期望的差不多。

如果没有OpenMP pragmas,结果会是什么?另外,您是否尝试反汇编代码以查看差异在哪里?一般的优化方法可能是将STEPCOUNTER乘以4,然后每次将i增加4,以便删除(i*4.0)乘法?在分母中使用整数也会有所帮助。无论哪种方式,要比较两个示例,我认为Alexandre关于查看生成的指令的评论将提供最好的洞察力。正如LaceySnr所说,要使用整数作为分母,您可以使用类型
uint32\u t
作为
I
,只要
步进计数器不超过
2**30-4
。然后它是
pi+=1.0/(i+1)
假设您也将循环更改为
(i=0;i<4*步进计数器;i+=4)
。您甚至可以在
1
处启动
i
,这样
pi+=…
就不需要在那里添加。看看这些变化是否会对启用优化产生影响,这将是一件有趣的事情。它们不一定会有帮助,但值得一试。此外,如果你从最小的术语开始添加,而不是从最大的术语开始添加,那么这个系列的精确度会稍微好一些。我想OMP并行意味着你不一定知道它们的相加顺序,但我怀疑如果你写循环以增加数量级的顺序来相加,那么OMP实际上更有可能以接近这个数量级的顺序来相加。我不确定它是否会影响比较,但是我认为您需要在C代码中为OpenMP pragma使用-fopenmp标志才能产生效果。!谢谢,我还有很多东西要用Fortran学习!你的回答肯定会给我添麻烦!在我接受你的回答之前,我会再等一会儿,看看是否有人有更多的见解。 $ time ./calcpi_c Pi=3.141593 real 0m13.903s user 0m13.860s sys 0m0.010s $ time ./calcpi_fort 3.1415926530880767 real 0m13.876s user 0m13.840s sys 0m0.000s