Shouldn't 1.8.0 be faster than Julia 1.7?

That’s what I like to see!

We can take a look at what @time is actually doing, with @macroexpand:

julia> @macroexpand @time (1:10^9).^2
quote
    begin
        $(Expr(:meta, :force_compile))
        local var"#8#stats" = Base.gc_num()
        local var"#10#elapsedtime" = Base.time_ns()
        Base.cumulative_compile_timing(true)
        local var"#11#compile_elapsedtimes" = Base.cumulative_compile_time_ns()
        local var"#9#val" = $(Expr(:tryfinally, :((1:10 ^ 9) .^ 2), quote
    var"#10#elapsedtime" = Base.time_ns() - var"#10#elapsedtime"
    Base.cumulative_compile_timing(false)
    var"#11#compile_elapsedtimes" = Base.cumulative_compile_time_ns() .- var"#11#compile_elapsedtimes"
end))
        local var"#12#diff" = Base.GC_Diff(Base.gc_num(), var"#8#stats")
        local var"#13#_msg" = Base.nothing
        local var"#14#has_msg" = !(Base.isnothing(var"#13#_msg"))
        var"#14#has_msg" && Base.print(var"#13#_msg", ": ")
        Base.time_print(var"#10#elapsedtime", (var"#12#diff").allocd, (var"#12#diff").total_time, Base.gc_alloc_count(var"#12#diff"), Base.first(var"#11#compile_elapsedtimes"), Base.last(var"#11#compile_elapsedtimes"), true, !var"#14#has_msg")
        var"#9#val"
    end
end

(I’ve removed all the comments signifying where the macro was defined.)

@time does nothing more than wrap your code (see the loval var"#9#val" = .. thing) and calculates how long it took to run. This has the sideeffect of running your code exactly as you wrote it - meaning in this case, it’s all run in global scope. Global scope does not allow for the same optimizations that can be done in a function, hence there will always be a performance difference between the two. Since the slowdown does not appear in a function, I’m inclined to conclude that this is not a bug with julia 1.8, but rather a benchmarking artifact due to benchmarking in global scope (which is a bad idea, as you’ve noticed).

This is also mentioned in the Performance tips:

Performance critical code should be inside a function

Any code that is performance critical should be inside a function. Code inside functions tends to run much faster than top level code, due to how Julia’s compiler works.

18 Likes