Skip to content

Commit 7d4e629

Browse files
committed
test: add harness for measuring performance
1 parent 0472d92 commit 7d4e629

File tree

4 files changed

+143
-1
lines changed

4 files changed

+143
-1
lines changed

.envrc

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

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ venv/
4848
doc/tags
4949
scripts/nvim_doc_tools
5050
scripts/nvim-typecheck-action
51+
tests/perf/
52+
profile.json

Makefile

+18-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ fastlint: scripts/nvim_doc_tools venv
3535
luacheck lua tests --formatter plain
3636
stylua --check lua tests
3737

38+
## profile: use LuaJIT profiler to profile the plugin
39+
.PHONY: profile
40+
profile:
41+
nvim --clean -u tests/perf_harness.lua -c 'lua jit_profile()'
42+
43+
## flame_profile: create a trace in the chrome profiler format
44+
.PHONY: flame_profile
45+
flame_profile:
46+
nvim --clean -u tests/perf_harness.lua -c 'lua flame_profile()'
47+
@echo "Visit https://ui.perfetto.dev/ and load the profile.json file"
48+
49+
## benchmark: benchmark performance opening directory with many files
50+
.PHONY: benchmark
51+
benchmark:
52+
nvim --clean -u tests/perf_harness.lua -c 'lua benchmark(10)'
53+
@cat tests/perf/benchmark.txt
54+
3855
scripts/nvim_doc_tools:
3956
git clone https://github.com/stevearc/nvim_doc_tools scripts/nvim_doc_tools
4057

@@ -44,4 +61,4 @@ scripts/nvim-typecheck-action:
4461
## clean: reset the repository to a clean state
4562
.PHONY: clean
4663
clean:
47-
rm -rf scripts/nvim_doc_tools scripts/nvim-typecheck-action venv .testenv
64+
rm -rf scripts/nvim_doc_tools scripts/nvim-typecheck-action venv .testenv tests/perf profile.json

tests/perf_harness.lua

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
vim.fn.mkdir("tests/perf/.env", "p")
2+
local root = vim.fn.fnamemodify("./tests/perf/.env", ":p")
3+
4+
for _, name in ipairs({ "config", "data", "state", "runtime", "cache" }) do
5+
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. name
6+
end
7+
8+
vim.opt.runtimepath:prepend(vim.fn.fnamemodify(".", ":p"))
9+
10+
---@module 'oil'
11+
---@type oil.SetupOpts
12+
local setup_opts = {
13+
-- columns = { "icon", "permissions", "size", "mtime" },
14+
}
15+
16+
local num_files = 100000
17+
18+
if not vim.uv.fs_stat(string.format("tests/perf/file %d.txt", num_files)) then
19+
vim.notify("Creating files")
20+
for i = 1, num_files, 1 do
21+
local filename = ("tests/perf/file %d.txt"):format(i)
22+
local fd = vim.uv.fs_open(filename, "a", 420)
23+
assert(fd)
24+
vim.uv.fs_close(fd)
25+
end
26+
end
27+
28+
local function wait_for_done(callback)
29+
vim.api.nvim_create_autocmd("User", {
30+
pattern = "OilEnter",
31+
once = true,
32+
callback = callback,
33+
})
34+
end
35+
36+
function _G.jit_profile()
37+
require("oil").setup(setup_opts)
38+
local outfile = "tests/perf/profile.txt"
39+
require("jit.p").start("3Fpli1s", outfile)
40+
local start = vim.uv.hrtime()
41+
require("oil").open("tests/perf")
42+
43+
wait_for_done(function()
44+
local delta = vim.uv.hrtime() - start
45+
require("jit.p").stop()
46+
print("Elapsed:", delta / 1e6, "ms")
47+
vim.cmd.edit({ args = { outfile } })
48+
end)
49+
end
50+
51+
function _G.benchmark(iterations)
52+
require("oil").setup(setup_opts)
53+
local num_outliers = math.floor(0.1 * iterations)
54+
local times = {}
55+
56+
local run_profile
57+
run_profile = function()
58+
-- Clear out state
59+
vim.cmd.enew()
60+
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
61+
if vim.api.nvim_buf_is_valid(bufnr) and bufnr ~= vim.api.nvim_get_current_buf() then
62+
vim.api.nvim_buf_delete(bufnr, { force = true })
63+
end
64+
end
65+
66+
local start = vim.uv.hrtime()
67+
wait_for_done(function()
68+
local delta = vim.uv.hrtime() - start
69+
table.insert(times, delta / 1e6)
70+
if #times < iterations then
71+
vim.schedule(run_profile)
72+
else
73+
-- Remove the outliers
74+
table.sort(times)
75+
for _ = 1, num_outliers do
76+
table.remove(times, 1)
77+
table.remove(times)
78+
end
79+
80+
local total = 0
81+
for _, time in ipairs(times) do
82+
total = total + time
83+
end
84+
85+
local lines = {
86+
table.concat(
87+
vim.tbl_map(function(t)
88+
return string.format("%dms", math.floor(t))
89+
end, times),
90+
" "
91+
),
92+
string.format("Average: %dms", math.floor(total / #times)),
93+
}
94+
vim.fn.writefile(lines, "tests/perf/benchmark.txt")
95+
vim.cmd.qall()
96+
end
97+
end)
98+
require("oil").open("tests/perf")
99+
end
100+
101+
run_profile()
102+
end
103+
104+
function _G.flame_profile()
105+
if not vim.uv.fs_stat("tests/perf/profile.nvim") then
106+
vim
107+
.system({ "git", "clone", "https://github.com/stevearc/profile.nvim", "tests/perf/profile.nvim" })
108+
:wait()
109+
end
110+
vim.opt.runtimepath:prepend(vim.fn.fnamemodify("./tests/perf/profile.nvim", ":p"))
111+
local profile = require("profile")
112+
profile.instrument_autocmds()
113+
profile.instrument("oil*")
114+
115+
require("oil").setup(setup_opts)
116+
profile.start()
117+
require("oil").open("tests/perf")
118+
wait_for_done(function()
119+
profile.stop("profile.json")
120+
vim.cmd.qall()
121+
end)
122+
end

0 commit comments

Comments
 (0)