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

当阵列大于 800,000,000 时,我的 Fortran 筛会显著变慢

闻人越
2023-03-14

我是一名物理学家,最近我一直在与Fortran合作。起初,我广泛使用Java进行娱乐,因为它是我学习的第一门语言,但我放弃了它,转而使用Fortran和C。我对素数有一种业余爱好,所以我创建了一个素数筛。我能在15秒内找到所有2^31以内的素数。这是Java的最大数组大小,所以到此为止。我小心翼翼地移植代码(我的意思是,小心点,我很沮丧我的代码太慢,找不到错误,所以我将Fortran代码移植回Java,以验证这不是我的错,然后将其移植回Fortran,擦除每次迭代!)。问题是,大约80000000 Fortran将陷入停顿。至此,它击败了Java,但在那之后,它的速度慢得惊人。我花了几个小时来绘制它并拟合曲线。它速度呈指数级下降,可能需要数百年才能解决Java级别的问题。我问过很多人都没有结果。为什么我会这样?!?!有没有聪明的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 Gig内存

共有3个答案

邢硕
2023-03-14

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

山阳辉
2023-03-14

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

您的原始代码无法将< code >素数的所有元素设置为< code>0。当第一次遇到这些语句时,很有可能:

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,因此您的算法无法将 及其倍数标记为prime。情况变得更糟了!除非 prime的元素被初始化为 0,否则无法保证 prime(a)0 ,并且您的程序可以运行到完成,而无需将数字标记为prime。我预计这个错误会使您的代码更快,因为它除了偶然之外不会进入内部循环。

也许更快,但如此破碎,不值得衡量其性能

关冠宇
2023-03-14

我看到了几种可以加快代码速度的方法,尽管它们似乎都不能解释性能的急剧下降。最有可能的罪魁祸首似乎是亚历山大·沃格特(Alexander Vogt)建议的RAM限制。

您应该做的第一件事是将< code>prime从< code>integer改为< code>logical array。这降低了内存需求,也加快了< code>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中,如果你声明素数(1:max)=0,然后只切换01的值,我会认为Matlab将数组视为逻辑数组。Java可能也在做同样的事情。这可以解释为什么您的Java代码不会受到性能下降的影响(假设RAM约束确实是问题所在)。

编辑:我做了一些实验。

在我的机器上(Debian Linux 64位,i3,16GB RAM,如果14带有默认标志),它花了22秒,最大= 8亿(8E8)最大 =2E9 需要 60 秒。这不是问题中报告的小时数。同样在每种情况下,素数数组碰巧被初始化为零。

此外,使用integer(1)使程序运行速度比使用integer(4)快33%。使用逻辑(1),它的运行速度比使用integer(1)快不到5%。这种行为可能是由于更好地使用了cash,因为Prime的每个元素在内存中占用的空间更少,因此处理器可以用当前现金中的数据更快地进行大量迭代。

我由此得出的结论是,正如Alexander Vogt所指出的,罪魁祸首是缺少RAM,正如HighPerformanceMark所指出的,作者的体验很有可能没有受到初始化< code>prime数组的遗漏的影响(尽管这绝对不应该发生)。此外,我怀疑Java将< code>prime声明为一个逻辑数组,这就是为什么那里没有出现问题。(虽然2^31在爪哇用了15秒?这里使用的Fortran代码与此相去甚远。真的是相同的代码吗?)

 类似资料:
  • 所以,我正在尝试匹配 2 个不同的数组。如果相同的单元格匹配,我想使用 .slice 方法从一个数组中删除该单元格。 编辑:我想做的是从数组1中移除一个数字,如果数组2包含一个匹配的数字。代码现在的工作方式是只删除1个条目。我要删除第一个数组中的所有条目。 我尝试运行这个,在控制台日志下,array1没有变化。似乎拼接方法没有删除任何单元格。我搜索了SE,但没有找到任何可以帮助我的东西。

  • 我正在从一个2.37GB的RDF数据集进行查询,其中包含大约1700万个三元组,并且还维护了该数据集的lucence索引。我尝试了jena-text模块的文本查询,它是在存储的lucene索引的基础上进行搜索的。但是它的性能相当慢,对于一个非常慢的搜索查询需要4秒或更多的时间。 然而,当我使用luncene索引查看器'luke'。索引似乎没有问题,当我从索引中搜索特定的术语时,搜索它需要几毫秒的时

  • 当我将本地值设置为1时,操作正常,但当设置为2时,错误消息报告如下

  • 我有这个功能: GetAccounts是一个对象数组 问题是,我的一个同事告诉我,我正在用这行字修改数组: 但我真的不明白为什么这被认为是突变?我确信我创建了原始数组的副本并对其进行了修改。 有什么建议如何解决这个问题,请?

  • 我目前有点卡住了,我需要使用JOLT转换JSON,但就我而言,我无法获得与我一起工作的数据/结构。 我有以下需要转换的数据集: 预期的结果应该是这样的: 我可能在我想要的结果中犯了一个错误,但基本上,“riskItemAllRisk”部分需要在一个数组中。 我正在进行的JOLT转换是: 为可怕的代码道歉,但这对我来说是第一次。 谢谢。