Julia Language Server initialization performance issue

Hello,

I am new to Julia and using neovim in combination with julia-vim (syntax highlighting) and coc-julia (language server).

Having pasted just this small snippet in main.jl and started with nvim main.jl

# wait for ide to highlight syntax error
println(1 + 2
 
for x in ARGS; println(x); end
function ()

end

, the time to wait for IDE until error highlight has been 43 seconds.

I absolutely fell in love with julia. But this seems to be serious latency. My use case is to utilize julia as a fast-paced shell script replacement.

  • Is this just my environment or usual julia startup time?
  • Is this fix being worked on ? (in some github issue someone said to work on it actively in 2021, cannot find the github issue anymore)

Thanks for clarifications!

julia version 1.7.2

I’m an emacs user, so I don’t have experience with coc-julia in particular. But I’d say that what you’re seeing here is not really the julia startup time (not sure what you mean exactly by that, though), but rather the startup time of the language server. The language server will indeed usually take a long time to start, although the situation has noticeably improved recently, and 43s seems a bit high for Julia 1.7.2.

The usual way to leverage this is to have a dedicated system image for the Language Server, but this probably has to be handled by your IDE when it launches the server. So I would try and look whether coc-julia can handle system images. A cursory look at its README seems to indicate that there is at least some support for this via the julia.CompileLanguageServerSysimg command.

2 Likes

You’re in the wrong town, buddy :slight_smile:

Julia is a compiled language, every time you run it, you pay the price of compilation.

You can create system images to reduce this, but these end up being 100s of Mb as they contain all the of the Julia runtime.

We call this the “First Time To Plot” problem, or FTTP or more generically FTTX

And is a topic of much discussion and, like your position, angst.

Yes, indeed this is rather language server startup time, sorry for not being specific enough.

The whole environment additionally runs in a virtual machine, which might explain higher initialization time.
Thanks for the info, that startup time still takes relatively long!

Huge. Appreciated!

More specifically: julia CLI is snappy and only takes 1-2 sec to compile and report syntax error.
So this is not a matter of just compile time, rather language server initializiation with regards to coc-jula (as answered by @ffevotte ).

See it as compliment to the language that I am still on board :slight_smile:

Also thanks for explaining TTFX and giving the link!

yes

no

To the OP: Julia DaemonMode is the typical solution for Julia TTFX problem and use case as a scripting tool.

2 Likes

Well, it may not be your position, but it causes a lot of angst.

1 Like

Ok, I don’t want to watch the whole video now. I just thought, “angst” must be way to much for this technical issue (admitted grave technical issue, at least for some). I see “we worry about it a lot”, but this is still far away of “angst”. So, I still don’t see the “angst”, but I am fine if this is just me.

1 Like

Well the video is from Bezanson himself and he uses the term gripes (name of a slack channel?). But I think we are getting kinda out of the topic here.

Just noticed this part. Angst is definitely the wrong term, at least for me.

I started to learn julia yesterday, without having background knowledge of it. Conciseness, expressiveness and practical functional programming features impressed me.

Waiting 40 seconds for a 5-liner code each IDE start was the first thing I found to be frustrating/irritating. I did neither experience this delay with other language servers (Python, TypeScript, Java).

@oheil DaemonMode.jl looks really interesting! I need some time to figure all this out and get bit more knowledge…

1 Like

For me, doesn’t seem too bad/slow, seem tolerable:

julia> @time using LanguageServer
  1.044672 seconds (1.76 M allocations: 147.223 MiB)

Note, before that, just after installin it, that it was much slower (typical for all packages):

julia> @time using LanguageServer
[ Info: Precompiling LanguageServer [2b0e0bc5-e4fd-59b4-8912-456d1b03d8d7]
 61.587472 seconds (1.86 M allocations: 152.937 MiB, 0.15% gc time)

Maybe you did see time more like the latter, but not the “Precompiling” message, as it was hidden from you (for some reason)? I.e. you wouldn’t experience such slowness again. I believe the LanguageServer.jl is also used by VS Code Julia extension. And VS Code starts up for me in about 3 sec. so not really a problem.

I also use vim regularly (but w/o any support/extension for Julia). It should be/has much faster startup than only VS Code, without any extensions. And it seems the overhead should be the same in seconds, with extension.

Since you’re a new Julia user:

That’s simplified advice, yes Julia compiles, but as you saw about it precompiles (not fully), all dependencies. Many dependencies load quickly, in like 0.01 sec. or less, while others can easily take 5 sec. or more, because of all sorts of (avoidable, mostly) issues.

Python ASLO compiles packages and stores so not needed again. But as with Julia, I believe the compilation of the main script is not stored, so it will be compiled each time (unless you use DeamonMode.jl).

Julia WAS designed to be a shell-scripting replacement, and while it IS in some ways better than bash or Python (regarding code quality/security), both bash and Python have slightly faster startup, and faster compilation, because Julia optimizes more. Thus you are faster at runtime (for long-running scripts), i.e. if the runtime is long enough it eats the startup overhead.

And not all scripts are speed-critical anyway.

Some options to be aware of:

$ julia -O0 --compile=min --startup-file=no

julia> @time using LanguageServer
  1.066555 seconds (1.77 M allocations: 147.824 MiB, 4.54% gc time)

It didn’t really help in this case, you can use either of the first, good to be aware of, especially for scripts. And you might not have a startup Julia file, but if you do, the third option is good (I wish it were the default…), to get rid of that extra overhead.

Note the LanguageServer.jl at least is only meant for IED/editor support, so for the actual scripts you wouldn’t use it so not suffer from that overhead.

6 Likes

Thanks for this elaborate answer @Palli !

I tried your benchmark:

julia> @time using LanguageServer
  1.867666 seconds (2.64 M allocations: 189.216 MiB, 3.52% gc time, 2.22% compilation time)

julia> @time using LanguageServer
  0.000343 seconds (108 allocations: 9.453 KiB)

This seems quite fast.

Instead, if I open a julia .jl script file via neovim/coc-julia, for the first ~40 seconds I get following in the bottom bar (each time nvim is restarted):

Initializing julia 1

After that for ~ 3 sec:

julia starting async tasks

and a list of loaded modules, which gets processed really quick.

Yes, but note that having a fully functional Language Server involves

  1. loading the LanguageServer & SymbolServer modules
  2. creating a LanguageServerInstance
  3. running the server

While the first two steps are very fast in recent versions, the last step still involves some latency (in the sense that the server is not able to answer any request before some compilation is done). This is relatively hard to benchmark, because the server itself is an endless loop waiting for requests coming from a client (which, at least in the real-world intended use case, is a separate process). But it looks like it takes around 8s on my machine for the server to answer the first request. Not a huge deal, but still a noticeable delay (but nowhere near the 43s reported here, which still seem to be high for a recent version of the server running on a recent version of julia).

Indeed, LanguageServer.jl is also used by the VS code extension. AFAIU, there is some magic performed to reduce startup times in the context of VS code, but I don’t know much about this and am not able to discuss this much further. However, my experience with eglot-jl (which is one of the ways to integrate LanguageServer.jl within Emacs) tells me that there still is some latency involved when starting the Language Server (although I should mention, again, that this used to be the root cause of many gripes about eglot-jl, but incredible progress has been made in this area). I know of only one way to reduce this latency, which involves system images, but it is relatively hard to implement in a completely foolproof way, especially when you don’t control the precise Julia version that your users will end up having on their system.

So it seems to be every single time, not just a one time (e.g. precompile) cost. That’s of course non-ideal. I just wanted to make sure not a misunderstanding, which it wasn’t. And clarify a bit for scripting (which is immune to this, while having its, smaller startup issues).

Yes, whatever the reason for VS Code being faster, at least it’s fast, so you can consider it rather than vim. Note, vim keybindings are available, for some compatibility, but that’s yet another (non-Julia specific) extension to use: Vim - Visual Studio Marketplace

I think it loads in the background while one can start doing things. With that the 30-40s are almost never perceived.

1 Like

I don’t even know exactly what the LanguageServer does for me, I suppose then, that if I trigger its use (too early, like by what debugging?) I could have to wait. Seems it never happened for me. I also suppose this background loading could also be implemented for vim…

I suppose you do not want to use Emacs (I’ve deferred trying, people swear by it), which has Julia support.

You could also try spacemacs:

The best editor is neither Emacs nor Vim, it’s Emacs and Vim!

Emacs was historically been considered to start up slowly (before Julia existed, so not to blame), I don’t what’s it like now of for spacemacs.

https://develop.spacemacs.org/layers/+lang/julia/README.html

You can use “my” setup I describe here: Neovim + LanguageServer.jl - #72 by fredrikekre. With that I can use the language server functionality instantly.

To be clear, IMO the situation should not be much better in Emacs than Vim w.r.t. the startup time of the Language Server. There is an experimental eglot-jl branch that implements the creation and use of a custom sysimage for the Language Server, which cuts down the LS startup time to almost nothing. But this branch is not merged (yet) since I haven’t found any completely foolproof way to perform the whole process in a plug & play way while supporting several Julia versions.


If I’m not mistaken, some features provided by the Language Server include:

  • linting
  • code navigation (things like “jumping” between definition and uses of a given function or variable) → this requires building an index of the code of all packages in the environment, which is cached but might take a little while the first time you develop in a given project
  • code refactoring (e.g. renaming of variables)
  • inline help

AFAIK, things like debugging or line-by-line code evaluation are handled in VS code by the extension itself, not really the Language Server.

Language server offers

  • code-completion (like VS Code IntelliSense)
  • code navigation (think e.g. of “go to references”)
  • error diagnostics, like syntax error highlighting.

Hence until after ~ 40 seconds each vim start, it can only be used as plain text editor for .jl files.

As workaround I keep nvim open by putting it as background job, when doing other things.

No, VS Code and Emacs are out of scope for me.

Thanks, I will definitely look into it later. All these customizations currently still feel a bit overwhelming…

Just installed neovim, nodejs, julia-vim and coc-julia on a fresh Fedora machine/VM.
Startup time: ~36sec.
I guess, additional extensions previously might have occupied some extra time.
Still being quite a long startup for default configuration, without UI feedback.


Update

To remove coc-julia as dependency for performance comparison, I tried to manually configure the language server, as proposed here. Now following error is shown on CoC console output:

ERROR: MethodError: no method matching JSONRPC.JSONRPCEndpoint(::Base.PipeEndpoint, ::Base.PipeEndpoint, ::Channel{Any}, ::Channel{Any}, ::Dict{String, Channel{Any}}, ::String, ::Symbol, ::Nothing, ::Nothing)

Surely, I must have done something wrong :grimacing: .

Is there something to do besides installing LanguageServer , SymbolServer and StaticLint and creating coc-settings.json:

{
"languageserver": {
  "julia": {
    "command": "/path/to/bin/julia",
    "args" : ["--startup-file=no", "--history-file=no", "-e",
    "using LanguageServer;\n       using Pkg;\n       import StaticLint;\n       import SymbolServer;\n       env_path = dirname(Pkg.Types.Context().env.project_file);\n       debug = false;\n       server = LanguageServer.LanguageServerInstance(stdin, stdout, debug, env_path, \"\");\n       server.runlinter = true;\n       run(server);" ],
    "filetypes": ["julia"]
  }
}
}

What I don’t get:

Check out JuliaEditorSupport/LanguageServer.jl for more information.

As said, VS Code is not used. In the wiki, they mention LanguageClient-neovim and vim-plug, which aren’t used either.