kabouzeid / nvim-lspinstall

Provides the missing :LspInstall for nvim-lspconfig
MIT License
526 stars 67 forks source link

LspInstall is very expensive to load #143

Open akinsho opened 2 years ago

akinsho commented 2 years ago

Hi @kabouzeid,

Been using lspinstall for a while and have noticed quite an increase in the time to load lspconfig which I initially attributed to that plugin but having disabled lspinstall I've realised that it is quite expensive to load. I dug through the code base and realised something. One of the first things the plugin does is iterate a list of servers https://github.com/kabouzeid/nvim-lspinstall/blob/22975634102e30bcea72f9508dff9190900bcecf/lua/lspinstall.lua#L82 Derived from https://github.com/kabouzeid/nvim-lspinstall/blob/22975634102e30bcea72f9508dff9190900bcecf/lua/lspinstall/servers.lua#L1

I'm pretty confident that this is the reason for the quite high startup cost of this plugin, roughly around 15ms+ on a quite beefy machine, which is the slowest of any plugin I use. The reason being that requiring a module is kind of expensive and doing it 35+ times right on startup is very expensive. It means that when setting up this plugin a user has to essentially load most of this plugin's codebase just set up the few servers they use, in my case I only install 2 currently, but this logic requires the code for 35+.

I think another way to potentially handle this is that this plugin already checks if a server is installed by checking for its directory. Potentially, you could use libuv to read the contents of a directory and check what servers are installed and only require those rather than all of them, e.g.

function get_servers()
    local directory_fd = vim.loop.fs_opendir(install_path)
    local dirs = vim.loop.fs_readdir(directory_fd
    local servers = {}
    for _, lang in ipairs(dirs) do
        if langs[lang] then
            local server = require(('lspinstall/servers/%s'):format(lang))
            table.insert(servers, server)
        end
    end
    return servers
end

Another potential solution would be using lazy.nvim's patterns this plugin by TJ is kind of just an example of how a plugin author can lazy require modules so that they aren't actually required unless a property on the table gets accessed.

this would look more like

local servers = {
 bash = lazy.require('lspinstall/servers/bash') -- this will not be required immediately
 ...
}

-- This is only actually required when a property is indexed
-- e.g.
-- bash.server_config -- accessing the prop actually triggers the require
siduck commented 2 years ago

I agree with @akinsho , it takes 16ms on my old machine image many lsp servers which I dont use are also sourced :

image

williamboman commented 2 years ago

Just out of curiosity - what tools are you using to profile this?

siduck commented 2 years ago

Just out of curiosity - what tools are you using to profile this?

https://github.com/norcalli/profiler.nvim

lewis6991 commented 2 years ago

https://github.com/kabouzeid/nvim-lspinstall/pull/173 should alleviate the problem

kabouzeid commented 2 years ago

Ahh this looks very good! Thank you @lewis6991. I'll make some tests and if it works as it expected merge soon.

lewis6991 commented 2 years ago

Here's my startup time currently using impatients profiler.

───────────────────────────┬────────────────┬────────────┬────────────┬────────────┬────────────┐
                           │ Loader         │ Resolve    │ Load       │ Exec       │ Total      │
───────────────────────────┼────────────────┼────────────┼────────────┼────────────┼────────────┤
Total                      │                │   2.2017ms │   4.4617ms │  93.9100ms │ 100.5734ms │
───────────────────────────┴────────────────┴────────────┴────────────┴────────────┴────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────┐
By Plugin                                                                                       │
───────────────────────────┬────────────────┬────────────┬────────────┬────────────┬────────────┤
lspinstall                 │          cache │   0.6123ms │   0.2323ms │  37.1933ms │  38.0378ms │
lewis6991                  │          cache │   0.1113ms │   0.1262ms │  34.0965ms │  34.3340ms │
impatient                  │       standard │   0.0000ms │   0.0000ms │   4.7589ms │   4.7589ms │
packer_compiled            │          cache │   0.0234ms │   0.0844ms │   4.3859ms │   4.4938ms │
diffview                   │          cache │   0.3135ms │   0.7074ms │   3.1027ms │   4.1236ms │
octo                       │          cache │   0.2004ms │   1.0370ms │   1.6385ms │   2.8760ms │
telescope                  │          cache │   0.0714ms │   0.1609ms │   2.2520ms │   2.4843ms │
vim                        │          mixed │   0.0953ms │   0.6230ms │   1.2499ms │   1.9681ms │
treesitter-context         │          cache │   0.0175ms │   0.0326ms │   1.2366ms │   1.2867ms │
gitsigns                   │          cache │   0.2141ms │   0.2849ms │   0.7009ms │   1.1999ms │
null-ls                    │          cache │   0.0868ms │   0.2017ms │   0.6182ms │   0.9067ms │
nvim-treesitter            │          cache │   0.0568ms │   0.1866ms │   0.5071ms │   0.7505ms │
plenary                    │          cache │   0.0890ms │   0.1956ms │   0.4602ms │   0.7448ms │
trouble                    │          cache │   0.0624ms │   0.1407ms │   0.4320ms │   0.6352ms │
symbols-outline            │          cache │   0.0679ms │   0.1424ms │   0.3272ms │   0.5374ms │
colorizer                  │          cache │   0.0192ms │   0.0553ms │   0.3495ms │   0.4239ms │
dirvish                    │          cache │   0.0329ms │   0.0668ms │   0.1577ms │   0.2574ms │
lspconfig                  │          cache │   0.0153ms │   0.0600ms │   0.1032ms │   0.1784ms │
fzf_lib                    │          cache │   0.0061ms │   0.0075ms │   0.0952ms │   0.1087ms │
packer                     │          cache │   0.0317ms │   0.0389ms │   0.0370ms │   0.1075ms │
foldsigns                  │          cache │   0.0111ms │   0.0160ms │   0.0597ms │   0.0868ms │
nvim-treesitter-playground │          cache │   0.0229ms │   0.0194ms │   0.0326ms │   0.0748ms │
persistence                │          cache │   0.0106ms │   0.0084ms │   0.0431ms │   0.0622ms │
spellsitter                │          cache │   0.0109ms │   0.0173ms │   0.0164ms │   0.0446ms │
ffi                        │      preloader │   0.0000ms │   0.0000ms │   0.0354ms │   0.0354ms │
spaceless                  │          cache │   0.0106ms │   0.0093ms │   0.0112ms │   0.0312ms │
cleanfold                  │          cache │   0.0084ms │   0.0071ms │   0.0089ms │   0.0244ms │
bit                        │             NA │   0.0000ms │   0.0000ms │   0.0003ms │   0.0003ms │
───────────────────────────┴────────────────┴────────────┴────────────┴────────────┴────────────┘

lspinstall is taking about 40ms which equals my init.lua.