Fortran 计算 real*4 的平方根的速度与添加整数一样快?

Fortran computes square root of real*4 as fast as addition of ints?

剧透: 测试程序对结果没有做任何处理,因此优化编译器删除了循环的内容,因此每次什么都不做循环大约需要相同的时间 运行...无论如何,我会保留问题和答案,以防有人(我?)犯同样的错误(再次...)。

原文post: 我想测试与简单加法相比计算平方根要慢多少,并编写了下面的小程序。我得到的结果是它需要大约相同的时间,在这种情况下为 0.3 秒。这是怎么回事?

program sqtest
implicit none
real r, s
integer i,j,n, sq, t

sq=11
n=100000000
r=1.11

if (sq==1) then
 do i = 1,n
  s = sqrt(float(i)*r)
 enddo
 write(*,*) "squareroot"
else
 do j = 1,n
  t = j+4
 enddo
 write(*,*) "plus"
endif


end program

输入sq=1使用平方根。平方根循环也进行乘法和从 intfloat 的转换。

也许您的编译器正在优化代码。您可以通过测量 n 的不同阶数(例如 1e6、1e7、1e8、..、1e10)来测试这一点,并查看时间尺度如何变化。顺便说一句,machine/compiler 上整数的允许范围是多少?

您将在本文档中找到硬件平方根的成本:http://www.agner.org/optimize/instruction_tables.pdf

可以用不同的方式计算 sqrt。通常,它是一个仅涉及加法和乘法运算的迭代过程。 通常 sqrt 计算为 sqrt(x) = x * (1/sqrt(x)),因为 (1/sqrt(x)) 可以比 sqrt(x) 更快地计算。

如果使用 Haswell CPU,单个 SQRTSS 指令的延迟在单精度下为 11 个周期,在双精度下为 16 个周期 (SQRTSD)。 在单精度中,与双精度相比,它需要更少的迭代来收敛到所需的精度。在同一个 CPU 上,有一个延迟为 1 个周期的 sqrt (RSQRTSS) 的近似版本,因此如果您要求激进的优化,您的编译器可能会选择生成此指令。

如果您需要多个独立的平方根,就像在您的示例中一样,代码可以由编译器自动矢量化。存在矢量化变体 VSQRTPS,吞吐量的倒数为 14。在这种情况下,您将大致平均每平方尺 14/8 = 1.75 个周期。

参考文献:

进行此类测试时需要考虑很多事情。您必须首先明确定义要比较的内容。对于这种简单的测试,您还应该停用优化,大多数主要编译器都接受 -O0 选项来停用优化。否则,编译器会发现您没有对计算值做任何事情,甚至 运行 您的循环也没有,因为它没用。

长话短说,我稍微修改了你的程序,让它有这个

program sqtest
implicit none
real r0, r1, r2, s
integer i,n
real :: start, finish


    n=10**9
    call random_number(r0)
    call random_number(r1)
    call random_number(r2)


    call cpu_time(start)
    do i = 1,n
        s = sqrt(r0)
    enddo
    call cpu_time(finish)
    print '("SQRT:      Time = ",f6.3," seconds.")',finish-start

    call cpu_time(start)
    do i = 1,n
        s = r1+r2
    enddo
    call cpu_time(finish)
    print '("Addtition: Time = ",f6.3," seconds.")',finish-start

end program

它在我的系统上给出了以下结果:

ifort 13, n = 10^8
SQRT:      Time =  0.378 seconds
Addtition: Time =  0.202 seconds

ifort 13, n = 10^9
SQRT:      Time =  3.460 seconds
Addtition: Time =  1.857 seconds

gfortran (GCC) 4.9, n = 10^8
SQRT:      Time =  0.385 seconds
Addtition: Time =  0.191 seconds

gfortran (GCC) 4.9, n = 10^9
SQRT:      Time =  3.529 seconds
Addtition: Time =  1.733 seconds

pgf90 14, n = 10^8
SQRT:      Time =  0.380 seconds
Addtition: Time =  0.058 seconds

pgf90 14, n = 10^9
SQRT:      Time =  3.438 seconds
Addtition: Time =  0.520 sec

您会注意到我在代码中调用了 CPU 时间。为了使数字有意义,您应该 运行 每个案例多次并计算时间平均值或选择最小值。最小值是接近您的系统在最佳条件下可以达到的值。 您还将看到结果取决于编译器。 pgf90 显然在加法上给出了更好的结果。我从平方根中删除了 float(i)*。 gfortran 和 pgf90 执行速度非常快(n = 10^9 约 2.6 秒),而 ifort 执行非常慢(n = 10^9 约 7.3 秒)。这意味着 gfortran 和 pgf90 不知何故在那里选择了不同的路径(更快的操作),也许他们做了一些优化,即使我禁用了它?