Infuse the constant math in Julia slow

I am sure it should be a mistake ...

Ok, so basically when I pass the Math Constant type to the power (^) function and repeat it several times ... The loop is very slow and uses a lot of memory allocation. The example is trivial because it does not give any result, but I had a real application where I had this problem.

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 

And this is the result of calling functions with large 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) 

This is mistake? Or is there a logical explanation? What would be the proper way to power pi to fourth in a large loop?

Thanks.

+6
source share
1 answer

Looking at Constants.jl in Julia 0.3 and 0.4

Although it looks like you are using Julia 0.3, and these results mostly concern Julia 0.4, it also has the same problem, and the reason is the same as the unnecessary distribution of intermediate elements.

To get an idea of โ€‹โ€‹what is going on, you can enter methods(^) and list all the methods defined for implementing ^. There are a lot of them, so I will not list them, but the important thing is that the Julia file that implements them is specified, in which case it is Constants.jl .

Julia 0.3 vs. Julia 0.4

First time in July 0.3

 julia> @time test1(10000000) elapsed time: 0.313772825 seconds (320393016 bytes allocated, 37.16% gc time) 

In Julia 0.4, thanks to a better garbage collector, the problem is reduced, but still present.

 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) 

When there is an abnormal number of distributions, always suspect type instabilities or time series.

definition ^ for pi

In particular, lines 70 through 72 define a general method for raising MathConst to power.

 for op in Symbol[:+, :-, :*, :/, :^] @eval $op(x::MathConst, y::MathConst) = $op(Float64(x),Float64(y)) end 

using ^ for e instead of pi

Note also that later this is a specialization for the constant e , and the new test, where pi is replaced by e, does not have the same problem.

 julia> function test4(N) for i = 1:N x = e^4 end end test4 (generic function with 1 method) 

run test4

 julia> @time test4(10000000) 108.401 milliseconds (4 allocations: 144 bytes) 

Convert Int to Float64

If Float is created from Int , then this will avoid a problem starting with Float .

 julia> function test5(N) for i = 1:N x = pi^4.0 end end 

what's happening

 julia> @time test5(10000000) 65.430 milliseconds (4 allocations: 144 bytes) 

define a new feature set

Finally, you can create a new set of functions (testing in this case) that specifically define Int as the second parameter to avoid casting. This is not the best approach due to the ambiguity that it introduces, but it is good for the test.

 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. 

and then re-create test1

 julia> function test1(N) for i = 1:N x = pi^4 end end test1 (generic function with 1 method) 

and then re-run

 julia> @time test1(10000000) 2.757 microseconds (4 allocations: 144 bytes) 

do the same for Julia 0.3

Same fix, but use float64 instead of float64

 julia> for op in Symbol[:+, :-, :*, :/, :^] @eval $op(x::MathConst, y::Int64) = $op(float64(x),y) end 

and finally override and re-run the test in Julia 0.3

 julia> @time test1(10000000) elapsed time: 3.023e-6 seconds (80 bytes allocated) 
+4
source

Source: https://habr.com/ru/post/987613/


All Articles