test: add harness for measuring performance

This commit is contained in:
Steven Arcangeli 2024-11-14 19:18:18 -08:00
parent 0472d9296a
commit 7d4e62942f
4 changed files with 143 additions and 1 deletions

1
.envrc
View file

@ -1,2 +1,3 @@
export VIRTUAL_ENV=venv
layout python
python -c 'import pyparsing' 2>/dev/null || pip install -r scripts/requirements.txt

2
.gitignore vendored
View file

@ -48,3 +48,5 @@ venv/
doc/tags
scripts/nvim_doc_tools
scripts/nvim-typecheck-action
tests/perf/
profile.json

View file

@ -35,6 +35,23 @@ fastlint: scripts/nvim_doc_tools venv
luacheck lua tests --formatter plain
stylua --check lua tests
## profile: use LuaJIT profiler to profile the plugin
.PHONY: profile
profile:
nvim --clean -u tests/perf_harness.lua -c 'lua jit_profile()'
## flame_profile: create a trace in the chrome profiler format
.PHONY: flame_profile
flame_profile:
nvim --clean -u tests/perf_harness.lua -c 'lua flame_profile()'
@echo "Visit https://ui.perfetto.dev/ and load the profile.json file"
## benchmark: benchmark performance opening directory with many files
.PHONY: benchmark
benchmark:
nvim --clean -u tests/perf_harness.lua -c 'lua benchmark(10)'
@cat tests/perf/benchmark.txt
scripts/nvim_doc_tools:
git clone https://github.com/stevearc/nvim_doc_tools scripts/nvim_doc_tools
@ -44,4 +61,4 @@ scripts/nvim-typecheck-action:
## clean: reset the repository to a clean state
.PHONY: clean
clean:
rm -rf scripts/nvim_doc_tools scripts/nvim-typecheck-action venv .testenv
rm -rf scripts/nvim_doc_tools scripts/nvim-typecheck-action venv .testenv tests/perf profile.json

122
tests/perf_harness.lua Normal file
View file

@ -0,0 +1,122 @@
vim.fn.mkdir("tests/perf/.env", "p")
local root = vim.fn.fnamemodify("./tests/perf/.env", ":p")
for _, name in ipairs({ "config", "data", "state", "runtime", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. name
end
vim.opt.runtimepath:prepend(vim.fn.fnamemodify(".", ":p"))
---@module 'oil'
---@type oil.SetupOpts
local setup_opts = {
-- columns = { "icon", "permissions", "size", "mtime" },
}
local num_files = 100000
if not vim.uv.fs_stat(string.format("tests/perf/file %d.txt", num_files)) then
vim.notify("Creating files")
for i = 1, num_files, 1 do
local filename = ("tests/perf/file %d.txt"):format(i)
local fd = vim.uv.fs_open(filename, "a", 420)
assert(fd)
vim.uv.fs_close(fd)
end
end
local function wait_for_done(callback)
vim.api.nvim_create_autocmd("User", {
pattern = "OilEnter",
once = true,
callback = callback,
})
end
function _G.jit_profile()
require("oil").setup(setup_opts)
local outfile = "tests/perf/profile.txt"
require("jit.p").start("3Fpli1s", outfile)
local start = vim.uv.hrtime()
require("oil").open("tests/perf")
wait_for_done(function()
local delta = vim.uv.hrtime() - start
require("jit.p").stop()
print("Elapsed:", delta / 1e6, "ms")
vim.cmd.edit({ args = { outfile } })
end)
end
function _G.benchmark(iterations)
require("oil").setup(setup_opts)
local num_outliers = math.floor(0.1 * iterations)
local times = {}
local run_profile
run_profile = function()
-- Clear out state
vim.cmd.enew()
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_is_valid(bufnr) and bufnr ~= vim.api.nvim_get_current_buf() then
vim.api.nvim_buf_delete(bufnr, { force = true })
end
end
local start = vim.uv.hrtime()
wait_for_done(function()
local delta = vim.uv.hrtime() - start
table.insert(times, delta / 1e6)
if #times < iterations then
vim.schedule(run_profile)
else
-- Remove the outliers
table.sort(times)
for _ = 1, num_outliers do
table.remove(times, 1)
table.remove(times)
end
local total = 0
for _, time in ipairs(times) do
total = total + time
end
local lines = {
table.concat(
vim.tbl_map(function(t)
return string.format("%dms", math.floor(t))
end, times),
" "
),
string.format("Average: %dms", math.floor(total / #times)),
}
vim.fn.writefile(lines, "tests/perf/benchmark.txt")
vim.cmd.qall()
end
end)
require("oil").open("tests/perf")
end
run_profile()
end
function _G.flame_profile()
if not vim.uv.fs_stat("tests/perf/profile.nvim") then
vim
.system({ "git", "clone", "https://github.com/stevearc/profile.nvim", "tests/perf/profile.nvim" })
:wait()
end
vim.opt.runtimepath:prepend(vim.fn.fnamemodify("./tests/perf/profile.nvim", ":p"))
local profile = require("profile")
profile.instrument_autocmds()
profile.instrument("oil*")
require("oil").setup(setup_opts)
profile.start()
require("oil").open("tests/perf")
wait_for_done(function()
profile.stop("profile.json")
vim.cmd.qall()
end)
end