我很确定这必须是一个错误......
好的,基本上,当我将一个Math Constant类型传递给power(^)函数并多次迭代时......循环非常慢并且使用了大量的内存分配。这个例子是微不足道的,因为它没有产生任何结果,但我有真正的应用程序,我有这个问题。
function test1(N)
for i = 1:N
x = pi^4
end
end
function test2(N)
for i = 1:N
x = pi*pi*pi*pi
end
end
function test3(N)
pif = float64(pi)
for i = 1:N
x = pif^4
end
end
这是用大N调用函数的结果:
@time test1(10000000)
@time test2(10000000)
@time test3(10000000)
elapsed time: 0.958278949 seconds (320000160 bytes allocated, 56.44% gc time)
elapsed time: 6.341e-6 seconds (80 bytes allocated)
elapsed time: 4.982e-6 seconds (80 bytes allocated)
这是一个错误吗?或者它有合理的解释吗?在一个大循环内为pi提供动力的正确方法是什么?
感谢。
答案 0 :(得分:4)
虽然看起来你正在使用Julia 0.3,而这些结果主要是关于Julia 0.4,但它也有同样的问题,原因是相同的,这是不必要的中间体分配。
要了解正在发生的事情,您可以键入methods(^)
,它将生成为实现^而定义的所有方法的列表。它们有很多,所以我不会列出它们,但重要的是列出了实现它们的Julia文件,在这种情况下,它是Constants.jl。
朱莉娅第一次0.3
julia> @time test1(10000000)
elapsed time: 0.313772825 seconds (320393016 bytes allocated, 37.16% gc time)
在Julia 0.4中,由于更好的垃圾收集器,问题减少了,但仍然存在。
julia> @time test1(10000000)
170.445 milliseconds (20000 k allocations: 305 MB, 6.94% gc time)
julia> @time test2(10000000)
2.355 microseconds (4 allocations: 144 bytes)
如果分配数量异常,则始终怀疑类型不稳定或临时性。
^ h for pi 的定义特别是,lines 70 through 72定义了一个将MathConst提升为幂的通用方法。
for op in Symbol[:+, :-, :*, :/, :^]
@eval $op(x::MathConst, y::MathConst) = $op(Float64(x),Float64(y))
end
另请注意,稍后是specialization for the constant e,并且pi替换为e的新测试没有同样的问题。
julia> function test4(N)
for i = 1:N
x = e^4
end
end
test4 (generic function with 1 method)
运行test4
julia> @time test4(10000000)
108.401 milliseconds (4 allocations: 144 bytes)
如果从 Int 创建 Float ,则可以通过 Float 开始来避免此问题。
julia> function test5(N)
for i = 1:N
x = pi^4.0
end
end
发生了什么
julia> @time test5(10000000)
65.430 milliseconds (4 allocations: 144 bytes)
最后,可以创建一组新的函数(在这种情况下进行测试),特别是将Int定义为避免强制转换的第二个参数。这不是最好的方法,因为它引入了歧义,但它对测试有好处。
julia> for op in Symbol[:+, :-, :*, :/, :^]
@eval $op(x::MathConst, y::Int64) = $op(Float64(x),y)
end
Warning: New definition
^(MathConst{sym}, Int64) at none:2
is ambiguous with:
^(MathConst{:e}, Integer) at constants.jl:122.
To fix, define
^(MathConst{:e}, Int64)
before the new definition.
julia> function test1(N)
for i = 1:N
x = pi^4
end
end
test1 (generic function with 1 method)
然后重新运行
julia> @time test1(10000000)
2.757 microseconds (4 allocations: 144 bytes)
相同的修复方法,但请使用float64
代替Float64
julia> for op in Symbol[:+, :-, :*, :/, :^]
@eval $op(x::MathConst, y::Int64) = $op(float64(x),y)
end
最后在Julia 0.3中重新定义并重新进行测试
julia> @time test1(10000000)
elapsed time: 3.023e-6 seconds (80 bytes allocated)