refactor(test): dedup benchmark utils #40478

This commit is contained in:
Justin M. Keyes
2026-06-29 10:36:36 -04:00
committed by GitHub
parent 559730af0a
commit 06fd0d1e45
6 changed files with 135 additions and 179 deletions

View File

@@ -1,4 +1,5 @@
local n = require('test.functional.testnvim')()
local t = require('test.testutil')
local clear = n.clear
local api = n.api
local fn = n.fn
@@ -9,7 +10,6 @@ describe('nvim_replace_termcodes performance', function()
it('200 calls with a key repeated 5000 times', function()
clear()
local stats = {}
local sum = 0
local ms = 1 / 1000000
for _, keycode in ipairs(keycodes.names) do
@@ -23,24 +23,12 @@ describe('nvim_replace_termcodes performance', function()
local elapsed = vim.uv.hrtime() - start
table.insert(stats, elapsed)
sum = sum + elapsed
io.stdout:write(('\n%-20s%14.6f ms'):format(notation, elapsed * ms))
io.stdout:flush()
end
io.stdout:write('\n')
table.sort(stats)
print(('%18s'):rep(6):format('avg', 'min', '25%', 'median', '75%', 'max'))
print(
(' %14.6f ms'):rep(6):format(
sum / #stats * ms,
stats[1] * ms,
stats[1 + math.floor(#stats * 0.25)] * ms,
stats[1 + math.floor(#stats * 0.5)] * ms,
stats[1 + math.floor(#stats * 0.75)] * ms,
stats[#stats] * ms
)
)
t.bench_report(stats, { unit = 'ms' })
end)
end)
@@ -48,7 +36,6 @@ describe('keytrans() performance', function()
it('200 calls with a key repeated 5000 times', function()
clear()
local stats = {}
local sum = 0
local ms = 1 / 1000000
for _, keycode in ipairs(keycodes.names) do
@@ -62,23 +49,11 @@ describe('keytrans() performance', function()
local elapsed = vim.uv.hrtime() - start
table.insert(stats, elapsed)
sum = sum + elapsed
io.stdout:write(('\n%-20s%14.6f ms'):format(notation, elapsed * ms))
io.stdout:flush()
end
io.stdout:write('\n')
table.sort(stats)
print((' %17s'):rep(6):format('avg', 'min', '25%', 'median', '75%', 'max'))
print(
(' %14.6f ms'):rep(6):format(
sum / #stats * ms,
stats[1] * ms,
stats[1 + math.floor(#stats * 0.25)] * ms,
stats[1 + math.floor(#stats * 0.5)] * ms,
stats[1 + math.floor(#stats * 0.75)] * ms,
stats[#stats] * ms
)
)
t.bench_report(stats, { unit = 'ms' })
end)
end)

View File

@@ -1,101 +1,54 @@
local n = require('test.functional.testnvim')()
local clear = n.clear
local exec_lua = n.exec_lua
local bench = n.bench
describe('option set perf', function()
before_each(clear)
--- Runs `fn` (a string of Lua) `iters` times, prints min/median/max, and returns nothing.
--- @param name string
--- @param iters integer
--- @param body string Lua statement(s) executed each iteration; may use the loop var `i`.
local function bench(name, iters, body)
local stats = exec_lua(
[[
local iters, body = ...
local fn = assert(loadstring('local i = ...\n' .. body))
-- Warmup (JIT, option metadata cache, etc.).
for i = 1, 100 do
fn(i)
end
local samples = {}
for i = 1, iters do
local t0 = vim.uv.hrtime()
fn(i)
samples[i] = vim.uv.hrtime() - t0
end
table.sort(samples)
return samples
]],
iters,
body
)
local ms = 1 / 1000000
print(
('\n%-44s min %0.4fms median %0.4fms max %0.4fms'):format(
name,
stats[1] * ms,
stats[1 + math.floor(#stats * 0.5)] * ms,
stats[#stats] * ms
)
)
end
local ITERS = 20000
local trials = 20000
it('vim.o scalar (number)', function()
bench('vim.o.scrolloff = i % 10', ITERS, [[vim.o.scrolloff = i % 10]])
bench([[vim.o.scrolloff = i % 10]], { n = trials, label = 'vim.o.scrolloff = i % 10' })
end)
it('vim.o scalar (boolean)', function()
bench('vim.o.wrap = (i % 2 == 0)', ITERS, [[vim.o.wrap = (i % 2 == 0)]])
bench([[vim.o.wrap = (i % 2 == 0)]], { n = trials, label = 'vim.o.wrap = (i % 2 == 0)' })
end)
it('vim.o scalar (string)', function()
-- makeprg has a trivial did_set handler, so this isolates the set path itself.
bench("vim.o.makeprg = 'make'", ITERS, [[vim.o.makeprg = 'make']])
bench([[vim.o.makeprg = 'make']], { n = trials, label = "vim.o.makeprg = 'make'" })
end)
it('vim.opt array via table', function()
bench(
"vim.opt.wildignore = {'*.o','*.a','*.so'}",
ITERS,
[[
vim.opt.wildignore = { '*.o', '*.a', '*.so' }
]]
)
bench([[vim.opt.wildignore = { '*.o', '*.a', '*.so' }]], {
n = trials,
label = "vim.opt.wildignore = {'*.o','*.a','*.so'}",
})
end)
it('vim.opt map via table', function()
bench(
"vim.opt.listchars = { eol='~', space='.', tab='> ' }",
ITERS,
[[
vim.opt.listchars = { eol = '~', space = '.', tab = '> ' }
]]
)
bench([[vim.opt.listchars = { eol = '~', space = '.', tab = '> ' }]], {
n = trials,
label = "vim.opt.listchars = { eol='~', space='.', tab='> ' }",
})
end)
it('vim.opt:append (operation)', function()
bench(
'vim.opt.wildignore:append(...)',
ITERS,
[[
vim.o.wildignore = ''
vim.opt.wildignore:append({ '*.tmp', '*.bak' })
]]
]],
{ n = trials, label = 'vim.opt.wildignore:append(...)' }
)
end)
it('nvim_set_option_value with table (direct API)', function()
bench(
'nvim_set_option_value(listchars, {..})',
ITERS,
[[
vim.api.nvim_set_option_value('listchars', { eol = '~', space = '.' }, {})
]]
)
bench([[vim.api.nvim_set_option_value('listchars', { eol = '~', space = '.' }, {})]], {
n = trials,
label = 'nvim_set_option_value(listchars, {..})',
})
end)
end)

View File

@@ -1,52 +1,17 @@
local t = require('test.testutil')
describe('vim.text', function()
--- @param t number[]
local function mean(t)
assert(#t > 0)
local sum = 0
for _, v in ipairs(t) do
sum = sum + v
end
return sum / #t
end
--- @param t number[]
local function median(t)
local len = #t
if len % 2 == 0 then
return t[len / 2]
end
return t[(len + 1) / 2]
end
--- @param f fun(t: number[]): table<number, number|string|table>
local function measure(f, input, N)
local stats = {} ---@type number[]
for _ = 1, N do
local tic = vim.uv.hrtime()
f(input)
local toc = vim.uv.hrtime()
stats[#stats + 1] = (toc - tic) / 1000000
end
table.sort(stats)
print(
string.format(
'\nN: %d, Min: %0.6f ms, Max: %0.6f ms, Median: %0.6f ms, Mean: %0.6f ms',
N,
math.min(unpack(stats)),
math.max(unpack(stats)),
median(stats),
mean(stats)
)
)
end
local input, output = string.rep('😂', 2 ^ 16), string.rep('F09F9882', 2 ^ 16)
it('hexencode', function()
measure(vim.text.hexencode, input, 100)
t.bench(function()
vim.text.hexencode(input)
end, { n = 100, label = 'hexencode' })
end)
it('hexdecode', function()
measure(vim.text.hexdecode, output, 100)
t.bench(function()
vim.text.hexdecode(output)
end, { n = 100, label = 'hexdecode' })
end)
end)

View File

@@ -1,45 +1,6 @@
local t = require('test.testutil')
describe('vim.trim()', function()
--- @param t number[]
local function mean(t)
assert(#t > 0)
local sum = 0
for _, v in ipairs(t) do
sum = sum + v
end
return sum / #t
end
--- @param t number[]
local function median(t)
local len = #t
if len % 2 == 0 then
return t[len / 2]
end
return t[(len + 1) / 2]
end
--- @param f fun(t: number[]): table<number, number|string|table>
local function measure(f, input, N)
local stats = {} ---@type number[]
for _ = 1, N do
local tic = vim.uv.hrtime()
f(input)
local toc = vim.uv.hrtime()
stats[#stats + 1] = (toc - tic) / 1000000
end
table.sort(stats)
print(
string.format(
'\nN: %d, Min: %0.6f ms, Max: %0.6f ms, Median: %0.6f ms, Mean: %0.6f ms',
N,
math.min(unpack(stats)),
math.max(unpack(stats)),
median(stats),
mean(stats)
)
)
end
local strings = {
['10000 whitespace characters'] = string.rep(' ', 10000),
['10000 whitespace characters and one non-whitespace at the end'] = string.rep(' ', 10000)
@@ -64,7 +25,9 @@ describe('vim.trim()', function()
for _, name in ipairs(string_names) do
it(name, function()
measure(vim.trim, strings[name], 100)
t.bench(function()
vim.trim(strings[name])
end, { n = 100, label = name })
end)
end
end)