refactor: prepping for vim.loader

This commit is contained in:
Folke Lemaitre 2023-03-20 16:42:11 +01:00
parent 887eb75591
commit 690f9e88e2
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
2 changed files with 62 additions and 57 deletions

View File

@ -26,32 +26,34 @@ local M = {}
---@field modname string Name of the module ---@field modname string Name of the module
---@field stat? uv_fs_t File stat of the module path ---@field stat? uv_fs_t File stat of the module path
M.VERSION = 3 ---@alias LoaderStats table<string, {total:number, time:number, [string]:number?}?>
M.path = vim.fn.stdpath("cache") .. "/luac" M.path = vim.fn.stdpath("cache") .. "/luac"
M.enabled = false M.enabled = false
---@type table<string, {total:number, time:number, [string]:number?}?>
M.stats = {
find = { total = 0, time = 0, not_found = 0 },
}
---@class Loader ---@class Loader
---@field _rtp string[] ---@field _rtp string[]
---@field _rtp_pure string[] ---@field _rtp_pure string[]
---@field _rtp_key string ---@field _rtp_key string
local Loader = { local Loader = {
VERSION = 3,
---@type table<string, table<string,ModuleInfo>> ---@type table<string, table<string,ModuleInfo>>
_indexed = {}, _indexed = {},
---@type table<string, string[]> ---@type table<string, string[]>
_topmods = {}, _topmods = {},
_loadfile = loadfile, _loadfile = loadfile,
---@type LoaderStats
_stats = {
find = { total = 0, time = 0, not_found = 0 },
},
} }
--- Tracks the time spent in a function --- Tracks the time spent in a function
---@private ---@private
function M._track(stat, start) function Loader.track(stat, start)
M.stats[stat] = M.stats[stat] or { total = 0, time = 0 } Loader._stats[stat] = Loader._stats[stat] or { total = 0, time = 0 }
M.stats[stat].total = M.stats[stat].total + 1 Loader._stats[stat].total = Loader._stats[stat].total + 1
M.stats[stat].time = M.stats[stat].time + uv.hrtime() - start Loader._stats[stat].time = Loader._stats[stat].time + uv.hrtime() - start
end end
--- slightly faster/different version than vim.fs.normalize --- slightly faster/different version than vim.fs.normalize
@ -77,7 +79,7 @@ end
function Loader.get_rtp() function Loader.get_rtp()
local start = uv.hrtime() local start = uv.hrtime()
if vim.in_fast_event() then if vim.in_fast_event() then
M._track("get_rtp", start) Loader.track("get_rtp", start)
return (Loader._rtp or {}), false return (Loader._rtp or {}), false
end end
local updated = false local updated = false
@ -94,7 +96,7 @@ function Loader.get_rtp()
updated = true updated = true
Loader._rtp_key = key Loader._rtp_key = key
end end
M._track("get_rtp", start) Loader.track("get_rtp", start)
return Loader._rtp, updated return Loader._rtp, updated
end end
@ -115,7 +117,7 @@ function Loader.write(name, entry)
local cname = Loader.cache_file(name) local cname = Loader.cache_file(name)
local f = assert(uv.fs_open(cname, "w", 438)) local f = assert(uv.fs_open(cname, "w", 438))
local header = { local header = {
M.VERSION, Loader.VERSION,
entry.hash.size, entry.hash.size,
entry.hash.mtime.sec, entry.hash.mtime.sec,
entry.hash.mtime.nsec, entry.hash.mtime.nsec,
@ -142,16 +144,16 @@ function Loader.read(name)
---@type integer[]|{[0]:integer} ---@type integer[]|{[0]:integer}
local header = vim.split(data:sub(1, zero - 1), ",") local header = vim.split(data:sub(1, zero - 1), ",")
if tonumber(header[1]) ~= M.VERSION then if tonumber(header[1]) ~= Loader.VERSION then
return return
end end
M._track("read", start) Loader.track("read", start)
return { return {
hash = { size = tonumber(header[2]), mtime = { sec = tonumber(header[3]), nsec = tonumber(header[4]) } }, hash = { size = tonumber(header[2]), mtime = { sec = tonumber(header[3]), nsec = tonumber(header[4]) } },
chunk = data:sub(zero + 1), chunk = data:sub(zero + 1),
} }
end end
M._track("read", start) Loader.track("read", start)
end end
--- The `package.loaders` loader for lua files using the cache. --- The `package.loaders` loader for lua files using the cache.
@ -163,10 +165,10 @@ function Loader.loader(modname)
local ret = M.find(modname)[1] local ret = M.find(modname)[1]
if ret then if ret then
local chunk, err = Loader.load(ret.modpath, { hash = ret.stat }) local chunk, err = Loader.load(ret.modpath, { hash = ret.stat })
M._track("loader", start) Loader.track("loader", start)
return chunk or error(err) return chunk or error(err)
end end
M._track("loader", start) Loader.track("loader", start)
return "\ncache_loader: module " .. modname .. " not found" return "\ncache_loader: module " .. modname .. " not found"
end end
@ -189,10 +191,10 @@ function Loader.loader_lib(modname)
local dash = modname:find("-", 1, true) local dash = modname:find("-", 1, true)
local funcname = dash and modname:sub(dash + 1) or modname local funcname = dash and modname:sub(dash + 1) or modname
local chunk, err = package.loadlib(ret.modpath, "luaopen_" .. funcname:gsub("%.", "_")) local chunk, err = package.loadlib(ret.modpath, "luaopen_" .. funcname:gsub("%.", "_"))
M._track("loader_lib", start) Loader.track("loader_lib", start)
return chunk or error(err) return chunk or error(err)
end end
M._track("loader_lib", start) Loader.track("loader_lib", start)
return "\ncache_loader_lib: module " .. modname .. " not found" return "\ncache_loader_lib: module " .. modname .. " not found"
end end
@ -209,7 +211,7 @@ function Loader.loadfile(filename, mode, env, hash)
filename = Loader.normalize(filename) filename = Loader.normalize(filename)
mode = nil -- ignore mode, since we byte-compile the lua source files mode = nil -- ignore mode, since we byte-compile the lua source files
local chunk, err = Loader.load(filename, { mode = mode, env = env, hash = hash }) local chunk, err = Loader.load(filename, { mode = mode, env = env, hash = hash })
M._track("loadfile", start) Loader.track("loadfile", start)
return chunk, err return chunk, err
end end
@ -244,7 +246,7 @@ function Loader.load(modpath, opts)
if not hash then if not hash then
-- trigger correct error -- trigger correct error
chunk, err = Loader._loadfile(modpath, opts.mode, opts.env) chunk, err = Loader._loadfile(modpath, opts.mode, opts.env)
M._track("load", start) Loader.track("load", start)
return chunk, err return chunk, err
end end
@ -254,7 +256,7 @@ function Loader.load(modpath, opts)
-- selene: allow(incorrect_standard_library_use) -- selene: allow(incorrect_standard_library_use)
chunk, err = load(entry.chunk --[[@as string]], "@" .. modpath, opts.mode, opts.env) chunk, err = load(entry.chunk --[[@as string]], "@" .. modpath, opts.mode, opts.env)
if not (err and err:find("cannot load incompatible bytecode", 1, true)) then if not (err and err:find("cannot load incompatible bytecode", 1, true)) then
M._track("load", start) Loader.track("load", start)
return chunk, err return chunk, err
end end
end end
@ -265,7 +267,7 @@ function Loader.load(modpath, opts)
entry.chunk = string.dump(chunk) entry.chunk = string.dump(chunk)
Loader.write(modpath, entry) Loader.write(modpath, entry)
end end
M._track("load", start) Loader.track("load", start)
return chunk, err return chunk, err
end end
@ -332,7 +334,7 @@ function M.find(modname, opts)
elseif Loader.lsmod(path)[topmod] then elseif Loader.lsmod(path)[topmod] then
for _, pattern in ipairs(patterns) do for _, pattern in ipairs(patterns) do
local modpath = path .. pattern local modpath = path .. pattern
M.stats.find.stat = (M.stats.find.stat or 0) + 1 Loader._stats.find.stat = (Loader._stats.find.stat or 0) + 1
local hash = uv.fs_stat(modpath) local hash = uv.fs_stat(modpath)
if hash then if hash then
results[#results + 1] = { modpath = modpath, stat = hash, modname = modname } results[#results + 1] = { modpath = modpath, stat = hash, modname = modname }
@ -361,10 +363,10 @@ function M.find(modname, opts)
_find(opts.paths) _find(opts.paths)
end end
M._track("find", start) Loader.track("find", start)
if #results == 0 then if #results == 0 then
-- module not found -- module not found
M.stats.find.not_found = M.stats.find.not_found + 1 Loader._stats.find.not_found = Loader._stats.find.not_found + 1
end end
return results return results
@ -474,48 +476,51 @@ function Loader.lsmod(path)
end end
end end
end end
M._track("lsmod", start) Loader.track("lsmod", start)
end end
return Loader._indexed[path] return Loader._indexed[path]
end end
--- Debug function that wrapps all loaders and tracks stats --- Debug function that wrapps all loaders and tracks stats
---@private ---@private
function M.profile_loaders() function M._profile_loaders()
for l, loader in pairs(package.loaders) do for l, loader in pairs(package.loaders) do
local loc = debug.getinfo(loader, "Sn").source:sub(2) local loc = debug.getinfo(loader, "Sn").source:sub(2)
package.loaders[l] = function(modname) package.loaders[l] = function(modname)
local start = vim.loop.hrtime() local start = vim.loop.hrtime()
local ret = loader(modname) local ret = loader(modname)
M._track("loader " .. l .. ": " .. loc, start) Loader.track("loader " .. l .. ": " .. loc, start)
M._track("loader_all", start) Loader.track("loader_all", start)
return ret return ret
end end
end end
end end
--- Prints all cache stats --- Prints all cache stats
---@param opts? {print?:boolean}
---@return LoaderStats
---@private ---@private
function M.inspect() function M._inspect(opts)
if opts and opts.print then
---@private ---@private
local function ms(nsec) local function ms(nsec)
return math.floor(nsec / 1e6 * 1000 + 0.5) / 1000 .. "ms" return math.floor(nsec / 1e6 * 1000 + 0.5) / 1000 .. "ms"
end end
local chunks = {} ---@type string[][] local chunks = {} ---@type string[][]
---@type string[] ---@type string[]
local stats = vim.tbl_keys(M.stats) local stats = vim.tbl_keys(Loader._stats)
table.sort(stats) table.sort(stats)
for _, stat in ipairs(stats) do for _, stat in ipairs(stats) do
vim.list_extend(chunks, { vim.list_extend(chunks, {
{ "\n" .. stat .. "\n", "Title" }, { "\n" .. stat .. "\n", "Title" },
{ "* total: " }, { "* total: " },
{ tostring(M.stats[stat].total) .. "\n", "Number" }, { tostring(Loader._stats[stat].total) .. "\n", "Number" },
{ "* time: " }, { "* time: " },
{ ms(M.stats[stat].time) .. "\n", "Bold" }, { ms(Loader._stats[stat].time) .. "\n", "Bold" },
{ "* avg time: " }, { "* avg time: " },
{ ms(M.stats[stat].time / M.stats[stat].total) .. "\n", "Bold" }, { ms(Loader._stats[stat].time / Loader._stats[stat].total) .. "\n", "Bold" },
}) })
for k, v in pairs(M.stats[stat]) do for k, v in pairs(Loader._stats[stat]) do
if not vim.tbl_contains({ "time", "total" }, k) then if not vim.tbl_contains({ "time", "total" }, k) then
chunks[#chunks + 1] = { "* " .. k .. ":" .. string.rep(" ", 9 - #k) } chunks[#chunks + 1] = { "* " .. k .. ":" .. string.rep(" ", 9 - #k) }
chunks[#chunks + 1] = { tostring(v) .. "\n", "Number" } chunks[#chunks + 1] = { tostring(v) .. "\n", "Number" }
@ -523,8 +528,8 @@ function M.inspect()
end end
end end
vim.api.nvim_echo(chunks, true, {}) vim.api.nvim_echo(chunks, true, {})
end
return Loader._stats
end end
M._Cache = Loader
return M return M

View File

@ -675,7 +675,7 @@ function M:debug()
end) end)
self:nl() self:nl()
Util.foreach(require("lazy.core.cache").stats, function(name, stats) Util.foreach(require("lazy.core.cache")._inspect(), function(name, stats)
self:append(name, "LazyH2"):nl() self:append(name, "LazyH2"):nl()
local props = { local props = {
{ "total", stats.total or 0, "Number" }, { "total", stats.total or 0, "Number" },