当阵列大于800,000,000时,我的Fortran筛子会显着减慢

时间:2014-07-29 02:37:15

标签: performance fortran primes

我是一名物理学家,最近我一直在与Fortran合作。最初我使用Java进行广泛的娱乐,因为它是我学习的第一种语言,但我已经放弃了它用于Fortran和C ++。我对素数有业余热情,因此我创建了一个素数筛子。我能够在15秒内找到最多2 ^ 31的所有素数。这是Java的最大数组大小,所以这就是结束。我小心地移植了代码(我的意思是,我很沮丧,我的代码很慢,而且我找不到错误,我将我的Fortran代码移植回Java以验证它不是我的错,然后移植回来到Fortran,擦除每次迭代!)。问题是大约800,000,000 Fortran将陷入停顿。到目前为止,它击败了Java,但在此之后,它的速度非常慢。我花了几个小时绘制它并拟合曲线。它以指数方式减速,可能需要数百年才能解决Javas级别。我问过很多人无济于事。为什么这发生在我身上?!?!有没有明智的Fortran程序员可以帮助我?我正在运行Macbook Pro 2013年末i5。我的代码如下。

program sieve
integer(4),allocatable:: prime(:)
integer(4)::a,max,b,primeCount
write(*,*)"Welcome to the slow prime number sieve!"
write(*,*)"--------------------------------------------"
write(*,*)"Up to what numbers do you need to find primes for?"
write(*,*)"Enter a number below 2^(32-1)"
read*, max

primeCount=0
allocate(prime(max))
prime(1)=1

    do a=2,int(sqrt(real(max))) !the main loop
        if(prime(a)==0)then !if the number is marked as prime procede
            do b=2*a,max,a  !eliminate all the numbers that are multiples of the number
                if(prime(b)==0)then !but only spend time eliminating if the number is still marked prime
                    prime(b)=1
                end if
            end do
        end if
    end do

do a=1,max
    if(prime(a)==0)then
        primeCount=primeCount+1
    end if
end do

print*, primeCount

end program

编辑:我的机器安装了4台ram

3 个答案:

答案 0 :(得分:4)

我可以看到你可以做的几件事来加速代码,虽然它们似乎都不能解释你遇到的性能急剧下降。亚历山大·沃格特(Alexander Vogt)建议,最可能的罪魁祸首似乎是RAM约束。

您应该做的第一件事是将primeinteger更改为logical数组。这降低了内存需求,也加快了if (prime(a)==0)的评估速度。

代码的相关部分如下所示

logical(1),allocatable:: prime(:)

primeCount=0
allocate(prime(max))
prime = .false.
prime(1)=.true.

    do a=2,int(sqrt(real(max))) !the main loop
        if(.not. prime(a))then !if the number is marked as prime procede
            do b=2*a,max,a  !eliminate all the numbers that are multiples of the number
                if(.not. prime(b))then !but only spend time eliminating if the number is still marked prime
                    prime(b)=.true.
                end if
            end do
        end if
    end do

do a=1,max
    if(.not. prime(a))then
        primeCount=primeCount+1
    end if
end do

我不做任何Java编程但是在Matlab中如果你声明prime(1:max)=0然后只切换值01我会认为Matlab将数组视为一个logical数组。 Java可能也在做同样的事情。这可以解释为什么您的Java代码不会受到性能下降的影响(假设RAM约束确实是问题)。

编辑:我做了一些实验。

在我的机器上(Debian Linux 64位,i3,16GB RAM,带有默认标志的ifort 14)max=800 million (8E8)需要22秒。 max=2E9需要60秒。这不是问题中报告的小时数。同样在每种情况下,prime数组恰好都被初始化为零。

此外,使用integer(1)使程序运行速度比使用integer(4)快33%。 logical(1)使用integer(1)的速度比使用prime快5%。这种行为可能是由于更好地使用现金,因为prime的每个元素在内存中占用的空间更少,因此处理器可以进行更多次迭代,当前现金数据更快地通过循环。

我将从中得出的结论是,罪魁祸首是亚历山大·沃格特所指出的缺乏内存,并且作者的经验很可能不会被遗漏初始化{{{ HighPerformanceMark指出,1}}数组(虽然绝对不应该发生)。此外,我怀疑Java将prime声明为逻辑数组,这就是为什么问题没有在那里发生的原因。 (虽然在Java中15秒内有2 ^ 31?这里使用的Fortran代码与此无关。真的是相同的代码吗?)

答案 1 :(得分:3)

这更像是我之前评论的扩展版本,而不是答案。我认为你没有将prime的所有元素设置为0 的错误是会影响代码的运行时间,尽管我的初步评估反正是它让它更快。

您的原始代码未能设置为0 primes的所有元素。当第一次遇到这些陈述时很可能是这样的:

do a=2,int(sqrt(real(max))) !the main loop
    if(prime(a)==0)then !if the number is marked as prime procede

prime(2)的值不是0,因此您的算法无法将2及其倍数标记为素数。它变得更糟!除非prime的元素初始化为0,否则prime(a)无法保证为0,并且您的程序可以在没有将数字标记为素数的情况下运行完成。我希望这个错误能让你的代码更快,因为它不会偶然进入内循环。

可能更快但是如此破碎它不值得衡量它的表现。

答案 2 :(得分:1)

您只能在prime(a)数组中存储奇数。这会将prime数组的大小减小到一半和循环大小。还要确保为编译器使用最佳优化。