Julia for 循环比 while 慢?
Julia for loops slower than while?
我在玩 Julia 语言,发现我写的小程序很慢。
怀疑它与 for
循环有某种关系,我重写了它以使用 while
,并且速度提高了大约 15 倍。
我确定我在范围等方面做错了什么,但我不知道是什么。
function primes_for()
num_primes = 0
for a = 2:3000000
prime = true
sa = floor(sqrt(a))
for c in 2:sa
if a % c == 0
prime = false
break
end
end
if prime
num_primes += 1
end
end
println("Number of primes is $num_primes")
end
function primes()
num_primes = 0
a = 2
while a < 3000000
prime = true
c = 2
while c * c <= a
if a % c == 0
prime = false
break
end
c += 1
end
if prime
num_primes += 1
end
a += 1
end
println("Number of primes is $num_primes")
end
@time primes_for()
@time primes()
造成速度差异的不是 for/while,而是 sqrt
。 sqrt returns 浮点数没有帮助,它提升了整数输出的 sqrt 周围的所有其余代码。
请注意,@time 不是在测量 while 和 for 循环,而是在这些循环之外的代码。
如果您要对代码进行基准测试,则其余代码需要相同,删除 sqrt 是该算法的主要优化之一。也可以在测试中删除 c * c
,但这比较棘手。
正如@Vincent Yu 和@Kelly Bundy 在评论中所解释的那样,这是因为 sa = floor(sqrt(a))
创建了一个浮动。那么c
就变成了float,而a % c
就慢了。
您可以将 floor(sqrt(a))
替换为 floor(Int, sqrt(a))
,或者 最好是 ,我认为 isqrt(a)
,其中 returns
Integer square root: the largest integer m
such that m*m <= n
.
这避免了 floor(Int, sqrt(a))
可能向下舍入过多的(不太可能的)事件,如果 sqrt(x^2) = x - ε
由于浮点错误可能会发生这种情况。
编辑: 这里有一个基准来演示(注意 isqrt
的使用):
function primes_for2()
num_primes = 0
for a = 2:3000000
prime = true
# sa = floor(sqrt(a))
sa = isqrt(a)
for c in 2:sa
if a % c == 0
prime = false
break
end
end
if prime
num_primes += 1
end
end
println("Number of primes is $num_primes")
end
1.7.0> @time primes_for()
Number of primes is 216816
6.705099 seconds (15 allocations: 480 bytes)
1.7.0> @time primes_for2()
Number of primes is 216816
0.691304 seconds (15 allocations: 480 bytes)
1.7.0> @time primes()
Number of primes is 216816
0.671784 seconds (15 allocations: 480 bytes)
我注意到在我的计算机上每次调用 isqrt
大约需要 8ns,3000000 次 8ns 是 0.024 秒。调用常规 sqrt
大约需要 1ns。
我在玩 Julia 语言,发现我写的小程序很慢。
怀疑它与 for
循环有某种关系,我重写了它以使用 while
,并且速度提高了大约 15 倍。
我确定我在范围等方面做错了什么,但我不知道是什么。
function primes_for()
num_primes = 0
for a = 2:3000000
prime = true
sa = floor(sqrt(a))
for c in 2:sa
if a % c == 0
prime = false
break
end
end
if prime
num_primes += 1
end
end
println("Number of primes is $num_primes")
end
function primes()
num_primes = 0
a = 2
while a < 3000000
prime = true
c = 2
while c * c <= a
if a % c == 0
prime = false
break
end
c += 1
end
if prime
num_primes += 1
end
a += 1
end
println("Number of primes is $num_primes")
end
@time primes_for()
@time primes()
造成速度差异的不是 for/while,而是 sqrt
。 sqrt returns 浮点数没有帮助,它提升了整数输出的 sqrt 周围的所有其余代码。
请注意,@time 不是在测量 while 和 for 循环,而是在这些循环之外的代码。
如果您要对代码进行基准测试,则其余代码需要相同,删除 sqrt 是该算法的主要优化之一。也可以在测试中删除 c * c
,但这比较棘手。
正如@Vincent Yu 和@Kelly Bundy 在评论中所解释的那样,这是因为 sa = floor(sqrt(a))
创建了一个浮动。那么c
就变成了float,而a % c
就慢了。
您可以将 floor(sqrt(a))
替换为 floor(Int, sqrt(a))
,或者 最好是 ,我认为 isqrt(a)
,其中 returns
Integer square root: the largest integer
m
such thatm*m <= n
.
这避免了 floor(Int, sqrt(a))
可能向下舍入过多的(不太可能的)事件,如果 sqrt(x^2) = x - ε
由于浮点错误可能会发生这种情况。
编辑: 这里有一个基准来演示(注意 isqrt
的使用):
function primes_for2()
num_primes = 0
for a = 2:3000000
prime = true
# sa = floor(sqrt(a))
sa = isqrt(a)
for c in 2:sa
if a % c == 0
prime = false
break
end
end
if prime
num_primes += 1
end
end
println("Number of primes is $num_primes")
end
1.7.0> @time primes_for()
Number of primes is 216816
6.705099 seconds (15 allocations: 480 bytes)
1.7.0> @time primes_for2()
Number of primes is 216816
0.691304 seconds (15 allocations: 480 bytes)
1.7.0> @time primes()
Number of primes is 216816
0.671784 seconds (15 allocations: 480 bytes)
我注意到在我的计算机上每次调用 isqrt
大约需要 8ns,3000000 次 8ns 是 0.024 秒。调用常规 sqrt
大约需要 1ns。