diff --git a/develop/basics.lua b/develop/basics.lua new file mode 100644 index 0000000..63a6daa --- /dev/null +++ b/develop/basics.lua @@ -0,0 +1,58 @@ +local basics = {} + +local __table_pack = (function() + return table.pack or function(...) + return { n = select('#', ...), ... } + end +end)() + +local __table_unpack = (function() + return table.unpack or unpack +end)() + +---@param name string +---@param loop fun(...): ... +---@param init? fun(): ... +function basics.describe_bench(name, loop, init) + collectgarbage('collect') + collectgarbage('stop') + + print(string.format('| %s ... |', name)) + + local iters = 0 + local state = init and __table_pack(init()) or {} + + pcall(function() + local warmup_s = os.clock() + repeat + loop(__table_unpack(state)) + until os.clock() - warmup_s > 0.1 + end) + + local start_s = os.clock() + local start_kb = collectgarbage('count') + + local success, result = pcall(function() + repeat + iters = iters + 1 + loop(__table_unpack(state)) + until os.clock() - start_s > 0.1 + end) + + local finish_s = os.clock() + local finish_kb = collectgarbage('count') + + print(string.format(' %s | us: %.2f | op/s: %.2f | kb/i: %.2f | iters: %d', + success and 'PASS' or 'FAIL', + (finish_s - start_s) * 1e6 / iters, + iters / (finish_s - start_s), + (finish_kb - start_kb) / iters, + iters)) + + if not success then print(' ' .. result) end + + collectgarbage('restart') + collectgarbage('collect') +end + +return basics diff --git a/develop/unbench.lua b/develop/unbench.lua index 1d2b8f5..27cac4d 100644 --- a/develop/unbench.lua +++ b/develop/unbench.lua @@ -1,157 +1,321 @@ package.loaded['evolved'] = nil local evo = require 'evolved' -local __table_pack = (function() - return table.pack or function(...) - return { n = select('#', ...), ... } - end -end)() +local basics = require 'develop.basics' -local __table_unpack = (function() - return table.unpack or unpack -end)() +local N = 1000 ----@param name string ----@param loop fun(...): ... ----@param init? fun(): ... -local function __bench_describe(name, loop, init) - collectgarbage('collect') - collectgarbage('stop') +print '----------------------------------------' - print(string.format('| %s ... |', name)) - - local iters = 0 - local state = init and __table_pack(init()) or {} - - local start_s = os.clock() - local start_kb = collectgarbage('count') - - local success, result = pcall(function() - repeat - iters = iters + 1 - loop(__table_unpack(state)) - until os.clock() - start_s > 0.2 +basics.describe_bench(string.format('create %d tables', N), + ---@param tables table[] + function(tables) + for i = 1, N do + local t = {} + tables[i] = t + end + end, function() + return {} end) - local finish_s = os.clock() - local finish_kb = collectgarbage('count') +basics.describe_bench(string.format('create and collect %d tables', N), + ---@param tables table[] + function(tables) + for i = 1, N do + local t = {} + tables[i] = t + end - print(string.format(' %s | us: %.2f | op/s: %.2f | kb/i: %.2f', - success and 'PASS' or 'FAIL', - (finish_s - start_s) * 1e6 / iters, - iters / (finish_s - start_s), - (finish_kb - start_kb) / iters)) + for i = 1, #tables do + tables[i] = nil + end - if not success then print(' ' .. result) end + collectgarbage('collect') + end, function() + return {} + end) - collectgarbage('restart') - collectgarbage('collect') -end +print '----------------------------------------' ----@param tables table[] -__bench_describe('create and destroy 1k tables', function(tables) - for i = 1, 1000 do - local t = {} - tables[i] = t - end +basics.describe_bench(string.format('create %d tables with 1 component / AoS', N), + ---@param f1 table + ---@param tables table + function(f1, tables) + for i = 1, N do + local e = {} + e[f1] = true + tables[i] = e + end + end, function() + local f1 = {} + return f1, {} + end) - for i = 1, #tables do - tables[i] = nil - end +basics.describe_bench(string.format('create %d tables with 2 component / AoS', N), + ---@param f1 table + ---@param f2 table + ---@param tables table + function(f1, f2, tables) + for i = 1, N do + local e = {} + e[f1] = true + e[f2] = true + tables[i] = e + end + end, function() + local f1, f2 = {}, {} + return f1, f2, {} + end) - collectgarbage('collect') -end, function() - return {} -end) +basics.describe_bench(string.format('create %d tables with 3 component / AoS', N), + ---@param f1 table + ---@param f2 table + ---@param f3 table + ---@param tables table + function(f1, f2, f3, tables) + for i = 1, N do + local e = {} + e[f1] = true + e[f2] = true + e[f3] = true + tables[i] = e + end + end, function() + local f1, f2, f3 = {}, {}, {} + return f1, f2, f3, {} + end) ----@param entities evolved.id[] -__bench_describe('create and destroy 1k entities', function(entities) - local id = evo.id - local destroy = evo.destroy +basics.describe_bench(string.format('create %d tables with 4 component / AoS', N), + ---@param f1 table + ---@param f2 table + ---@param f3 table + ---@param f4 table + ---@param tables table + function(f1, f2, f3, f4, tables) + for i = 1, N do + local e = {} + e[f1] = true + e[f2] = true + e[f3] = true + e[f4] = true + tables[i] = e + end + end, function() + local f1, f2, f3, f4 = {}, {}, {}, {} + return f1, f2, f3, f4, {} + end) - for i = 1, 1000 do - local e = id() - entities[i] = e - end +print '----------------------------------------' - for i = 1, #entities do - destroy(entities[i]) - end -end, function() - return {} -end) +basics.describe_bench(string.format('create %d tables with 1 component / SoA', N), + ---@param f1 table + ---@param tables table + function(f1, tables) + local fs1 = {} + for i = 1, N do + local e = {} + fs1[i] = true + tables[i] = e + end + tables[f1] = fs1 + end, function() + local f1 = {} + return f1, {} + end) ----@param f1 evolved.fragment ----@param entities evolved.id[] -__bench_describe('create and destroy 1k entities with one component', function(f1, entities) - local id = evo.id - local insert = evo.insert - local destroy = evo.destroy +basics.describe_bench(string.format('create %d tables with 2 component / SoA', N), + ---@param f1 table + ---@param f2 table + ---@param tables table + function(f1, f2, tables) + local fs1 = {} + local fs2 = {} + for i = 1, N do + local e = {} + fs1[i] = true + fs2[i] = true + tables[i] = e + end + tables[f1] = fs1 + tables[f2] = fs2 + end, function() + local f1, f2 = {}, {} + return f1, f2, {} + end) - for i = 1, 1000 do - local e = id() - entities[i] = e +basics.describe_bench(string.format('create %d tables with 3 component / SoA', N), + ---@param f1 table + ---@param f2 table + ---@param f3 table + ---@param tables table + function(f1, f2, f3, tables) + local fs1 = {} + local fs2 = {} + local fs3 = {} + for i = 1, N do + local e = {} + fs1[i] = true + fs2[i] = true + fs3[i] = true + tables[i] = e + end + tables[f1] = fs1 + tables[f2] = fs2 + tables[f3] = fs3 + end, function() + local f1, f2, f3 = {}, {}, {} + return f1, f2, f3, {} + end) - insert(e, f1) - end +basics.describe_bench(string.format('create %d tables with 4 component / SoA', N), + ---@param f1 table + ---@param f2 table + ---@param f3 table + ---@param f4 table + ---@param tables table + function(f1, f2, f3, f4, tables) + local fs1 = {} + local fs2 = {} + local fs3 = {} + local fs4 = {} + for i = 1, N do + local e = {} + fs1[i] = i + fs2[i] = i + fs3[i] = i + fs4[i] = i + tables[i] = e + end + tables[f1] = fs1 + tables[f2] = fs2 + tables[f3] = fs3 + tables[f4] = fs4 + end, function() + local f1, f2, f3, f4 = {}, {}, {}, {} + return f1, f2, f3, f4, {} + end) - for i = 1, #entities do - destroy(entities[i]) - end -end, function() - local f1 = evo.id(2) - return f1, {} -end) +print '----------------------------------------' ----@param f1 evolved.fragment ----@param f2 evolved.fragment ----@param entities evolved.id[] -__bench_describe('create and destroy 1k entities with two components', function(f1, f2, entities) - local id = evo.id - local insert = evo.insert - local destroy = evo.destroy +basics.describe_bench(string.format('create and destroy %d entities', N), + ---@param entities evolved.id[] + function(entities) + local id = evo.id + local destroy = evo.destroy - for i = 1, 1000 do - local e = id() - entities[i] = e + for i = 1, N do + local e = id() + entities[i] = e + end - insert(e, f1) - insert(e, f2) - end + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + return {} + end) - for i = 1, #entities do - destroy(entities[i]) - end -end, function() - local f1, f2 = evo.id(2) - return f1, f2, {} -end) +basics.describe_bench(string.format('create and destroy %d entities with 1 component', N), + ---@param f1 evolved.fragment + ---@param entities evolved.id[] + function(f1, entities) + local id = evo.id + local insert = evo.insert + local destroy = evo.destroy ----@param f1 evolved.fragment ----@param f2 evolved.fragment ----@param f3 evolved.fragment ----@param entities evolved.id[] -__bench_describe('create and destroy 1k entities with three components', function(f1, f2, f3, entities) - local id = evo.id - local insert = evo.insert - local destroy = evo.destroy + for i = 1, N do + local e = id() + insert(e, f1) + entities[i] = e + end - for i = 1, 1000 do - local e = id() - entities[i] = e + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + local f1 = evo.id(2) + return f1, {} + end) - insert(e, f1) - insert(e, f2) - insert(e, f3) - end +basics.describe_bench(string.format('create and destroy %d entities with 2 components', N), + ---@param f1 evolved.fragment + ---@param f2 evolved.fragment + ---@param entities evolved.id[] + function(f1, f2, entities) + local id = evo.id + local insert = evo.insert + local destroy = evo.destroy - for i = 1, #entities do - destroy(entities[i]) - end -end, function() - local f1, f2, f3 = evo.id(3) - return f1, f2, f3, {} -end) + for i = 1, N do + local e = id() + insert(e, f1) + insert(e, f2) + entities[i] = e + end + + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + local f1, f2 = evo.id(2) + return f1, f2, {} + end) + +basics.describe_bench(string.format('create and destroy %d entities with 3 components', N), + ---@param f1 evolved.fragment + ---@param f2 evolved.fragment + ---@param f3 evolved.fragment + ---@param entities evolved.id[] + function(f1, f2, f3, entities) + local id = evo.id + local insert = evo.insert + local destroy = evo.destroy + + for i = 1, N do + local e = id() + insert(e, f1) + insert(e, f2) + insert(e, f3) + entities[i] = e + end + + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + local f1, f2, f3 = evo.id(3) + return f1, f2, f3, {} + end) + +basics.describe_bench(string.format('create and destroy %d entities with 4 components', N), + ---@param f1 evolved.fragment + ---@param f2 evolved.fragment + ---@param f3 evolved.fragment + ---@param f4 evolved.fragment + ---@param entities evolved.id[] + function(f1, f2, f3, f4, entities) + local id = evo.id + local insert = evo.insert + local destroy = evo.destroy + + for i = 1, N do + local e = id() + insert(e, f1) + insert(e, f2) + insert(e, f3) + insert(e, f4) + entities[i] = e + end + + for i = 1, #entities do + destroy(entities[i]) + end + end, function() + local f1, f2, f3, f4 = evo.id(4) + return f1, f2, f3, f4, {} + end) + +print '----------------------------------------' --- --- initial @@ -160,22 +324,22 @@ end) --[[ lua 5.1 | create and destroy 1k entities ... | PASS | us: 312.60 | op/s: 3199.00 | kb/i: 0.05 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 1570.31 | op/s: 636.82 | kb/i: 0.63 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 2780.82 | op/s: 359.61 | kb/i: 0.91 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 4060.00 | op/s: 246.31 | kb/i: 1.67 ]] --[[ luajit 2.1 | create and destroy 1k entities ... | PASS | us: 12.22 | op/s: 81840.80 | kb/i: 0.00 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 56.22 | op/s: 17786.07 | kb/i: 0.02 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 412.73 | op/s: 2422.89 | kb/i: 0.11 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 611.62 | op/s: 1635.00 | kb/i: 0.17 ]] @@ -186,22 +350,22 @@ end) --[[ lua 5.1 | create and destroy 1k entities ... | PASS | us: 255.40 | op/s: 3915.42 | kb/i: 0.04 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 1248.45 | op/s: 801.00 | kb/i: 0.50 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 2208.79 | op/s: 452.74 | kb/i: 0.73 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 3278.69 | op/s: 305.00 | kb/i: 1.37 ]] --[[ luajit 2.1 | create and destroy 1k entities ... | PASS | us: 12.12 | op/s: 82482.59 | kb/i: 0.00 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 69.05 | op/s: 14482.59 | kb/i: 0.03 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 400.40 | op/s: 2497.51 | kb/i: 0.09 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 574.71 | op/s: 1740.00 | kb/i: 0.14 ]] @@ -212,22 +376,22 @@ end) --[[ lua 5.1 | create and destroy 1k entities ... | PASS | us: 255.40 | op/s: 3915.42 | kb/i: 0.04 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 1005.03 | op/s: 995.00 | kb/i: 0.41 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 1747.83 | op/s: 572.14 | kb/i: 0.59 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 2576.92 | op/s: 388.06 | kb/i: 1.08 ]] --[[ luajit 2.1 | create and destroy 1k entities ... | PASS | us: 12.20 | op/s: 81940.30 | kb/i: 0.00 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 53.66 | op/s: 18636.82 | kb/i: 0.02 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 357.02 | op/s: 2801.00 | kb/i: 0.09 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 533.33 | op/s: 1875.00 | kb/i: 0.15 ]] @@ -238,22 +402,22 @@ end) --[[ lua 5.1 | create and destroy 1k entities ... | PASS | us: 253.49 | op/s: 3945.00 | kb/i: 0.04 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 913.64 | op/s: 1094.53 | kb/i: 0.37 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 1562.50 | op/s: 640.00 | kb/i: 0.53 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 2280.90 | op/s: 438.42 | kb/i: 0.97 ]] --[[ luajit 2.1 | create and destroy 1k entities ... | PASS | us: 12.05 | op/s: 82995.02 | kb/i: 0.00 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 53.61 | op/s: 18651.74 | kb/i: 0.02 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 232.02 | op/s: 4310.00 | kb/i: 0.06 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 329.49 | op/s: 3035.00 | kb/i: 0.10 ]] @@ -264,21 +428,21 @@ end) --[[ lua 5.1 | create and destroy 1k entities ... | PASS | us: 254.45 | op/s: 3930.00 | kb/i: 0.04 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 897.32 | op/s: 1114.43 | kb/i: 0.36 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 1481.48 | op/s: 675.00 | kb/i: 0.49 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 2126.32 | op/s: 470.30 | kb/i: 0.90 ]] --[[ luajit 2.1 | create and destroy 1k entities ... | PASS | us: 12.31 | op/s: 81248.76 | kb/i: 0.00 -| create and destroy 1k entities with one component ... | +| create and destroy 1k entities with 1 component ... | PASS | us: 46.97 | op/s: 21288.56 | kb/i: 0.02 -| create and destroy 1k entities with two components ... | +| create and destroy 1k entities with 2 components ... | PASS | us: 75.19 | op/s: 13300.00 | kb/i: 0.03 -| create and destroy 1k entities with three components ... | +| create and destroy 1k entities with 3 components ... | PASS | us: 108.28 | op/s: 9235.00 | kb/i: 0.06 ]]