我是一名物理学家,最近我一直在与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
答案 0 :(得分:4)
我可以看到你可以做的几件事来加速代码,虽然它们似乎都不能解释你遇到的性能急剧下降。亚历山大·沃格特(Alexander Vogt)建议,最可能的罪魁祸首似乎是RAM约束。
您应该做的第一件事是将prime
从integer
更改为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
然后只切换值0
和1
我会认为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
数组的大小减小到一半和循环大小。还要确保为编译器使用最佳优化。