perf: simplify mapped logic

This commit is contained in:
littensy 2025-05-06 17:07:30 -07:00
parent f704acce71
commit b61d0b3b86
2 changed files with 14 additions and 56 deletions

View file

@ -10,7 +10,7 @@ local function run(name: string, test: () -> ())
table.clear(times) table.clear(times)
table.clear(stack) table.clear(stack)
while os.clock() - start < 1 do while os.clock() - start < 2 do
iter += 1 iter += 1
test() test()
end end

View file

@ -10,12 +10,12 @@ type Mapped =
& (<KI, VI, KO, VO>(selector: () -> { [KI]: VI }, mapper: (item: VI, key: KI) -> (VO, KO)) -> () -> { [KO]: VO }) & (<KI, VI, KO, VO>(selector: () -> { [KI]: VI }, mapper: (item: VI, key: KI) -> (VO, KO)) -> () -> { [KO]: VO })
& (<K, VI, VO>(selector: () -> { [K]: VI }, mapper: (item: VI, key: K) -> VO) -> () -> { [K]: VO }) & (<K, VI, VO>(selector: () -> { [K]: VI }, mapper: (item: VI, key: K) -> VO) -> () -> { [K]: VO })
local function ensureCanMutateOutput<KO, VO>(output: { [KO]: VO }, previous: { [KO]: VO }): { [KO]: VO } local function ensureCanMutateOutput<KO, VO>(output: { [KO]: VO }, previous: { [KO]: VO }?): { [KO]: VO }
return if output == previous then table.clone(output) else output return if output == previous then table.clone(output) else output
end end
local function mapped<KI, VI, KO, VO>(selector: () -> { [KI]: VI }, mapper: (item: VI, key: KI) -> (VO, KO)): () -> { [KO]: VO } local function mapped<KI, VI, KO, VO>(selector: () -> { [KI]: VI }, mapper: (item: VI, key: KI) -> (VO, KO)): () -> { [KO]: VO }
local currentData: { [KI]: VI } local currentData: { [KI]: VI } = {}
local keyCache: { [KI]: KO } = {} local keyCache: { [KI]: KO } = {}
local function map(value: VI, key: KI): (VO, KO) local function map(value: VI, key: KI): (VO, KO)
@ -31,75 +31,33 @@ local function mapped<KI, VI, KO, VO>(selector: () -> { [KI]: VI }, mapper: (ite
end end
return computed(function(previousOutput) return computed(function(previousOutput)
local currentOutput: { [KO]: VO } = previousOutput or {}
local previousData = currentData local previousData = currentData
currentData = selector() currentData = selector()
-- Only perform additions if this is the first run for key, cachedKey in keyCache do
if not previousOutput then if currentData[key] ~= nil then
local output: { [KO]: VO } = {} continue
for key, value in currentData do
local valueOut, keyOut = map(value, key)
if valueOut ~= nil then
output[keyOut] = valueOut
keyCache[key] = keyOut
end
end end
return output currentOutput = ensureCanMutateOutput(currentOutput, previousOutput)
currentOutput[cachedKey] = nil
keyCache[key] = nil
end end
local output = previousOutput
-- Check equality since the selector might not be a computed value
if currentData == previousData then
return output
end
-- Clear removed values from the output and key cache
for key in previousData do
if currentData[key] == nil then
local keyOut = keyCache[key]
if keyOut then
output = ensureCanMutateOutput(output, previousOutput)
output[keyOut] = nil
keyCache[key] = nil
end
end
end
-- Process new or changed values
for key, value in currentData do for key, value in currentData do
if value == previousData[key] then if value == previousData[key] then
continue continue
end end
local valueOut, keyOut = map(value, key) local valueOut, keyOut = map(value, key)
local cachedKeyOut = keyCache[key]
if valueOut ~= nil then currentOutput = ensureCanMutateOutput(currentOutput, previousOutput)
-- Skip update if the key and value are the same currentOutput[keyOut] = valueOut
if keyOut == cachedKeyOut and valueOut == output[cachedKeyOut] then keyCache[key] = keyOut
continue
end
output = ensureCanMutateOutput(output, previousOutput)
output[keyOut] = valueOut
keyCache[key] = keyOut
-- Remove the old key if a new key is assigned
if cachedKeyOut ~= nil and keyOut ~= cachedKeyOut then
output[cachedKeyOut] = nil
end
elseif cachedKeyOut ~= nil then
-- Remove the cached key if the new value is nil
output = ensureCanMutateOutput(output, previousOutput)
output[cachedKeyOut] = nil
keyCache[key] = nil
end
end end
return output return currentOutput
end) end)
end end