Merge reactive scope refactor

This commit is contained in:
aaron 2023-09-15 12:54:42 +01:00
parent 0e439f084f
commit efc4798ddb
48 changed files with 2750 additions and 1949 deletions

View file

@ -1,167 +1,291 @@
local BENCH, START = require("test/testkit").benchmark()
-- try prevent inlining by wrapping in a closure referencing an upvalue that
-- cannot be determined at compile-time
local function NO_INLINE(fn)
local r = math.random()
return function(x)
local _ = r
fn(x)
end
end
local testkit = require("test/testkit")
local BENCH, START = testkit.benchmark()
local vide = require "src/init"
local source = vide.source
local derive = vide.derive
local indexes = vide.indexes
local values = vide.values
local cleanup = vide.cleanup
local create = vide.create
local N = 2^18 -- 262144[
local function TITLE(name: string)
print()
print(testkit.color.white(name))
end
BENCH("create state", function()
local N = 2^18 -- 262144
local function WRAP_BENCH(name: string, fn: () -> ())
vide.root(function(destroy)
BENCH(name, fn)
return destroy
end)()
end
TITLE "sources"
WRAP_BENCH("create source", function()
local cache = table.create(N)
local source = vide.source
for i = 1, START(N) do
cache[i] = source(1)
end
end)
BENCH("get value", function()
local state = vide.source(1)
WRAP_BENCH("get value", function()
local src = source(1)
for i = 1, START(N) do
state()
src()
end
end)
BENCH("set value", function()
local state = vide.source(1)
WRAP_BENCH("set value", function()
local src = source(1)
for i = 1, START(N) do
state(i)
src(i)
end
end)
BENCH("derive 1 state", function()
WRAP_BENCH("derive 1 source", function()
local cache = table.create(N)
local state = vide.source(1)
local derive = vide.derive
local src = source(1)
for i = 1, START(N) do
cache[i] = derive(function()
return state()
return src()
end)
end
end)
BENCH("derive 4 states", function()
WRAP_BENCH("derive 4 sources", function()
local cache = table.create(N)
local state = vide.source(1)
local state2 = vide.source(2)
local state3 = vide.source(3)
local state4 = vide.source(4)
local derive = vide.derive
local src = vide.source(1)
local src2 = vide.source(2)
local src3 = vide.source(3)
local src4 = vide.source(4)
for i = 1, START(N) do
cache[i] = derive(function()
return state() + state2() + state3() + state4()
return src() + src2() + src3() + src4()
end)
end
end)
BENCH("set derived value", function()
local state = vide.source(1)
local _derived = vide.derive(state)
TITLE "graphs"
WRAP_BENCH("update 1->1 graph", function()
local src = source(1)
local _derived = derive(function() return src() end)
for i = 1, START(N) do
state(i)
src(i)
end
end)
BENCH("apply 0 properties", function()
WRAP_BENCH("update 1->1 graph with cleanup", function()
local src = source(1)
derive(function()
cleanup(function() end)
return src()
end)
for i = 1, START(N) do
src(i)
end
end)
WRAP_BENCH("update 1->1000 graph", function()
local src = source(-1)
for i = 1, 1000 do
derive(function() return src() end)
end
src(0)
for i = 1, START(10) do
src(i)
end
end)
WRAP_BENCH("update 1->1->1->1...1000 graph", function()
local src = source(-1)
local last = src
for i = 1, 1000 do
local l = last
last = derive(function() return l() end)
end
src(0)
for i = 1, START(10) do
src(i)
end
end)
-- todo: repeat with batching
WRAP_BENCH("update 1000->1 graph", function()
local srcs = {}
for i = 1, 1000 do
srcs[i] = source(0)
end
derive(function()
for i = 1, 1000 do
srcs[i]()
end
return false
end)
for i = 1, START(1) do
for idx = 1, 1000 do
srcs[idx](i)
end
end
end)
-- todo: optimize, repeat with batching
WRAP_BENCH("update 1000x 1->1 common extern. graph", function()
local ext = source(-1)
local srcs = {}
for i = 1, 1000 do
srcs[i] = source(0)
derive(function() return srcs[i]() + ext() end)
end
ext(0)
for i = 1, START(10) do
for idx = 1, 1000 do
srcs[idx](i)
end
end
end)
TITLE "property apply"
WRAP_BENCH("apply 0 properties", function()
local apply = require "src/apply"
local instance = vide.create("Frame") {}
local instance = create("Frame") {}
for i = 1, START(N) do
apply(instance, {})
end
end)
BENCH("apply 8 properties", function()
WRAP_BENCH("apply 8 properties", function()
local apply = require "src/apply"
local instance = vide.create("Frame") {}
local instance = create("Frame") {}
for i = 1, START(N) do
apply(instance, {
Name = i,
Name2 = i,
Name3 = i,
Name4 = i,
Name5 = i,
Name6 = i,
Name7 = i,
Name8 = i,
Text = i,
Text2 = i,
Text3 = i,
Text4 = i,
Text5 = i,
Text6 = i,
Text7 = i,
Text8 = i,
})
end
end)
BENCH("bind source", function()
WRAP_BENCH("bind property", function()
local apply = require "src/apply"
local instance = vide.create("Frame") {}
local state = vide.source(1)
local instance = create("Frame") {}
local src = source(1)
for i = 1, START(N) do
apply(instance, {
Name = state
Text = src
})
end
return nil
end)
BENCH("update binding", function()
WRAP_BENCH("update binding", function()
local apply = require "src/apply"
local instance = vide.create("Frame") {}
local state = vide.source(1)
local instance = create("Frame") {}
local src = source(1)
apply(instance, {
Name = state
Text = src
})
for i = 1, START(N) do
state(i)
src(i)
end
return nil
end)
BENCH("indexes() no change", function()
TITLE "indexes()"
N /= 1024
WRAP_BENCH("indexes() all new", function()
local data = {}
for i = 1, N do
data[i] = i
end
local state = vide.source(data)
local _list = vide.indexes(state, function(v, i)
return {}
end)
--state(state()) -- fill double buffer
local src = source(data)
START(N)
state(data)
local _list = indexes(src, function(v, i)
return {}
end)
return nil
end)
BENCH("indexes() all change", function()
WRAP_BENCH("indexes() no change", function()
local data = {}
for i = 1, N do
data[i] = i
end
local state = vide.source(data)
local src = source(data)
local _list = vide.indexes(state, function(v, i)
local _list = indexes(src, function(v, i)
return {}
end)
--state(state()) -- fill double buffer
START(N)
src(data)
return nil
end)
WRAP_BENCH("indexes() all change", function()
local data = {}
for i = 1, N do
data[i] = i
end
local src = source(data)
local _list = indexes(src, function(v, i)
return {}
end)
--src(src()) -- fill double buffer
for i, v in data do
data[i] = v + 1
@ -169,19 +293,19 @@ BENCH("indexes() all change", function()
START(N)
state(data)
src(data)
end)
BENCH("indexes() all remove", function()
WRAP_BENCH("indexes() all remove", function()
local data = {}
for i = 1, N do
data[i] = i
end
local state = vide.source(data)
local src = source(data)
local _list = vide.indexes(state, function(v, i)
local _list = indexes(src, function(v, i)
return {}
end)
@ -189,43 +313,65 @@ BENCH("indexes() all remove", function()
START(N)
state(data)
src(data)
return nil
end)
BENCH("values() no change", function()
TITLE "values()"
WRAP_BENCH("values() all new", function()
local data = {}
for i = 1, N do
data[i] = {}
end
local state = vide.source(data)
local src = source(data)
local _list = vide.values(state, function(v, i)
START(N)
local _list = values(src, function(v, i)
return {}
end)
state(state()) -- fill double buffer
return nil
end)
WRAP_BENCH("values() no change", function()
local data = {}
for i = 1, N do
data[i] = {}
end
local src = source(data)
local _list = values(src, function(v, i)
return {}
end)
src(src()) -- fill double buffer
START(N)
state(data)
src(data)
end)
BENCH("values() all change", function()
WRAP_BENCH("values() all change", function()
local data = {}
for i = 1, N do
data[i] = {}
end
local state = vide.source(data)
local src = source(data)
local _list = vide.values(state, function(v, i)
local _list = values(src, function(v, i)
return {}
end)
state(state()) -- fill double buffer
src(src()) -- fill double buffer
for i = 1, N do
local r = math.random(1, #data)
@ -234,19 +380,19 @@ BENCH("values() all change", function()
START(N)
state(data)
src(data)
end)
BENCH("values() all remove", function()
WRAP_BENCH("values() all remove", function()
local data = {}
for i = 1, N do
data[i] = {}
end
local state = vide.source(data)
local src = source(data)
local _list = vide.values(state, function(v, i)
local _list = values(src, function(v, i)
return {}
end)
@ -254,11 +400,15 @@ BENCH("values() all remove", function()
START(N)
state(data)
src(data)
end)
BENCH("register new cleanup", function()
local cleanup = vide.cleanup
N *= 1024
TITLE "cleanup"
WRAP_BENCH("register new cleanup", function()
local cleanup = cleanup
local cleaner = function() end
@ -276,61 +426,12 @@ BENCH("register new cleanup", function()
end
end)
-- cleanup cleanups from previous benchmark
;(collectgarbage :: any)("collect")
vide.step(0)
BENCH("repeat cleanup", function()
local cleanup = vide.cleanup
local foo = NO_INLINE(function(i)
cleanup(function()
-- return i -- uncomment to include overhead of closure creation
end)
end)
for i = 1, START(N) do
foo(i)
end
end)
-- cleanup cleanups from previous benchmark
;(collectgarbage :: any)("collect")
vide.step(0)
BENCH("cleanup gc check", function()
local cleanup = vide.cleanup
local cleaner = function() end
local callers = table.create(N)
for i = 1, N do
callers[i] = function()
cleanup(cleaner)
return i
end
callers[i]()
end
START(N)
vide.step(0)
end)
BENCH("cleanup gc removal", function()
START(N)
vide.step(0) -- cleanup from previous benchmark
end)
TITLE "aggregate"
do
-- the purpose of the two following benchmarks is to measure the overhead of
-- aggregate construction
BENCH("set explicit mock vector2", function()
local create = vide.create
WRAP_BENCH("set explicit mock vector2", function()
local apply = require "src/apply"
local Vector2 = require "test/mock".Vector2
@ -345,8 +446,7 @@ do
end
end)
BENCH("set aggregate mock vector2", function()
local create = vide.create
WRAP_BENCH("set aggregate mock vector2", function()
local apply = require "src/apply"
local Vector2 = require "test/mock".Vector2
@ -362,4 +462,47 @@ do
end)
end
-- innacurate due to no Vector3 in vanilla Luau
-- mock vector is 200x slower than native vector
-- WRAP_BENCH("spring update", function()
-- local root, source, spring = vide.root, vide.source, vide.spring
-- local src = source(0)
-- root(function()
-- for i = 1, N do
-- spring(src)
-- end
-- START(N)
-- src(1)
-- return nil
-- end)
-- end)
-- N /= 1024
-- WRAP_BENCH("spring step", function()
-- local root, source, spring = vide.root, vide.source, vide.spring
-- local src = source(0)
-- root(function()
-- for i = 1, N do
-- spring(src)
-- end
-- src(1)
-- START(N)
-- vide.step(1/60)
-- return nil
-- end)
-- end)
return nil

View file

@ -113,6 +113,7 @@ local Instance = {} :: any do
local function __newindex(userdata: userdata, property: string, value: unknown)
local data = get_data(userdata)
if property == "Name" then
if type(value) ~= "string" then error("name must be a string", 2) end
data.name = value :: string
elseif property == "Parent" then
assert(value == nil or is_instance(value), "attempt to set non-instance as parent")

View file

@ -1,5 +1,6 @@
local function dir(directory: string)
return setmetatable({} :: { [string]: any }, { __index = function(_, path) return directory .. path end })
return setmetatable({} :: { [string]: any },
{ __index = function(_, path) return directory .. path end })
end
local script = dir "src/"

View file

@ -37,12 +37,12 @@ local function main()
local source = vide.source
local spring = vide.spring
local watch = vide.watch
local effect = vide.effect
local value = source(MAX)
local sprung = spring(value, 1, 0.3)
watch(function()
effect(function()
local v = sprung()
local fv = math.floor(v)
local reset = "\27[H\27[2J" -- ANSI clear terminal
@ -65,7 +65,7 @@ local function main()
until false
end
main()
vide.root(main)

File diff suppressed because it is too large Load diff