mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-12 02:23:39 +08:00
127 lines
3.5 KiB
Lua
127 lines
3.5 KiB
Lua
local B = {}
|
||
local stat = require "plenary.benchmark.stat"
|
||
|
||
local get_stats = function(results)
|
||
local ret = {}
|
||
|
||
ret.max, ret.min = stat.maxmin(results)
|
||
ret.mean = stat.mean(results)
|
||
ret.median = stat.median(results)
|
||
ret.std = stat.std_dev(results)
|
||
|
||
return ret
|
||
end
|
||
|
||
local get_output = function(index, res, runs)
|
||
-- divine with a sutable one / 1e3, 1e6, 1e9
|
||
local time_types = { "ns", "μs", "ms" }
|
||
|
||
local get_leading = function(time)
|
||
time = math.floor(time)
|
||
local count = 0
|
||
repeat
|
||
time = math.floor(time / 10)
|
||
count = count + 1
|
||
until time <= 0
|
||
return count
|
||
end
|
||
|
||
local get_best_fmt = function(time)
|
||
for _, v in ipairs(time_types) do
|
||
if math.abs(time) < 1000.0 then
|
||
return string.format("%s%3.1f %s", string.rep(" ", 3 - get_leading(time)), time, v)
|
||
end
|
||
time = time / 1000.0
|
||
end
|
||
return string.format("%.1f %s", time, "s")
|
||
end
|
||
|
||
return string.format(
|
||
"Benchmark #%d: '%s'\n Time(mean ± σ): %s ± %s\n Range(min … max): %s … %s %d runs\n",
|
||
index,
|
||
res.name,
|
||
get_best_fmt(res.stats.mean),
|
||
get_best_fmt(res.stats.std),
|
||
get_best_fmt(res.stats.min),
|
||
get_best_fmt(res.stats.max),
|
||
runs
|
||
)
|
||
end
|
||
|
||
local get_summary = function(res)
|
||
if #res == 1 then
|
||
return ""
|
||
end
|
||
|
||
local fastest_mean = math.huge
|
||
local fastest_index = 1
|
||
for i, benchmark in ipairs(res) do
|
||
if benchmark.stats.mean < fastest_mean then
|
||
fastest_mean = benchmark.stats.mean
|
||
fastest_index = i
|
||
end
|
||
end
|
||
|
||
if fastest_mean == math.huge then
|
||
return ""
|
||
end
|
||
|
||
local output = {}
|
||
local fastest = res[fastest_index].stats
|
||
for i, benchmark in ipairs(res) do
|
||
if i ~= fastest_index then
|
||
local result = benchmark.stats
|
||
local ratio = result.mean / fastest.mean
|
||
|
||
-- // https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulas
|
||
-- // Covariance asssumed to be 0, i.e. variables are assumed to be independent
|
||
local ratio_std = ratio
|
||
* math.sqrt(math.pow(result.std / result.mean, 2) + math.pow(fastest.std / fastest.mean, 2))
|
||
|
||
table.insert(output, string.format(" %.1f ± %.1f times faster than '%s'\n", ratio, ratio_std, benchmark.name))
|
||
end
|
||
end
|
||
|
||
return string.format("Summary\n '%s' ran\n%s", res[fastest_index].name, table.concat(output, ""))
|
||
end
|
||
|
||
---@class benchmark_run_opts
|
||
---@field warmup number @number of initial runs before starting to track time.
|
||
---@field runs number @number of runs to make
|
||
---@field fun table<array<string, function>> @functions to execute
|
||
|
||
---Benchmark a function
|
||
---@param name string @benchmark name
|
||
---@param opts benchmark_run_opts
|
||
local bench = function(name, opts)
|
||
vim.validate {
|
||
opts = { opts, "table" },
|
||
fun = { opts.fun, "table" },
|
||
}
|
||
opts.warmup = vim.F.if_nil(opts.warmup, 3)
|
||
opts.runs = vim.F.if_nil(opts.runs, 5)
|
||
|
||
opts.fun = type(opts.fun) == "function" and { opts.fun } or opts.fun
|
||
local output = { string.format("Benchmark Group: '%s' -----------------------\n", name) }
|
||
local res = {}
|
||
for i, fun in ipairs(opts.fun) do
|
||
res[i] = { name = fun[1], results = {} }
|
||
for _ = 1, opts.warmup do
|
||
fun[2]()
|
||
end
|
||
for j = 1, opts.runs do
|
||
local start = vim.loop.hrtime()
|
||
fun[2]()
|
||
res[i].results[j] = vim.loop.hrtime() - start
|
||
end
|
||
res[i].stats = get_stats(res[i].results)
|
||
table.insert(output, get_output(i, res[i], opts.runs))
|
||
end
|
||
|
||
print(string.format("%s\n%s", table.concat(output, ""), get_summary(res)))
|
||
|
||
return res
|
||
end
|
||
|
||
return bench
|