在 Julia 中为数学常数供电很慢
Powering a Math constant in Julia is slow
我很确定这一定是一个错误...
好吧,基本上,当我将一个数学常量类型传递给幂 (^) 函数并迭代它几次时...循环真的很慢并且使用了大量内存分配。这个例子很简单,因为它不会产生任何结果,但我有实际的应用程序,但我遇到了这个问题。
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 驱动到第 4 位的正确方法是什么?
谢谢。
查看 Julia 0.3 和 0.4 中的 Constants.jl
虽然看起来你使用的是 Julia 0.3,而且这些结果主要是关于 Julia 0.4,但它也有同样的问题,原因是相同的,这是不必要的中间体分配。
要了解正在发生的事情,您可以键入 methods(^)
,它将生成为实现 ^ 定义的所有方法的列表。它们有很多,所以我不会列出它们,但重要的是列出了实现它们的 Julia 文件,在本例中,它是 Constants.jl。
朱莉娅 0.3 与朱莉娅 0.4
Julia 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)
当分配数量异常时,总是怀疑类型不稳定或临时。
pi 的 ^ 定义
特别是,lines 70 through 72 定义了一个通用方法来提高 MathConst 的幂。
for op in Symbol[:+, :-, :*, :/, :^]
@eval $op(x::MathConst, y::MathConst) = $op(Float64(x),Float64(y))
end
使用 ^ 代替 pi
另请注意,稍后是一个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)
运行 测试4
julia> @time test4(10000000)
108.401 milliseconds (4 allocations: 144 bytes)
Int 到 Float64 的转换
如果 Float 是从 Int 创建的,那么这将通过从 开始来避免这个问题浮动.
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.
然后重新创建test1
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)
对 Julia 0.3 做同样的事情
相同的修复,但使用 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)
我很确定这一定是一个错误...
好吧,基本上,当我将一个数学常量类型传递给幂 (^) 函数并迭代它几次时...循环真的很慢并且使用了大量内存分配。这个例子很简单,因为它不会产生任何结果,但我有实际的应用程序,但我遇到了这个问题。
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 驱动到第 4 位的正确方法是什么?
谢谢。
查看 Julia 0.3 和 0.4 中的 Constants.jl
虽然看起来你使用的是 Julia 0.3,而且这些结果主要是关于 Julia 0.4,但它也有同样的问题,原因是相同的,这是不必要的中间体分配。
要了解正在发生的事情,您可以键入 methods(^)
,它将生成为实现 ^ 定义的所有方法的列表。它们有很多,所以我不会列出它们,但重要的是列出了实现它们的 Julia 文件,在本例中,它是 Constants.jl。
朱莉娅 0.3 与朱莉娅 0.4
Julia 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)
当分配数量异常时,总是怀疑类型不稳定或临时。
pi 的 ^ 定义
特别是,lines 70 through 72 定义了一个通用方法来提高 MathConst 的幂。
for op in Symbol[:+, :-, :*, :/, :^]
@eval $op(x::MathConst, y::MathConst) = $op(Float64(x),Float64(y))
end
使用 ^ 代替 pi
另请注意,稍后是一个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)
运行 测试4
julia> @time test4(10000000)
108.401 milliseconds (4 allocations: 144 bytes)
Int 到 Float64 的转换
如果 Float 是从 Int 创建的,那么这将通过从 开始来避免这个问题浮动.
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.
然后重新创建test1
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)
对 Julia 0.3 做同样的事情
相同的修复,但使用 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)