Does anybody use `--track-allocations`

Would anyone mind if we removed support for julia --track-allocations in favor of the more reliable allocation profiler @allocations and @allocated macros? (RFC: Remove `--track-allocation` by vchuravy · Pull Request #48070 · JuliaLang/julia · GitHub)

1 Like

I’m assuming that @allocations refers to the recently merged PR #47367. I understand that like @allocated it returns a single number. With --track-allocations one can identify the lines in the code where the allocations happen. I find this quite useful. How would one do that with one of the macros?

1 Like

No, this is about the allocation profiler

2 Likes

--track-allocations is the only way I have successfully debugged spurious allocations. Sure, the macros you are describing are probably very convenient for people that are comfortable juggling the various “descending in the call graph” tools, but --track-allocations is simply easier for me, even if it requires reading through a ton of files. Similarly, the two or three times I tried to make the allocation profiler work, I failed in the first few minutes (the reported data seemed nonsensical) and simply switched back to --track-allocations.

However, there is probably a better workflow than mine. I would be grateful if someone writes a few detailed blog posts on such a better workflow. (I am referring to a workflow that uses the macros. There are already good blog posts about the profiler.)

Edit: thinking more about it, my negative knee-jerk reaction comes almost certainly from the fact that I could not make the profiler work in vscode on my first couple of tries. If the profiler is the only option, I am sure I will learn to love it.

The TLDR is that with the allocation profiler, profiling allocations becomes exactly the same as profiling runtime. Just Allocs@profile my_func() and it will take a trace of all the allocations.

1 Like

I’m trying this out. I’m looking at the results with PProf.jl. Some questions I have:

  1. PProf.jl pulls in a lot of packages, mostly for graphics, I guess. Is there a more light-weight, text-based interface that produces human-readable output?

  2. At my present level of understanding, I find it hard to read the output I get. After saying Profile.Allocs@profile my_func() and PProf.Allocs.pprof(), I don’t even see a function my_func in the output. (I’ve tried out various views in the web interface.) Am I missing any documentation here?

  3. If everything works, will there be a way to track down allocations to a specific line in the code I’ve written? I find this more helpful than just knowing the function in which the allocation occurs. In the diagrams I’ve seen there were no line numbers.

Thanks in advance for any clarification!

1 Like

OK, to address the original question: I would prefer --track-allocation to stay.

Firstly, it appears to be much more lightweight than the allocation profiler. To read the profiler output, one seems to need PProf.jl. On my machine, that package pulled in 5GB of artifacts!

Secondly, --track-allocation seems to be easier to use for casual users, at least with the current state of the documentation. In a test case where I try to locate allocations in a function with just three lines, I end up with a graph having close to 70 nodes, and it’s not obvious to me how they are related to my three lines. I’m sure there is a way to understand that, but I prefer the self-explanatory output of --track-allocation. Other non-expert users might do so, too.

5 Likes

I would use the --track-allocations to narrow down where allocations were happening at a rough level but to drill down and get specific repeatable results on allocations I’d used @allocated. I say more repeatable because in the past I got seemingly inconsistent results using the --track-allocations method.

The new profiler has the momentous advantage of greater flexibility, but no one has yet been motivated to make friendly postprocessing tools. The PProf recommendation seems insane. But Profile.Allocs.fetch yields a vector of simple structs with stacktraces and sizes, and crunching it for a particular investigation is really not that hard.

Apparently an example is in order.

using LinearAlgebra
using Profile

function eigen_allocs()
    A = rand(100,100)

    eigen(A) # in case we need to compile anything
    @time eigen(A) # for reference
    Profile.Allocs.@profile sample_rate=1.0 eigen(A)

    results = Profile.Allocs.fetch()
    allocs = results.allocs
    t = 0
    for r in allocs
        st = r.stacktrace
        for s in st
            fn = string(s.file)
            if occursin("LinearAlgebra", fn)
                println("$(basename(fn)):$(s.line) $(r.size)")
                t += r.size
                break
            end
        end
    end
    println("total: $t")
end

eigen_allocs()
0.008760 seconds (21 allocations: 422.469 KiB)
LinearAlgebra.jl:377 80000
LinearAlgebra.jl:377 40
lapack.jl:2048 848
...
eigen.jl:169 32
total: 431968

Exercise for the reader: fix the logic to select the most useful stacktrace entries.

2 Likes

Yeah, there’s a pretty easy PR to add allocation profiling support to ProfileView which is a lot lighter weight.

1 Like

I generally agree with the statement, but note that in VSCode (with the Julia extension) you get the same visualizations as for regular (time) profiling. For example, it highlights the code lines in the editor that have the most / the largest allocations.

(Note how the three lines, where A,B,C are defined, are highlighted. Since I selected size, the C line has the tallest bar because its size is the largest.)

6 Likes

First, I believe it’s not the only way, and second, I want a way to guarantee no allocations.

Some languages, at least D, has such a way, to mark a function @nogc. Such a function can only call similarly marked (or I think if proven to not allocate). You could test and see there are no allocations, and that’s often enough, but also might be only be the case for your code-path, with no guarantee at runtime… I could also foresee the macro/marking not just for applying to the function, as with @inline, but also as with it, possible to use it in the caller.

Probably something that could be done better. Would you miss the feature until then, or would it be ok to just use it in an older version of Julia? I guess it’s simple to drop the feature from Julia, not sure what is the great benefit, in startup cost or sysimage size etc. I’ve never used that feature (and not sure most even know of it), so feel free to drop it on my account! I’m neural though, unless there’s any such downside, then I want it gone in 1.9.

It’s not “mission-critical” for me; in this sense I could do without in the latest Julia version. However, if it goes, I would hope to see a lightweight and easy-to-use replacement in the near future.