From fea3b6d81ace6a23bf990602731ba31902c26375 Mon Sep 17 00:00:00 2001 From: Tom Lau Date: Mon, 1 Jul 2024 09:30:43 +0800 Subject: [PATCH 1/3] perf: Optimize work distribution in multi-threaded `--check` The current hash function used to distribute work seems not perfect. We can actually use round robin to distribute after sorting file list. --- script/cli/check_worker.lua | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/script/cli/check_worker.lua b/script/cli/check_worker.lua index 23c34b2c5..822c83dc6 100644 --- a/script/cli/check_worker.lua +++ b/script/cli/check_worker.lua @@ -43,15 +43,6 @@ local checkLevel = define.DiagnosticSeverity[CHECKLEVEL] or define.DiagnosticSev util.enableCloseFunction() --- Hash function used to distribute work. -local function hashString(str) - local hash = 0 - for i = 1, #str do - hash = (hash * 37 & 0xFFFFFFFF) + str:byte(i, i) - end - return hash -end - local lastClock = os.clock() local results = {} @@ -109,9 +100,9 @@ xpcall(lclient.start, errorhandler, lclient, function (client) local uris = files.getChildFiles(rootUri) local max = #uris + table.sort(uris) -- sort file list to ensure the work distribution order across multiple threads for i, uri in ipairs(uris) do - local hash = hashString(uri) % numThreads + 1 - if hash == threadId then + if (i % numThreads + 1) == threadId then files.open(uri) diag.doDiagnostic(uri, true) -- Print regularly but always print the last entry to ensure that logs written to files don't look incomplete. From cad34b796b14c1e9d9ae062339d7e9d4cd18cc22 Mon Sep 17 00:00:00 2001 From: Tom Lau Date: Mon, 1 Jul 2024 09:40:16 +0800 Subject: [PATCH 2/3] perf: Optimize undefined-field check early break logic The current early break only wants to check if there are any definition. There is no need to fetch the full definitions list. We can early break as soon as we found the 1st one. --- script/core/diagnostics/undefined-field.lua | 2 +- script/vm/def.lua | 39 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua index 4fd55966d..ab5d4d544 100644 --- a/script/core/diagnostics/undefined-field.lua +++ b/script/core/diagnostics/undefined-field.lua @@ -21,7 +21,7 @@ return function (uri, callback) local function checkUndefinedField(src) await.delay() - if #vm.getDefs(src) > 0 then + if vm.hasDef(src) then return end local node = src.node diff --git a/script/vm/def.lua b/script/vm/def.lua index 5388a3849..669d39c25 100644 --- a/script/vm/def.lua +++ b/script/vm/def.lua @@ -92,3 +92,42 @@ function vm.getDefs(source) return results end + +local HAS_DEF_ERR = {} -- the error object for comparing +local function checkHasDef(checkFunc, source, pushResult) + local _, err = pcall(checkFunc, source, pushResult) + return err == HAS_DEF_ERR +end + +---@param source parser.object +function vm.hasDef(source) + local mark = {} + local hasLocal + local function pushResult(src) + if src.type == 'local' then + if hasLocal then + return + end + hasLocal = true + if source.type ~= 'local' + and source.type ~= 'getlocal' + and source.type ~= 'setlocal' + and source.type ~= 'doc.cast.name' then + return + end + end + if not mark[src] then + mark[src] = true + if guide.isAssign(src) + or guide.isLiteral(src) then + -- break out on 1st result using error() with a unique error object + error(HAS_DEF_ERR) + end + end + end + + return checkHasDef(searchBySimple, source, pushResult) + or checkHasDef(searchByLocalID, source, pushResult) + or checkHasDef(vm.compileByNodeChain, source, pushResult) + or checkHasDef(searchByNode, source, pushResult) +end From f58d0bade94622ac80c89250a31f38195bf99756 Mon Sep 17 00:00:00 2001 From: Tom Lau Date: Mon, 1 Jul 2024 09:42:02 +0800 Subject: [PATCH 3/3] doc: Update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index ef4a40179..d777fdc05 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ * `NEW` Add postfix snippet for `unpack` * `FIX` `diagnostics.severity` defaulting to "Warning" when run using `--check` [#2730](https://github.com/LuaLS/lua-language-server/issues/2730) * `NEW` Add support for lambda style functions, `|paramList| expr` is syntactic sugar for `function(paramList) return expr end` +* `CHG` Improve performance of multithreaded `--check` and `undefined-field` diagnostic ## 3.9.3 `2024-6-11`