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 computed = Charm.computed
|
||||||
local mapped = Charm.mapped
|
local mapped = Charm.mapped
|
||||||
local setActiveSub = Charm.setActiveSub
|
|
||||||
local signal = Charm.signal
|
local signal = Charm.signal
|
||||||
|
|
||||||
type Entry = { number }
|
type Key = number
|
||||||
|
type Value = number
|
||||||
|
|
||||||
local count = 512
|
local count = 512
|
||||||
local initialState: { Entry } = table.create(count)
|
local initialState: { [Key]: Value } = table.create(count, 0)
|
||||||
|
|
||||||
for i = 1, count do
|
local function expensiveOperation()
|
||||||
initialState[i] = table.create(64, i)
|
for _ = 1, 100 do
|
||||||
|
continue
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function mapper(value: Entry, key: number): (Entry, number)
|
local function simpleMapped(
|
||||||
return table.clone(value), key + 1
|
source: () -> { [Key]: Value },
|
||||||
end
|
mapper: (Value, Key) -> (Value, Key?)
|
||||||
|
): () -> { [Key]: Value }
|
||||||
local function simpleMapped(source: () -> { Entry }, mapper: (Entry, number) -> (Entry, number)): () -> { Entry }
|
|
||||||
return computed(function()
|
return computed(function()
|
||||||
local state = source()
|
local state = source()
|
||||||
local nextState = {}
|
local nextState = {}
|
||||||
|
|
||||||
for index, value in state do
|
for keyIn, valueIn in state do
|
||||||
local currentSub = setActiveSub(nil)
|
local valueOut, keyOut = mapper(valueIn, keyIn)
|
||||||
local success, valueOut, keyOut = pcall(mapper, value, index)
|
nextState[if keyOut == nil then keyIn else keyOut] = valueOut
|
||||||
setActiveSub(currentSub)
|
|
||||||
|
|
||||||
if success then
|
|
||||||
nextState[keyOut] = valueOut
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return state
|
return nextState
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function updatePercentage(percent: number): ({ Entry }) -> { Entry }
|
local function updateRandomly(updateChance: number): ({ [Key]: Value }) -> { [Key]: Value }
|
||||||
return function(state)
|
return function(state)
|
||||||
local nextState = table.clone(state)
|
local nextState = table.clone(state)
|
||||||
for index, value in state do
|
for key in state do
|
||||||
if math.random() < percent then
|
if math.random() < updateChance then
|
||||||
nextState[index] = table.clone(value)
|
nextState[key] += 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nextState
|
return nextState
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, percent in { 0.1, 0.5, 1 } do
|
for _, updateChance in { 0.1, 0.5, 1 } do
|
||||||
bench.run(`mapped {percent}`, function()
|
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 src, setSrc = signal(initialState)
|
||||||
local out = mapped(src, mapper)
|
local out = mapped(src, mapper)
|
||||||
|
|
||||||
|
|
@ -59,14 +60,14 @@ for _, percent in { 0.1, 0.5, 1 } do
|
||||||
out()
|
out()
|
||||||
bench.profileend()
|
bench.profileend()
|
||||||
|
|
||||||
setSrc(updatePercentage(percent))
|
setSrc(updateRandomly(updateChance))
|
||||||
|
|
||||||
bench.profilebegin("update")
|
bench.profilebegin("update")
|
||||||
out()
|
out()
|
||||||
bench.profileend()
|
bench.profileend()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
bench.run(`control {percent}`, function()
|
bench.run(`control {updateChance}`, function()
|
||||||
local src, setSrc = signal(initialState)
|
local src, setSrc = signal(initialState)
|
||||||
local out = simpleMapped(src, mapper)
|
local out = simpleMapped(src, mapper)
|
||||||
|
|
||||||
|
|
@ -74,7 +75,7 @@ for _, percent in { 0.1, 0.5, 1 } do
|
||||||
out()
|
out()
|
||||||
bench.profileend()
|
bench.profileend()
|
||||||
|
|
||||||
setSrc(updatePercentage(percent))
|
setSrc(updateRandomly(updateChance))
|
||||||
|
|
||||||
bench.profilebegin("update")
|
bench.profilebegin("update")
|
||||||
out()
|
out()
|
||||||
|
|
|
||||||
|
|
@ -914,22 +914,9 @@ end
|
||||||
@see https://github.com/littensy/charm?tab=readme-ov-file#mappedgetter-mapper
|
@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 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 nextInput: { [KI]: VI } = {}
|
||||||
local keyMap: { [KI]: KO } = {}
|
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 }
|
return computed(function(lastOutput: { [KO]: VO }?): { [KO]: VO }
|
||||||
local nextOutput: { [KO]: VO } = lastOutput or {}
|
local nextOutput: { [KO]: VO } = lastOutput or {}
|
||||||
local prevInput = nextInput
|
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
|
-- For keys that were removed from the input, remove the corresponding
|
||||||
-- keys from the output.
|
-- keys from the output.
|
||||||
for prevKeyIn in prevInput do
|
for keyIn in prevInput do
|
||||||
if nextInput[prevKeyIn] ~= nil then
|
if nextInput[keyIn] ~= nil then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
local prevKeyOut = keyMap[prevKeyIn]
|
local keyOut = keyMap[keyIn]
|
||||||
|
|
||||||
if prevKeyOut ~= nil then
|
if keyOut ~= nil then
|
||||||
if nextOutput == lastOutput then
|
if nextOutput == lastOutput then
|
||||||
nextOutput = table.clone(nextOutput)
|
nextOutput = table.clone(nextOutput)
|
||||||
end
|
end
|
||||||
nextOutput[prevKeyOut] = nil
|
nextOutput[keyOut] = nil
|
||||||
keyMap[prevKeyIn] = nil
|
keyMap[keyIn] = nil
|
||||||
end
|
end
|
||||||
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 output. If the mapper returns a new key that is different from
|
||||||
-- the previous call, the old mapped key should be removed from the
|
-- the previous call, the old mapped key should be removed from the
|
||||||
-- output.
|
-- output.
|
||||||
for nextKeyIn, nextValueIn in nextInput do
|
for keyIn, nextValueIn in nextInput do
|
||||||
if nextValueIn == prevInput[nextKeyIn] then
|
if nextValueIn == prevInput[keyIn] then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
local nextValueOut, nextKeyOut = mapUntracked(nextValueIn, nextKeyIn)
|
local nextValueOut, keyOutOrNil = mapper(nextValueIn, keyIn)
|
||||||
local prevKeyOut = keyMap[nextKeyIn]
|
local nextKeyOut: KO = if keyOutOrNil ~= nil then keyOutOrNil else keyIn :: any
|
||||||
|
local prevKeyOut = keyMap[keyIn]
|
||||||
|
|
||||||
if prevKeyOut ~= nil and prevKeyOut ~= nextKeyOut then
|
if prevKeyOut ~= nil and prevKeyOut ~= nextKeyOut then
|
||||||
if nextOutput == lastOutput then
|
if nextOutput == lastOutput then
|
||||||
|
|
@ -971,13 +959,13 @@ local function mapped<KI, VI, KO, VO>(getter: Getter<{ [KI]: VI }>, mapper: (VI,
|
||||||
end
|
end
|
||||||
nextOutput[prevKeyOut] = nil
|
nextOutput[prevKeyOut] = nil
|
||||||
nextOutput[nextKeyOut] = nextValueOut
|
nextOutput[nextKeyOut] = nextValueOut
|
||||||
keyMap[nextKeyIn] = nextKeyOut
|
keyMap[keyIn] = nextKeyOut
|
||||||
elseif nextOutput[nextKeyOut] ~= nextValueOut then
|
elseif nextOutput[nextKeyOut] ~= nextValueOut then
|
||||||
if nextOutput == lastOutput then
|
if nextOutput == lastOutput then
|
||||||
nextOutput = table.clone(nextOutput)
|
nextOutput = table.clone(nextOutput)
|
||||||
end
|
end
|
||||||
nextOutput[nextKeyOut] = nextValueOut
|
nextOutput[nextKeyOut] = nextValueOut
|
||||||
keyMap[nextKeyIn] = nextKeyOut
|
keyMap[keyIn] = nextKeyOut
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue