feat(lua): add noref to deepcopy

Problem:

Currently `deepcopy` hashes every single tables it copies so it can be
reused. For tables of mostly unique items that are non recursive, this
hashing is unnecessarily expensive

Solution:

Port the `noref` argument from Vimscripts `deepcopy()`.

The below benchmark demonstrates the results for two extreme cases of
tables of different sizes. One table that uses the same table lots of
times and one with all unique tables.

| test                 | `noref=false` (ms) | `noref=true` (ms) |
| -------------------- | ------------------ | ----------------- |
| unique tables (50)   | 6.59               | 2.62              |
| shared tables (50)   | 3.24               | 6.40              |
| unique tables (2000) | 23381.48           | 2884.53           |
| shared tables (2000) | 3505.54            | 14038.80          |

The results are basically the inverse of each other where `noref` is
much more performance on tables with unique fields, and `not noref` is
more performant on tables that reuse fields.
This commit is contained in:
Lewis Russell
2024-01-02 15:47:55 +00:00
committed by Lewis Russell
parent a064ed6229
commit 3734519e3b
9 changed files with 122 additions and 51 deletions

View File

@@ -158,7 +158,7 @@ end
function M._version(version, strict) -- Adapted from https://github.com/folke/lazy.nvim
if type(version) == 'table' then
if version.major then
return setmetatable(vim.deepcopy(version), Version)
return setmetatable(vim.deepcopy(version, true), Version)
end
return setmetatable({
major = version[1] or 0,
@@ -228,7 +228,7 @@ function VersionRange:has(version)
version = M.parse(version)
elseif getmetatable(version) ~= Version then
-- Need metatable to compare versions.
version = setmetatable(vim.deepcopy(version), Version)
version = setmetatable(vim.deepcopy(version, true), Version)
end
if version then
if version.prerelease ~= self.from.prerelease then
@@ -298,7 +298,7 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim
local semver = M.parse(version)
if semver then
local from = semver
local to = vim.deepcopy(semver)
local to = vim.deepcopy(semver, true)
if mods == '' or mods == '=' then
to.patch = to.patch + 1
elseif mods == '<' then