How to understand Traceur's output?

I am not sure, that I understand correctly the performance tips regards type declarations and containers with abstract types, so I tested my code with Traceur.
But I don’t really understand the output, could you please help me with it?
I have this code in mwe.jl:

using Dates

abstract type AbstractWorkoutType end

struct RunningWorkout <: AbstractWorkoutType
	distance::Float64
end

struct GymWorkout <: AbstractWorkoutType
	type::String
end

struct Workout{T<:AbstractWorkoutType}
	date::DateTime
	workout::T
	effort::Union{Int64, Nothing}
end

function traceme()
	dt1 = DateTime(2019,1,1)
	dt2 = DateTime(2019,1,2)

	r = RunningWorkout(1.5)
	g = GymWorkout("push ups")

	w1 = Workout(dt1, r, nothing)
	w2 = Workout(dt2, g, 5)

	return [w1, w2]
end

If I ran @trace, I get the following:

julia> include("mwe.jl")
traceme (generic function with 1 method)

julia> traceme()
2-element Array{Workout,1}:
 Workout{RunningWorkout}(2019-01-01T00:00:00, RunningWorkout(1.5), nothing)
 Workout{GymWorkout}(2019-01-02T00:00:00, GymWorkout("push ups"), 5)

julia> using Traceur

julia> @trace traceme()
┌ Warning: err is assigned as Union{Nothing, ArgumentError}
└ @ C:\cygwin\home\Administrator\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.1\Dates\src\types.jl:182
┌ Warning: unsafe_pointer_to_objref returns Any
└ @ pointer.jl:128
┌ Warning: getindex returns Any
└ @ essentials.jl:549
┌ Warning: getindex returns Any
└ @ array.jl:729
┌ Warning: aprimary is assigned as Type{Workout}
└ @ promotion.jl:95
┌ Warning: aprimary is assigned as Type{Workout{T<:AbstractWorkoutType}}
└ @ promotion.jl:108
┌ Warning: aprimary is assigned as Any
└ @ promotion.jl:112
┌ Warning: dynamic dispatch to (Base.UnionAll)(φ (%97 => %93, %119 => %114), φ (%97 => %82, %119 => %101))
└ @ promotion.jl:112
┌ Warning: typejoin returns Any
└ @ promotion.jl:16
┌ Warning: getindex returns Union{Workout{GymWorkout}, Workout{RunningWorkout}}
└ @ tuple.jl:24
┌ Warning: x is assigned as Union{Workout{GymWorkout}, Workout{RunningWorkout}}
└ @ abstractarray.jl:668
┌ Warning: y is assigned as Union{Nothing, Tuple{Int64,Int64}}
└ @ abstractarray.jl:667
┌ Warning: y is assigned as Union{Nothing, Tuple{Int64,Int64}}
└ @ abstractarray.jl:672
2-element Array{Workout,1}:
 Workout{RunningWorkout}(2019-01-01T00:00:00, RunningWorkout(1.5), nothing)
 Workout{GymWorkout}(2019-01-02T00:00:00, GymWorkout("push ups"), 5)

I don’t understand where these warnings come from, and whether I can ignore them.
Later I wan to operate on arrays of Workout, and while this current example is not performance critical, I have similar types at another project where speed does matter, so I would like to do it right.

Thank you for your help in advance!

2 Likes

Same for me. At least for newcomers it’s hard to understand what and where the issue is. It’d be so cool if it could point to the offending line in the code, stacktrace style, and to the relevant performance tips section… not a complaint, Traceur is great and I’m sure that it’ll all become more obvious as we learn from our mistakes. I just wish it shoved my nose into the mess and shouted out what I did wrong.

EDIT:
Looking at the announcement, the output shown there does look a lot like what I was wishing for, whereas mine is like cserteGT3’s. Should @trace be run in the REPL, have other things changed (e.g. some people point out that @code_warntype output used to be more easily legible) or am I making a mistake?

2 Likes

Ok, so the printing isn’t ideal, but whenever you only see a filename that’s a function in Base and therefore something you’re most likely not interested in.

Take a look at the docs for @trace:

help?> @trace
  @trace(functioncall(args...), maxdepth=2, modules=[])

  Analyse functioncall(args...) for common performance problems and print them to the
  terminal.

  Optional arguments:

    •    maxdepth constrols how far Traceur recurses through the call stack.

    •    If modules is nonempty, only warnings for methods defined in one of the modules
        specified will be printed.

In your case you’re probably only interested in issues in your code, so try e.g.

julia> @trace(traceme(), modules=[Main])
2-element Array{Workout,1}:
 Workout{RunningWorkout}(DateTime(2019, 1, 1, 0, 0, 0), RunningWorkout(1.5), nothing)
 Workout{GymWorkout}(DateTime(2019, 1, 2, 0, 0, 0), GymWorkout("push ups"), 5)      

which doesn’t find any problems.

If you have any ideas for improving the REPL output then feel free to open an issue or PR at the Traceur repo.

You might also be interested in the Juno interface:

3 Likes

Thank you, I had the assumption that these warnings mean that I’m probably OK.
Sorry for missing the docs, I tend to forget that it’s close-at-hand.

Thanks for the advice, very helpful! I wasn’t aware of the Juno.@trace command, that’s great. It may be worth highlighting this option in the docs. Similarly, a suggestion that users might wish to start the analysis by adding kwarg modules=[MyModule] could also be helpful for new users.

I have a question on the same topic. When running the following basic example with @trace(test(), modules=[Main]), the output issues what seems to be a type warning (see below).

function test()
    for v in [1,4,5]
        # anything
    end
end

Traceur’s output:
┌ Warning: is assigned as Union{Nothing, Tuple{Int64,Int64}}
└ @ ~/workspace/code/julia/tests.jl:2
┌ Warning: is assigned as Union{Nothing, Tuple{Int64,Int64}}
└ @ ~/workspace/code/julia/tests.jl:4

Giving the array an explicit type, like in the following, does not seem to change anything

a = Array{Int64, 1}([1,4,5])
for v in a
    # anything
end

My questions: Am I doing something wrong? Is there a fix to this type of warning?

The loop isn’t guaranteed to execute once, since the length of your array isn’t a type parameter and could therefore be 0. In that case v apparently gets assigned nothing. In general this isn’t a problem since Julia can now optimize small unions (especially with nothing and missing) very well, so this isn’t a performance problem.

We made the conscious choice not to special case this in Traceur for some reason I can’t remember anymore, but maybe we should.

Thanks! Then I will ignore such warnings. A potential issue with keeping these warnings is that they are quite many (at least in my case), which can make it harder to focus on the meaningful ones. Another is that beginners like me could spend some time trying to understand (and fix) them.