Profiling in VSCode returns some red bars with "Flag: GC" - what do they mean?

Hello!

When using @profview in Julia I observe that there exists both blue and red bars. The blue bars I think are fine, they just mention how much time is spent. But what about the red ones?

Is this something I should resolve, can I fix it in some way?

Kind regards

Red indicates type instability. Read the julia docs about it for an intro, and use tools like @code_warntype to debug (or Cthulhu’s @descend_code_warntype for trickier problems with deep call stacks).

Orange indicates that garbage collection (GC) has occured. Usually if you have type instability you will also have garbage collection because compiling is allocating.

Anywhere that memory is allocated (e.g. creating or cloning a vector/array) can trigger the garbage collector.

Depending on your problem, you might not really need to care about type instability of GC or allocations, so long as your code works, but if it is performance critical then eliminating type instability is the top priority, followed by reducing allocations.

1 Like

Okay I went in on some of the functions giving me this GC warning and this the output. I don’t see any Any but line %6 has a yellow color.

function updatexᵢⱼ!(xᵢⱼ, list, points)
    #if length(xᵢⱼ) != length(list) resize!(xᵢⱼ, length(list)) end
    for (iter, L) in enumerate(list)
        i = L[1]; j = L[2];
        xᵢⱼ[iter] = points[i] - points[j]
    end
end
MethodInstance for SPHExample.SimulationEquations.updatexᵢⱼ!(::Vector{SVector{3, Float64}}, ::Vector{Tuple{Int64, Int64, Float64}}, ::Vector{SVector{3, Float64}})
  from updatexᵢⱼ!(xᵢⱼ, list, points) @ SPHExample.SimulationEquations e:\git\SPHExample\src\SimulationEquations.jl:221
Arguments
  #self#::Core.Const(SPHExample.SimulationEquations.updatexᵢⱼ!)
  xᵢⱼ::Vector{SVector{3, Float64}}
  list::Vector{Tuple{Int64, Int64, Float64}}
  points::Vector{SVector{3, Float64}}
Locals
  @_5::Union{Nothing, Tuple{Tuple{Int64, Tuple{Int64, Int64, Float64}}, Tuple{Int64, Int64}}}
  @_6::Int64
  L::Tuple{Int64, Int64, Float64}
  iter::Int64
  j::Int64
  i::Int64
Body::Nothing
1 ─ %1  = SPHExample.SimulationEquations.enumerate(list)::Base.Iterators.Enumerate{Vector{Tuple{Int64, Int64, Float64}}}
│         (@_5 = Base.iterate(%1))
│   %3  = (@_5 === nothing)::Bool
│   %4  = Base.not_int(%3)::Bool
└──       goto #4 if not %4
2 ┄ %6  = @_5::Tuple{Tuple{Int64, Tuple{Int64, Int64, Float64}}, Tuple{Int64, Int64}}
│   %7  = Core.getfield(%6, 1)::Tuple{Int64, Tuple{Int64, Int64, Float64}}
│   %8  = Base.indexed_iterate(%7, 1)::Core.PartialStruct(Tuple{Int64, Int64}, Any[Int64, Core.Const(2)])
│         (iter = Core.getfield(%8, 1))
│         (@_6 = Core.getfield(%8, 2))
│   %11 = Base.indexed_iterate(%7, 2, @_6::Core.Const(2))::Core.PartialStruct(Tuple{Tuple{Int64, Int64, Float64}, Int64}, Any[Tuple{Int64, Int64, Float64}, Core.Const(3)])
│         (L = Core.getfield(%11, 1))
│   %13 = Core.getfield(%6, 2)::Tuple{Int64, Int64}
│         (i = Base.getindex(L, 1))
│         (j = Base.getindex(L, 2))
│   %16 = Base.getindex(points, i)::SVector{3, Float64}
│   %17 = Base.getindex(points, j)::SVector{3, Float64}
│   %18 = (%16 - %17)::SVector{3, Float64}
│         Base.setindex!(xᵢⱼ, %18, iter)
│         (@_5 = Base.iterate(%1, %13))
│   %21 = (@_5 === nothing)::Bool
│   %22 = Base.not_int(%21)::Bool
└──       goto #4 if not %22
3 ─       goto #2
4 ┄       return nothing

Thanks for the explanation of the colors. I care a lot about this since this is performance critical code and I am just uncertain how such a simple function cannot call the garbage collector correctly… I am probably doing something not so clever somewhere.

Kind regards

I don’t see anything that should allocate there, it all looks good, can you make a minimal example where it still does allocate?

You commented the perfect time, was just testing something. Basically using TimerOutputs package I am tracking each function - I think there is a bug in the package somewhere, since I see a low number of allocs for functions when BenchmarkTools would say zero. But back on track the function updatexᵢⱼ! returns 0 allocs:

But I get the GC flag for this function and two others as well;

And I just don’t know why.

Kind regards

Is that snippet run in global scope? I.e. in the REPL?

In regards to minimal example, if you clone this GitHub - AhmedSalih3d/SPHExample at optimize-code-execution and run example\MainSimulation.jl script it should work easily after Pkg.instantiate(). Note, I don’t expect you to do this, can take 10-15 minutes of your time to run simulation, but here is the full code for anyone curious.

Is that snippet run in global scope? I.e. in the REPL?

It is run inside of ´RunSimulation` function found here SPHExample/example/MainSimulation.jl at optimize-code-execution · AhmedSalih3d/SPHExample · GitHub and then this function is called at the bottom of the script. So should not be in global scope.

Kind regards

Is it possible that LoadParticlesFromCSV is type unstable? That could make most of your function “RunSimulation” type unstable.

Also, I’m unfamiliar with @timeit, but could it be a source of allocations?

Yes, LoadParticlesFromCSV has a lot of errors, also type errors. Some perhaps to CSV it self - would this propagate down later? The defined vectors etc. don’t have type errors and the dataframe from CSV is not sed later…

@timeit is from TimerOutputs.jl and can have a few allocations it self, but I use it in a lot of place and only see GC in a few.

Kind regards

If the type of the “points” vector cannot be inferred then that would cause dynamic dispatch every time you call a function with it as an argument (but that would not propagate problems down into the function)

I tried out your updatexᵢⱼ! function on some made up vectors. With @code_warntype and using Julia 1.10.0, I see the yellow under locals at @_5 but not at %6. Maybe that is some difference across versions in how @code_warntype presents the same information? In any event, I think that Union is just the result of iteration returning nothing at the end of the iteration: @code_warntype gives yellow ::Union{Nothing, Tuple{Int64,Int64}} in for loops. The yellow on the line that calls updatexᵢⱼ! in your image of the code looks like some information provided by VS Code, which I am not using, so I don’t know how that works. I’m not an expert at tracking down allocations, but it all looks harmless to me, and it definitely doesn’t seem to be allocating on my system.

@Larbino1 got it, then that function to load in data at start of simulation should not be an issue.

@mstewart, thanks! I agree :slight_smile: