Line-by-line profiling of Julia code possible?

Viewed 1839

I have some Julia functions that are several hundreds of lines long that I would like to profile so that I can then work on optimizing the code.

I am aware of the BenchmarkTools package which allows the overall execution time and memory consumption of a function to be measured using @btime or @benchmark. But those functions tell me nothing about where inside the functions the bottlenecks are. So my first step would have to be using some tool to identify which parts of the code are slow.

In Matlab for instance there is a very nice built-in profiler which runs a script/function and then reports the time spent on every line of the code. Similarly in Python there is a module called line_profiler which can produce a line-by-line report showing how much time was spent on every single line of a function.

What I’m looking for is simply a line-by-line report showing the total time spent on each line of code and how many times a particular piece of code was called.

Is there such a functionality in Julia? Either built-in or via some third-party package.

2 Answers

There is a Profiling chapter in Julia docs with all the necessary info.

Also, you can use ProfileView.jl or similar packages for visual exploration of the profiled code.

And, not exactly profiling, but very useful in practice package is TimerOutputs.jl

UPD: Since Julia is a compiling language it makes no sense to measure timing of individual lines, since the actual code that is executed can be very different from what is written in Julia.

For example following julia code

function f()
    x = 0
    for i in 0:100_000_000
        x += i
    end

    x
end

is lowered to

julia> @code_llvm f()
;  @ REPL[8]:1 within `f'
define i64 @julia_f_594() {
top:
;  @ REPL[8]:7 within `f'
  ret i64 5000000050000000
}

I.e. there is no loop at all. This is why instead of execution time proxy metric of how often a line appears in the set of all backtraces is used. Of course, it is not the same as the execution time, but it gives a good approximation of where the bottleneck is because lines with long execution time appear in backtraces more often.

OwnTime.jl. Doesn't do call counts though, but it should be easy to add.

Related