Debugging in Juno extremely slow

I’m currently trying to make the switch from Matlab to Julia 1.5.3. I am currently working with Atom/Juno (Atom 1.54.0, uber-juno 0.3.0) because of it’s similarity to the the Matlab editor. While I like many things in Julia, I’m having a really hard time debugging my code. Coming from Matlab, I am used to set breakpoints to see the definition of variables inside loops, functions etc. quickly.

In Juno, evaluating steps in the debugger takes AGES (I am speaking of minutes!) unless the function is very simple. I’ve been searching for solutions for a quite long time now and my current understanding is that this is to be expected(?) I would be grateful for a brief reply from an expert. Are there any good alternatives to Atom/Juno?

Thanks!

3 Likes

Julia’s interpreted mode is known to be pretty slow - speeding it up is a work in progress. The best way to quickly drop into a function’s scope is Infiltrator.jl, or you can use Exfiltrator.jl to send variables in deeply-nested scopes back to the global scope. Both should be just as fast as any other JIT-compiled method.

2 Likes

Thanks a lot! I’ve tried “Infiltrator.jl” and it seems to be a valuable tool.

Believe it or not, I suspect that one of Matlab’s advantages here is that all the performance-sensitive stuff is written in C and their debugger doesn’t step into it. So their debugger only lets you look at surface level logic, but the flip side is that it doesn’t really hurt performance. Conversely, the Julia debugger goes down to every single operation, and that just kills performance.

As a first cut you can try

julia> using JuliaInterpreter

julia> push!(JuliaInterpreter.compiled_modules, Base)
Set{Module} with 2 elements:
  Base.Threads
  Base

and see if it helps. (That will cause it to skip debugging for every method defined in Base.) Or, if you really want to exclude everything in Base, do

julia> using MethodAnalysis

julia> visit(Base) do item
           isa(item, Module) && push!(JuliaInterpreter.compiled_modules, item)
           true
       end

which will block every submodule of Base too. You might need to add some stdlibs too.

16 Likes

It might be worth switching to VSCode since development on Atom is being phased out. Although switching probably won’t solve your debugging issues, VSCode seems to be more responsive overall. Both IDEs have similar features and setups.

Hi Tim.

Thanks for this great advice.
Your first suggestion shortened the degugger to reach my breakpoint from “forever” to about 40 seconds.
Your second suggestion shortened it further to a second or so. Now it really feels like Matlab: I can step quickly through all the lines and see how variables develop.

I would really appreciate if there would be a simple button “Debugger for dummies” :wink: or something similar in a Julia editor that would allow for this kind of “superficial” inspection.

I’ve noticed that the Juno debugger seems to deliver many more details on the code than Matlab’s. But frankly speaking, I am not an IT specialist and don’t understand code at this level anyway.

5 Likes

Perhaps these two options could be added to Juno or vscode to improve the user experience? Would that be possible?

9 Likes

In my opinion, there should be 4 levels of exclusion. The default should be to compile everything except deved packages and code outside packages. The next should be to compile everything in base and stdlib. The third should be to compile only base, and the 4th should be to compile nothing.

The reason to default to compiling everything is that new users mostly won’t be trying to debug external packages and are the least likely to know how to use startup.jl to change the behavior.

7 Likes

The biggest problem with that approach is that it breaks when higher order functions are used:

julia> f(x) = 2x
f (generic function with 1 method)

julia> breakpoint(f)
f

julia> outer(arr) = map(f, arr)
outer (generic function with 1 method)

julia> @run outer([1,2,3]) # this should break when `f` is called, but doesn't
3-element Array{Int64,1}:
 2
 4
 6
7 Likes

To resolve this, I propose that instead we should have: compile everything in Base except a predefined list of higher order functions.
It’s not perfect, but i think it would be relatively easy and get as a lot of the way there.
I have been told that this is feasible.

5 Likes

Ha, I totally forgot we talked about that idea. Will add a “compile all in module except for” functionality to UI for setting compiled funcs/mods by pfitzseb · Pull Request #1888 · julia-vscode/julia-vscode · GitHub.

4 Likes

I have a semi-automatically generated whitelist here and would be happy to add missing higher order functions in Base (or in stdlibs, actually) to that.

The list is called debuggerDefaultCompiled, is it a list of function that will or will not be compiled by default?

Yes :slight_smile: (depends on whether a function is prefixed with a - or not).

Ah, thanks for clarifying :slight_smile: I was thinking about list comprehensions

julia> Meta.@lower [a for a in 1:2]
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─ %1 = 1:2
│   %2 = Base.Generator(Base.identity, %1)
│   %3 = Base.collect(%2)
└──      return %3
))))

is it Generator that needs to be added?
EDIT: Oxinabox already pointed that out :+1: