kakoune: kak starts noticeably slower than neovim
Install kakoune
on Arch Linux from the official repo.
Run kak -n
— kakoune starts instantly (perfect).
Run kak
— kakoune starts noticeably slowly, even though I didn’t even create ~/.config/kak/
folder yet.
I use neovim
with lots of plugins and huge config file, and it starts faster than kak
.
Given that I open and close editor many times during the day, it is essential to have the editor start as fast as possible.
I’m not sure how I can provide more information to narrow down the cause of slowness. What does kak
do, that kak -n
doesn’t?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 59 (51 by maintainers)
Commits related to this issue
- Tweak various scripts to improve the time they take to load As requested in #2152, startup time should be slightly better. — committed to mawww/kakoune by mawww 6 years ago
- Fix RegexCompileFlags::Backwards having the same value as Optimize That means every Optimized regex had the Backwards version compiled as well, which doubled the time it took to compile them and doub... — committed to mawww/kakoune by mawww 6 years ago
- Micro optimize command parsing by avoiding utf8 decoding Balanced quoted parsing does not need to decode utf8, neither does unquoted word parsing. This improves startup time a bit, helping for issue ... — committed to mawww/kakoune by mawww 6 years ago
The benefit of modules is also to be able to share code between various scripts. Right now a lot of language scripts have copy-pasted logic to handle auto-indentation and auto-insertion of delimiters, it’s rather fragile and I’m pretty sure some obscure language scripts are just outdated.
This is fixed in
master
by lazy loading implemented by @laelath in https://github.com/mawww/kakoune/pull/2782, thanks a lot everyone!The final numbers:
nvim with no config: 25ms
nvim with huge config and lots of plugins: 114ms
kak -n: 7ms
kak with no custom config in
v2019.01.20
: 70mskak with no custom config in
master
: 20ms20ms beats neovim’s 25ms, the issue is solved 🙂
In my view, lazy loading is the last resort, I’d much rather ensure that we are quick to load even without.
That said, I have a plan on how lazy loading would work, which is related to the “modules” system that would solve the dependency between script problem.
The idea for the “modules” system is to introduce 2 new commands:
provides
ormodule
or similar, that takes a single parameter which is an arbitrary string identifying a module, I expect most bundled.kak
files to start with that. That command will stop sourcing the current file if its parameter has already be given to a preceedingprovides
command.requires
or similar, which takes a name as a parameter, and loads a file matching that name from the script path (script path being a list of directories in which we might have scripts, like /usr/share/kak/rc and ~/.config/kak/rc).The link with lazy loading is that we can have
There are still problems to solve (for example, sourcing python.kak would add hooks that want to run on BufSetOption filetype=python, and those would not run currently on the file that triggers the loading of python.kak), but thats the general direction I had in mind.
Comments ?
Well, Kakoune doesn’t have to load everything as well. I can imagine we could put source commands into hooks so Kakoune would load language support only when file with specific extension is opened:
Of course this hook won’t work in Kakoune in current form. This solution would require quite a lot changes in scripts. First - we need a way to check if file was already sourced, second - language initialization should be handled after sourcing (usually language script does it in BufCreate hook, but in this case buffer already exists).
I don’t think it’s hard but it’s a lot of work.
kak -n
makes Kakoune skip loading all the included defaults, configuration and syntax highlighting, which is a lot of stuff. We were actually talking about this in #kakoune the other day; it’s hard to pinpoint any specific thing that’s slow, since Kakoune and its scripts do so many things at startup.One practical thing you can do:
:echo %val{runtime}/autoload<ret>
to find out.~/.local/config/kak/autoload/
which will prevent Kakoune from automatically loading anything at startup.autoload
directory, so that the ones you don’t use won’t be read at startup and won’t slow things down.kakoune is indeed plenty fast when started with no config (
kak -n
), however when starting with a few plugins, it is way more slower than my nvim config with more plugins. 🤔kakoune with a few plugins:
nvim with a bigger config and more plugins:
I guess nvim greatly improved loading speed for heavy configs since 2019
It is remarkable however that kakoune is faster than nvim with no config:
I wonder what it would take to improve loading time when using plugins
@mawww one issue with even super fast script/module loading is - bloat. Sometimes I’m just editing a git commit, or a text file; I really don’t want to see arbitrary clang completions or rust commands when pressing
:
.I’d much prefer to see something like @laelath described above, where the editor loads lang specific stuff only in buffers (or clients) when a file of that kind is open. Loading & caching per session would also keep the bloat problem.
This would work for most of the languages, but
c-family.kak
has a lot of it’s highlighters added within those top-levelevaluate-commands %sh{}
blocks.~If we consider my approach, we could go with only wrapping top-level
evaluate-commands %sh{
blocks inhook -once
, my observation shows that those are the slowest pieces of code to execute anyway. Having top-leveladd-highlighters
not being lazy loaded is acceptable in my mind, and solves this dependency problem that you mention.~Realized just after posting that I’m wrong, the issue is totally not solved.
I can convert a few heaviest files to get some estimates, I’m a bit hesitant to convert everything because it will take some time (e.g. I would want to manually test every syntax), and because of whitespace diffs it will quickly become outdated if other PRs will touch the same files.
Can we maybe discuss your concerns first?
Previously you mentioned two things: repeating the same thing in 92 files and creating many new commands that will remain in memory. Now commands are no longer being created, and all these 92 files already have
WinSetOption filetype
hooks anyway, so I would argue this change will not add any new repetition.Are you maybe concerned that this change will break a particular syntax? I can start by sending a PR just for that one (or for those few) syntaxes, and then you and I can do some more thorough testing.
I don’t want to rush you, I want you too to be happy and confident with the change, but if we agree to do this, I would ask you to review & merge it quickly to avoid wasted effort and possible conflicts 😛
I see, and just to be clear, I don’t disagree with the modular approach, especially because it brings other benefits as @occivink has mentioned — I would love to see the “modules” solution! But it is a complicated change that you leave as the last resort and don’t plan to do right now, while what I’m proposing is a simple thing for which I can make a PR today and close this ticket for good.
I think we can iteratively improve in this case, once we get to implement modular approach, deleting 6 lines per file is not the most difficult cleanup to do — and on a positive side, we will be able to measure the perf impact of “modules” solution, our goal would be not to decrease the performance comparing to “lazy loading” in its currently proposed form.
Gotcha, thanks for explaining this bit. But I don’t want to mix refactoring scripts into modules with what I’m proposing here, modularization is a much bigger change with different goals.
Let me perhaps ask a different question: do you see any issues with this change? Would you merge it? It is 6 new lines that don’t break anything and improve loading time by
60ms
on bash and10ms
on dash.https://github.com/mawww/kakoune/pull/2255/files?w=1
I’m not sure why everyone is so carefully avoiding the topic of lazy-loading, it gives the great perf boost and is a very simple thing to do.
I made an example for
c-family.kak
: https://github.com/mawww/kakoune/pull/2255/files?w=1Do you see any issues with this approach?
Ah, I see, so in reality
c-lang.kak
is the slowest script.It was bash, and dash is giving me a very good perf improvement, thanks for the idea @co-dh!
Time for some perf runs! The results are averaged for 100 executions of
kak -e 'quit'
.sh -> bash
:242ms
sh -> bash
, removedc-family.kak
:179ms
sh -> dash
:98ms
sh -> dash
, removedc-family.kak
:88ms
!!!Woohoo, we are finally beating neovim!
Can we still consider lazy-loading language-specific configs? Still not sure if this could be as simple as wrapping all top-level
evaluate-command
calls into aWinSetOption filetype
hook, or there’s more to it.Oh, I see. You right, megre operation is blazing fast while autoloading is way slower. Interesting
Looks like autoload is just kak/sh script here: https://github.com/mawww/kakoune/blob/0d838f80a0cc920e64c6a5a969861f83d96967a6/share/kak/kakrc
source command: https://github.com/mawww/kakoune/blob/665d3fa196f4df905ebd682965adf78d80eaf8a8/src/commands.cc#L1234
I did some additional benchmarking:
nvim with no config:
25ms
nvim with huge config and lots of plugins:
114ms
kak -n
:7ms
(awesome result!)kak
with no custom config (except whatever ArchLinux package ships):400ms
So before I even start configuring
kak
, it already takes almost 4 times longer to load than my fully customizednvim
— let’s try to get this number down 😉:echo %val{runtime}/autoload<ret>
outputs:/usr/share/kak /autoload
I don’t have the folder
/autoload
, but I do have the folder/usr/share/kak
present.Creating a folder
~/.config/kak/autoload
does makekak
load much faster, as if I provided the-n
argument. However, I do want to have all those files loaded 🙂Neovim is probably fast because it can load configs based on a file type, can we have the same in kakoune? For example, I probably don’t really need the file
rc/base/haskell.kak
loaded until I actually open a haskell file.Merging the files together as @TeddyDD suggested does provide a huge benefit, I get the startup time decreased from
400ms
down to176ms
(althoughnvim
is still faster even with all custom configs and plugins, again probably because it doesn’t have to load everything on startup).manually sourcing each file from kakrc is faster than loading same files from autoload. manually loading all default
kak
files takes~115ms
while when putting them in autoload takes~170ms
. why is that?