mirror of
https://github.com/littensy/charm.git
synced 2026-10-20 17:51:37 +00:00
perf: call mapper as-is in mapped()
This commit is contained in:
parent
27c7f5a6fd
commit
c52f55a55e
2 changed files with 42 additions and 53 deletions
|
|
@ -3,55 +3,56 @@ local bench = require("./bench")
|
|||
|
||||
local computed = Charm.computed
|
||||
local mapped = Charm.mapped
|
||||
local setActiveSub = Charm.setActiveSub
|
||||
local signal = Charm.signal
|
||||
|
||||
type Entry = { number }
|
||||
type Key = number
|
||||
type Value = number
|
||||
|
||||
local count = 512
|
||||
local initialState: { Entry } = table.create(count)
|
||||
local initialState: { [Key]: Value } = table.create(count, 0)
|
||||
|
||||
for i = 1, count do
|
||||
initialState[i] = table.create(64, i)
|
||||
local function expensiveOperation()
|
||||
for _ = 1, 100 do
|
||||
continue
|
||||
end
|
||||
end
|
||||
|
||||
local function mapper(value: Entry, key: number): (Entry, number)
|
||||
return table.clone(value), key + 1
|
||||
end
|
||||
|
||||
local function simpleMapped(source: () -> { Entry }, mapper: (Entry, number) -> (Entry, number)): () -> { Entry }
|
||||
local function simpleMapped(
|
||||
source: () -> { [Key]: Value },
|
||||
mapper: (Value, Key) -> (Value, Key?)
|
||||
): () -> { [Key]: Value }
|
||||
return computed(function()
|
||||
local state = source()
|
||||
local nextState = {}
|
||||
|
||||
for index, value in state do
|
||||
local currentSub = setActiveSub(nil)
|
||||
local success, valueOut, keyOut = pcall(mapper, value, index)
|
||||
setActiveSub(currentSub)
|
||||
|
||||
if success then
|
||||
nextState[keyOut] = valueOut
|
||||
end
|
||||
for keyIn, valueIn in state do
|
||||
local valueOut, keyOut = mapper(valueIn, keyIn)
|
||||
nextState[if keyOut == nil then keyIn else keyOut] = valueOut
|
||||
end
|
||||
|
||||
return state
|
||||
return nextState
|
||||
end)
|
||||
end
|
||||
|
||||
local function updatePercentage(percent: number): ({ Entry }) -> { Entry }
|
||||
local function updateRandomly(updateChance: number): ({ [Key]: Value }) -> { [Key]: Value }
|
||||
return function(state)
|
||||
local nextState = table.clone(state)
|
||||
for index, value in state do
|
||||
if math.random() < percent then
|
||||
nextState[index] = table.clone(value)
|
||||
for key in state do
|
||||
if math.random() < updateChance then
|
||||
nextState[key] += 1
|
||||
end
|
||||
end
|
||||
return nextState
|
||||
end
|
||||
end
|
||||
|
||||
for _, percent in { 0.1, 0.5, 1 } do
|
||||
bench.run(`mapped {percent}`, function()
|
||||
for _, updateChance in { 0.1, 0.5, 1 } do
|
||||
local function mapper(value: Value, key: Key): (Value, Key?)
|
||||
expensiveOperation()
|
||||
return value + 1, key + 1
|
||||
end
|
||||
|
||||
bench.run(`mapped {updateChance}`, function()
|
||||
local src, setSrc = signal(initialState)
|
||||
local out = mapped(src, mapper)
|
||||
|
||||
|
|
@ -59,14 +60,14 @@ for _, percent in { 0.1, 0.5, 1 } do
|
|||
out()
|
||||
bench.profileend()
|
||||
|
||||
setSrc(updatePercentage(percent))
|
||||
setSrc(updateRandomly(updateChance))
|
||||
|
||||
bench.profilebegin("update")
|
||||
out()
|
||||
bench.profileend()
|
||||
end)
|
||||
|
||||
bench.run(`control {percent}`, function()
|
||||
bench.run(`control {updateChance}`, function()
|
||||
local src, setSrc = signal(initialState)
|
||||
local out = simpleMapped(src, mapper)
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ for _, percent in { 0.1, 0.5, 1 } do
|
|||
out()
|
||||
bench.profileend()
|
||||
|
||||
setSrc(updatePercentage(percent))
|
||||
setSrc(updateRandomly(updateChance))
|
||||
|
||||
bench.profilebegin("update")
|
||||
out()
|
||||
|
|
|
|||
|
|
@ -914,22 +914,9 @@ end
|
|||
@see https://github.com/littensy/charm?tab=readme-ov-file#mappedgetter-mapper
|
||||
]]
|
||||
local function mapped<KI, VI, KO, VO>(getter: Getter<{ [KI]: VI }>, mapper: (VI, KI) -> (VO, KO)): Getter<{ [KO]: VO }>
|
||||
local map = noYield(mapper)
|
||||
local nextInput: { [KI]: VI } = {}
|
||||
local keyMap: { [KI]: KO } = {}
|
||||
|
||||
local function mapUntracked(value: VI, key: KI): (VO, KO)
|
||||
local prevSub = setActiveSub(nil)
|
||||
local success, valueOut, keyOut = pcall(map, value, key)
|
||||
activeSub = prevSub
|
||||
|
||||
if not success then
|
||||
error(`mapped() transform function failed: {valueOut}`, 0)
|
||||
end
|
||||
|
||||
return valueOut, if keyOut == nil then key :: any else keyOut
|
||||
end
|
||||
|
||||
return computed(function(lastOutput: { [KO]: VO }?): { [KO]: VO }
|
||||
local nextOutput: { [KO]: VO } = lastOutput or {}
|
||||
local prevInput = nextInput
|
||||
|
|
@ -937,19 +924,19 @@ local function mapped<KI, VI, KO, VO>(getter: Getter<{ [KI]: VI }>, mapper: (VI,
|
|||
|
||||
-- For keys that were removed from the input, remove the corresponding
|
||||
-- keys from the output.
|
||||
for prevKeyIn in prevInput do
|
||||
if nextInput[prevKeyIn] ~= nil then
|
||||
for keyIn in prevInput do
|
||||
if nextInput[keyIn] ~= nil then
|
||||
continue
|
||||
end
|
||||
|
||||
local prevKeyOut = keyMap[prevKeyIn]
|
||||
local keyOut = keyMap[keyIn]
|
||||
|
||||
if prevKeyOut ~= nil then
|
||||
if keyOut ~= nil then
|
||||
if nextOutput == lastOutput then
|
||||
nextOutput = table.clone(nextOutput)
|
||||
end
|
||||
nextOutput[prevKeyOut] = nil
|
||||
keyMap[prevKeyIn] = nil
|
||||
nextOutput[keyOut] = nil
|
||||
keyMap[keyIn] = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -957,13 +944,14 @@ local function mapped<KI, VI, KO, VO>(getter: Getter<{ [KI]: VI }>, mapper: (VI,
|
|||
-- the output. If the mapper returns a new key that is different from
|
||||
-- the previous call, the old mapped key should be removed from the
|
||||
-- output.
|
||||
for nextKeyIn, nextValueIn in nextInput do
|
||||
if nextValueIn == prevInput[nextKeyIn] then
|
||||
for keyIn, nextValueIn in nextInput do
|
||||
if nextValueIn == prevInput[keyIn] then
|
||||
continue
|
||||
end
|
||||
|
||||
local nextValueOut, nextKeyOut = mapUntracked(nextValueIn, nextKeyIn)
|
||||
local prevKeyOut = keyMap[nextKeyIn]
|
||||
local nextValueOut, keyOutOrNil = mapper(nextValueIn, keyIn)
|
||||
local nextKeyOut: KO = if keyOutOrNil ~= nil then keyOutOrNil else keyIn :: any
|
||||
local prevKeyOut = keyMap[keyIn]
|
||||
|
||||
if prevKeyOut ~= nil and prevKeyOut ~= nextKeyOut then
|
||||
if nextOutput == lastOutput then
|
||||
|
|
@ -971,13 +959,13 @@ local function mapped<KI, VI, KO, VO>(getter: Getter<{ [KI]: VI }>, mapper: (VI,
|
|||
end
|
||||
nextOutput[prevKeyOut] = nil
|
||||
nextOutput[nextKeyOut] = nextValueOut
|
||||
keyMap[nextKeyIn] = nextKeyOut
|
||||
keyMap[keyIn] = nextKeyOut
|
||||
elseif nextOutput[nextKeyOut] ~= nextValueOut then
|
||||
if nextOutput == lastOutput then
|
||||
nextOutput = table.clone(nextOutput)
|
||||
end
|
||||
nextOutput[nextKeyOut] = nextValueOut
|
||||
keyMap[nextKeyIn] = nextKeyOut
|
||||
keyMap[keyIn] = nextKeyOut
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue