Skip to main content

Performance Tuning

Neovim with many plugins can become slow if not carefully managed. This lesson shows how to profile and optimize.

Measure Startup Time​

# Measure startup time from the command line
nvim --startuptime /tmp/startup.log && nvim +quit
cat /tmp/startup.log | sort -k2 -rn | head -20
" Inside Neovim with lazy.nvim
:Lazy profile

Lazy Loading (The Most Impactful Fix)​

Every plugin that doesn't lazy-load increases startup time:

-- Bad: loads immediately
{
"heavy/plugin",
}

-- Good: loads only when needed
{
"heavy/plugin",
event = { "BufReadPost", "BufNewFile" }, -- on file open
-- or:
cmd = "HeavyCommand", -- on command
-- or:
ft = { "python" }, -- on filetype
-- or:
keys = { "<leader>xx" }, -- on keypress
}

Lazy Loading Priority​

Load triggerWhen it loads
lazy = falseImmediately (only for critical plugins)
event = "VimEnter"Right after Vim starts
event = "VeryLazy"After everything else
event = "BufReadPost"When opening a file
ft = "python"Only for Python files
cmd = "Cmd"Only when command is used
keys = { "..." }Only when key is pressed

Disable Unused Built-ins​

in lazy.setup() options
performance = {
rtp = {
disabled_plugins = {
"gzip",
"matchit",
"matchparen",
"netrwPlugin", -- if using neo-tree
"tarPlugin",
"tohtml",
"tutor",
"zipPlugin",
},
},
},

Large File Optimizations​

lua/config/autocmds.lua
vim.api.nvim_create_autocmd("BufReadPre", {
callback = function(args)
local max_filesize = 500 * 1024 -- 500KB
local ok, stats = pcall(vim.loop.fs_stat, args.file)
if ok and stats and stats.size > max_filesize then
-- Disable heavy features for large files
vim.b.large_file = true
vim.opt_local.syntax = "off"
vim.opt_local.swapfile = false
vim.opt_local.foldmethod = "manual"
vim.opt_local.undolevels = -1
vim.cmd("LspStop")
vim.notify("Large file: disabling LSP, syntax, undo", vim.log.levels.WARN)
end
end,
})

Treesitter Performance​

-- Disable for large files
highlight = {
enable = true,
disable = function(lang, buf)
local max_filesize = 100 * 1024 -- 100KB
local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
if ok and stats and stats.size > max_filesize then
return true
end
end,
},

Check Memory Usage​

# Memory used by Neovim process
ps -o pid,rss,comm -p $(pgrep nvim)

# Inside Neovim
:lua print(collectgarbage("count") .. " KB Lua memory")

Profiling Lua Code​

-- Profile a specific plugin config
local start = vim.loop.hrtime()
require("heavy.plugin").setup({})
local end_ = vim.loop.hrtime()
print(("Setup took %.2f ms"):format((end_ - start) / 1e6))

Quick Performance Checklist​

  • All plugins use event, cmd, ft, or keys for lazy loading
  • Built-in unused plugins disabled in lazy.setup()
  • LSP updatetime set to 300
  • Large file autocmd disables heavy features
  • Treesitter highlight disabled for large files
  • swapfile = false (avoids disk I/O)
  • Startup time measured with :Lazy profile

In LazyVim​

LazyVim Context

LazyVim is highly optimized out of the box and loads all plugins lazily by default, typically achieving a startup time of ~100-200ms even with dozens of plugins installed. The vim.loader.enable() experimental module is already enabled by default. Performance tuning in LazyVim is mostly about managing your extras.

To see exactly what is slowing down your startup in LazyVim, you can use the built-in profiler:

  1. Open Neovim
  2. Run :Lazy profile
  3. Sort by load time to identify bottlenecks

Often, startup slowdowns in LazyVim are caused by enabling too many heavy LazyExtras (like language servers you don't need) or adding poorly optimized custom plugins in lua/plugins/.

AspectVanilla NeovimLazyVim
Plugin LoadingManual configuration of event, cmd, etc.Handled automatically by lazy.nvim and default configs
Bytecode CacheRequires manual setup of vim.loader.enable()Pre-configured and enabled automatically
Finding BottlenecksManual profiling and timestamp trackingSimply use :Lazy profile
Disabling BloatCommenting out plugin specsDisabling unused features via :LazyExtras

What's Next​