Files
plainleaf/bench/lua_perf.bench.ts

1082 lines
29 KiB
TypeScript

// Focused micro-benchmarks for Space Lua interpreter performance.
// Benchmarks are weighted toward the real SilverBullet workload:
// ~40% API/syscall patterns (LuaNativeJSFunction, LuaBuiltinFunction)
// ~30% data structure traversal (tables, linked structures, iteration)
// ~15% string manipulation (format, find, split, concat, interpolation)
// ~15% function calls, closures, control flow
//
// Each snippet is pre-parsed to exclude parsing cost from measurements.
// All benchmarks create fresh Lua environments for isolation.
import { bench } from "vitest";
import { evalStatement } from "../client/space_lua/eval.ts";
import {
LuaBuiltinFunction,
LuaEnv,
LuaNativeJSFunction,
LuaStackFrame,
LuaTable,
luaTypeOf,
} from "../client/space_lua/runtime.ts";
import { parse as parseLua } from "../client/space_lua/parse.ts";
import { luaBuildStandardEnv } from "../client/space_lua/stdlib.ts";
const LOOP = 100_000;
const SMALL = 10_000;
// --- Helpers ---
function makeMinimalEnv(): { global: LuaEnv; sf: LuaStackFrame } {
const global = new LuaEnv();
global.setLocal("type", new LuaBuiltinFunction((_sf, v) => luaTypeOf(v)));
const sf = LuaStackFrame.createWithGlobalEnv(global);
return { global, sf };
}
function makeStdEnv(): { global: LuaEnv; sf: LuaStackFrame } {
const G = luaBuildStandardEnv();
const env = new LuaEnv(G);
const sf = LuaStackFrame.createWithGlobalEnv(env);
return { global: env, sf };
}
async function runMinimal(ast: any) {
const { global, sf } = makeMinimalEnv();
const r = evalStatement(ast, global, sf, false);
if (r instanceof Promise) await r;
}
async function runStd(ast: any) {
const { global, sf } = makeStdEnv();
const r = evalStatement(ast, global, sf, false);
if (r instanceof Promise) await r;
}
// Environment with sync/async API stubs simulating real syscall patterns
function makeApiEnv(): { global: LuaEnv; sf: LuaStackFrame } {
const G = luaBuildStandardEnv();
const env = new LuaEnv(G);
// Simulate sync API call (like space.readPage returning cached data)
env.setLocal(
"syncGet",
new LuaBuiltinFunction((_sf, key) => `value_for_${key}`),
);
// Simulate async API call (like space.readPage hitting storage)
env.setLocal(
"asyncGet",
new LuaNativeJSFunction((key: string) =>
Promise.resolve(`value_for_${key}`),
),
);
// Simulate sync API returning a table (like space.listPages)
env.setLocal(
"syncGetRecord",
new LuaBuiltinFunction((_sf, id: number) => {
const t = new LuaTable();
void t.set("id", id);
void t.set("name", `item_${id}`);
void t.set(
"tags",
(() => {
const tags = new LuaTable();
void tags.set(1, "tag_a");
void tags.set(2, "tag_b");
return tags;
})(),
);
return t;
}),
);
// Simulate async API returning a table
env.setLocal(
"asyncGetRecord",
new LuaNativeJSFunction((id: number) =>
Promise.resolve({ id, name: `item_${id}`, tags: ["tag_a", "tag_b"] }),
),
);
// Simulate sync predicate API (like checking config)
env.setLocal(
"syncCheck",
new LuaBuiltinFunction((_sf, val) => val !== null && val !== undefined),
);
// Simulate async write API (like space.writePage)
env.setLocal(
"asyncSet",
new LuaNativeJSFunction((_key: string, _value: string) =>
Promise.resolve(true),
),
);
// Simulate async page read returning multi-line content with dates
env.setLocal(
"asyncGetPage",
new LuaNativeJSFunction((id: number) =>
Promise.resolve(
`2024-03-${String((id % 28) + 1).padStart(2, "0")} Task alpha for page ${id}\n` +
`2023-12-${String((id % 28) + 1).padStart(2, "0")} Task beta for page ${id}\n` +
`2024-01-${String((id % 28) + 1).padStart(2, "0")} Task gamma for page ${id}\n`,
),
),
);
const sf = LuaStackFrame.createWithGlobalEnv(env);
return { global: env, sf };
}
async function runApi(ast: any) {
const { global, sf } = makeApiEnv();
const r = evalStatement(ast, global, sf, false);
if (r instanceof Promise) await r;
}
// =====================================================
// 1. API Call Patterns (sync and async)
// The primary workload in SilverBullet: Lua scripts
// call into TypeScript APIs via LuaNativeJSFunction
// and LuaBuiltinFunction.
// =====================================================
const API_LOOP = 1_000;
const luaSyncApiCalls = `
local results = {}
for i = 1, ${API_LOOP} do
results[i] = syncGet("key_" .. i)
end
`;
const luaAsyncApiCalls = `
local results = {}
for i = 1, ${API_LOOP} do
results[i] = asyncGet("key_" .. i)
end
`;
const luaMixedApiCalls = `
local results = {}
for i = 1, ${API_LOOP} do
if i % 10 == 0 then
results[i] = asyncGet("key_" .. i)
else
results[i] = syncGet("key_" .. i)
end
end
`;
// Simulate query-like pattern: fetch record, filter, collect
const luaApiFilterCollect = `
local results = {}
local count = 0
for i = 1, ${API_LOOP} do
local rec = syncGetRecord(i)
if rec.id % 3 == 0 then
count = count + 1
results[count] = rec
end
end
`;
// Async record fetch with field access and filtering
const luaAsyncApiFilterCollect = `
local results = {}
local count = 0
for i = 1, ${API_LOOP} do
local rec = asyncGetRecord(i)
if syncCheck(rec.name) then
count = count + 1
results[count] = rec
end
end
`;
// Chained async calls (read then write pattern)
const luaAsyncChainedCalls = `
for i = 1, ${API_LOOP} do
local val = asyncGet("key_" .. i)
asyncSet("out_" .. i, val .. "_processed")
end
`;
// Sync API with table construction (building query results)
const luaSyncApiTableBuild = `
local results = {}
for i = 1, ${API_LOOP} do
local name = syncGet("name_" .. i)
local status = syncGet("status_" .. i)
results[i] = { name = name, status = status, index = i }
end
`;
// =====================================================
// 2. Data Structure Traversal
// Tables are the core data type in Space Lua.
// Queries produce tables, configs are tables,
// page metadata is tables.
// =====================================================
const luaTableConstructor = `
local tables = {}
for i = 1, ${SMALL} do
tables[i] = { x = i, y = i * 2, z = "tag_" .. i }
end
`;
const luaTableInsertLoop = `
local t = {}
for i = 1, ${LOOP} do
t[#t + 1] = i
end
`;
const luaIpairsIteration = `
local t = {}
for i = 1, 1000 do t[i] = i end
local s = 0
for round = 1, 100 do
for _, v in ipairs(t) do
s = s + v
end
end
`;
const luaPairsIteration = `
local t = {}
for i = 1, 100 do t["key" .. i] = i end
local s = 0
for round = 1, 1000 do
for k, v in pairs(t) do
s = s + v
end
end
`;
// Nested table access (common in page metadata traversal)
const luaNestedTableAccess = `
local pages = {}
for i = 1, 200 do
pages[i] = {
name = "page_" .. i,
meta = { tags = { "tag1", "tag2" }, priority = i % 5 }
}
end
local count = 0
for _, page in ipairs(pages) do
if page.meta.priority > 2 then
for _, tag in ipairs(page.meta.tags) do
count = count + 1
end
end
end
`;
// Table used as a map: insert, lookup, delete
const luaTableAsMap = `
local map = {}
for i = 1, 5000 do
map["key_" .. i] = "value_" .. i
end
local found = 0
for i = 1, 5000 do
if map["key_" .. i] ~= nil then
found = found + 1
end
end
for i = 1, 5000, 2 do
map["key_" .. i] = nil
end
`;
// Linked list traversal (common pattern for tree/graph data)
const luaLinkedList = `
local head = nil
for i = 1000, 1, -1 do
head = { value = i, next = head }
end
local total = 0
for round = 1, 100 do
local node = head
while node do
total = total + node.value
node = node.next
end
end
`;
// table.sort with custom comparator (query ORDER BY)
const luaTableSort = `
local items = {}
for i = 1, 1000 do
items[i] = { name = "item_" .. (1000 - i), priority = i % 7 }
end
for round = 1, 20 do
table.sort(items, function(a, b)
if a.priority == b.priority then
return a.name < b.name
end
return a.priority < b.priority
end)
end
`;
// table.concat (building output strings)
const luaTableConcat = `
local parts = {}
for i = 1, 1000 do
parts[i] = "segment_" .. i
end
local result
for round = 1, 100 do
result = table.concat(parts, ", ")
end
`;
// =====================================================
// 3. String Manipulation
// Template interpolation, string matching, and
// building output are core SilverBullet patterns.
// =====================================================
const luaStringFormat = `
local s
for i = 1, ${SMALL} do
s = string.format("hello %s world %d", "test", i)
end
`;
const luaStringFind = `
local text = "the quick brown fox jumps over the lazy dog"
local count = 0
for i = 1, ${SMALL} do
if string.find(text, "fox") then
count = count + 1
end
end
`;
const luaStringSub = `
local text = "abcdefghijklmnopqrstuvwxyz"
local s
for i = 1, ${LOOP} do
s = string.sub(text, 5, 15)
end
`;
// String concatenation building (template-like patterns)
const luaStringConcat = `
local result = ""
for i = 1, 1000 do
result = result .. "item " .. i .. ", "
end
`;
// String split + rejoin (common in tag processing)
const luaStringSplitProcess = `
local input = "tag1,tag2,tag3,tag4,tag5"
local results = {}
for round = 1, 2000 do
local parts = string.split(input, ",")
local upper = {}
for i, v in ipairs(parts) do
upper[i] = string.upper(v)
end
results[round] = table.concat(upper, ";")
end
`;
// String matching (regex-like patterns in content processing)
const luaStringGsub = `
local text = "Hello [world](link1) and [foo](link2) and [bar](link3)"
local result
for i = 1, 5000 do
result = string.gsub(text, "%[(.-)%]%((.-)%)", "%1")
end
`;
// =====================================================
// 4. Function Calls, Closures, Control Flow
// Function call overhead matters because every API
// call, iterator, and callback goes through luaCall.
// =====================================================
const luaFibonacci = `
local function fib(n)
if n < 2 then return n end
return fib(n - 1) + fib(n - 2)
end
local r = fib(20)
`;
const luaClosureCreateCall = `
local s = 0
for i = 1, ${SMALL} do
local function add(x) return x + i end
s = s + add(i)
end
`;
const luaMethodCall = `
local obj = {}
obj.value = 0
function obj:inc(n)
self.value = self.value + n
end
for i = 1, ${LOOP} do
obj:inc(1)
end
`;
const luaVarargs = `
local function sum(...)
local args = {...}
local s = 0
for i = 1, #args do
s = s + args[i]
end
return s
end
local total = 0
for i = 1, ${SMALL} do
total = total + sum(1, 2, 3, 4, 5)
end
`;
// Higher-order functions (filter/map pattern used in queries)
const luaHigherOrderFunctions = `
local function filter(t, pred)
local result = {}
local n = 0
for _, v in ipairs(t) do
if pred(v) then
n = n + 1
result[n] = v
end
end
return result
end
local function map(t, fn)
local result = {}
for i, v in ipairs(t) do
result[i] = fn(v)
end
return result
end
local data = {}
for i = 1, 1000 do data[i] = i end
for round = 1, 50 do
local evens = filter(data, function(x) return x % 2 == 0 end)
local doubled = map(evens, function(x) return x * 2 end)
end
`;
// =====================================================
// 5. Realistic Mixed Workloads
// Patterns that combine multiple operations the way
// real SilverBullet Lua code does.
// =====================================================
// Simulate query: fetch pages, filter by tag, sort, build result
const luaQuerySimulation = `
local pages = {}
for i = 1, 500 do
pages[i] = {
name = "page_" .. i,
tags = { "tag_" .. (i % 5) },
modified = 1000000 - i,
}
end
-- filter: only tag_1 and tag_3
local filtered = {}
local n = 0
for _, p in ipairs(pages) do
local tag = p.tags[1]
if tag == "tag_1" or tag == "tag_3" then
n = n + 1
filtered[n] = p
end
end
-- sort by modified desc
table.sort(filtered, function(a, b)
return a.modified > b.modified
end)
-- build output table
local result = {}
for i, p in ipairs(filtered) do
result[i] = { name = p.name, tag = p.tags[1] }
end
`;
// Simulate config/metadata processing
const luaConfigProcessing = `
local config = {
theme = "dark",
plugins = { "markdown", "lua", "query", "template" },
shortcuts = {},
}
for i = 1, 100 do
config.shortcuts["ctrl_" .. i] = "action_" .. i
end
-- Process: iterate config, build summary string
local parts = {}
local n = 0
for round = 1, 100 do
n = 0
for k, v in pairs(config.shortcuts) do
n = n + 1
parts[n] = k .. "=" .. v
end
end
local summary = table.concat(parts, "; ")
`;
// Simulate template rendering: string building from structured data
const luaTemplateRendering = `
local items = {}
for i = 1, 200 do
items[i] = { title = "Item " .. i, done = i % 3 == 0, priority = i % 5 }
end
local output = {}
for round = 1, 20 do
local n = 0
for _, item in ipairs(items) do
n = n + 1
local status = item.done and "x" or " "
local prio = ""
if item.priority > 3 then prio = " (!)" end
output[n] = string.format("- [%s] %s%s", status, item.title, prio)
end
end
local result = table.concat(output, "; ")
`;
// =====================================================
// 6. Async Patterns
// Async API calls mixed with sync table/string work.
// =====================================================
// Async loop with sync re-entry (the key optimization from Phase 3)
const luaAsyncLoopReentry = `
local results = {}
for i = 1, ${API_LOOP} do
local val = asyncGet("key_" .. i)
-- sync work between async calls
results[i] = string.upper(val) .. "_done"
end
`;
// While loop with async body
const luaAsyncWhileLoop = `
local i = 0
local results = {}
while i < ${API_LOOP} do
i = i + 1
results[i] = asyncGet("item_" .. i)
end
`;
// Async __index metamethod (lazy loading pattern)
const luaAsyncMetamethod = `
local mt = {}
mt.__index = function(t, k)
return asyncGet(k)
end
local proxy = setmetatable({}, mt)
local results = {}
for i = 1, ${API_LOOP} do
results[i] = proxy["field_" .. i]
end
`;
// Async record fetch + sync processing (realistic query pattern)
const luaAsyncQueryPattern = `
local results = {}
local count = 0
for i = 1, ${API_LOOP} do
local rec = asyncGetRecord(i)
-- sync filtering and string work
if syncCheck(rec) then
count = count + 1
results[count] = { id = i, label = "item_" .. i }
end
end
`;
// =====================================================
// 7. Community-Driven Gap Coverage
// Benchmarks derived from analyzing 163 community
// Lua scripts (~40K lines) to cover patterns that
// are heavily used but previously unbenchmarked.
// =====================================================
// --- pcall overhead (51 occurrences in community scripts, 0 benchmarks) ---
const luaPcallSuccess = `
local function safe_read(key)
return syncGet(key)
end
local results = {}
for i = 1, 1000 do
local ok, val = pcall(safe_read, "key_" .. i)
if ok then results[i] = val end
end
`;
const luaPcallError = `
local function failing_read(key)
error("not found: " .. key)
end
local errors = 0
for i = 1, 1000 do
local ok, msg = pcall(failing_read, "key_" .. i)
if not ok then errors = errors + 1 end
end
`;
const luaPcallAsyncApi = `
local results = {}
local errors = 0
for i = 1, 1000 do
local ok, val = pcall(asyncGet, "key_" .. i)
if ok then
results[#results + 1] = val
else
errors = errors + 1
end
end
`;
// --- string.match / string.gmatch (595+ occurrences, only gsub was benchmarked) ---
const luaStringMatchDate = `
local text = "Due: 2024-03-15, Created: 2023-12-01, Modified: 2024-01-30"
local results = {}
for i = 1, 5000 do
local y, m, d = string.match(text, "(%d%d%d%d)%-(%d%d)%-(%d%d)")
results[1] = y
end
`;
const luaStringGmatchMulti = `
local text = "2024-03-15 task1\\n2024-03-16 task2\\n2024-03-17 task3\\n2024-03-18 task4\\n2024-03-19 task5"
local results = {}
for round = 1, 2000 do
local n = 0
for date, task in string.gmatch(text, "(%d+%-%d+%-%d+) ([^\\n]+)") do
n = n + 1
results[n] = date
end
end
`;
const luaStringMatchComplex = `
local text = "[[Page Link]] and #tag and [markdown](http://url.com)"
local count = 0
for i = 1, 2000 do
if string.match(text, "%[%[(.-)%]%]") then count = count + 1 end
if string.match(text, "#(%w+)") then count = count + 1 end
if string.match(text, "%[(.-)%]%((.-)%)") then count = count + 1 end
end
`;
// --- String building: table.insert + table.concat (THE idiomatic output pattern) ---
const luaTableInsertConcat = `
for round = 1, 50 do
local parts = {}
for i = 1, 1000 do
table.insert(parts, "- item " .. i .. ": " .. string.format("value_%d", i))
end
local output = table.concat(parts, "\\n")
end
`;
// --- SilverBullet string extensions (595+ for split/trim, thin JS wrappers) ---
const luaStringSplitHot = `
local input = "one/two/three/four/five/six/seven/eight"
local count = 0
for i = 1, 5000 do
local parts = string.split(input, "/")
count = count + #parts
end
`;
const luaStringTrimStartsEnds = `
local inputs = { " hello ", " world ", " test ", " foo ", " bar " }
local count = 0
for i = 1, 5000 do
for _, s in ipairs(inputs) do
local trimmed = string.trim(s)
if string.startsWith(trimmed, "h") then count = count + 1 end
if string.endsWith(trimmed, "d") then count = count + 1 end
end
end
`;
// --- type() checking (723+ occurrences, most-called builtin, 0 benchmarks) ---
const luaTypeChecking = `
local values = { 1, "hello", true, nil, {}, 3.14 }
local counts = { number = 0, string = 0, boolean = 0, table = 0 }
for round = 1, 20000 do
for _, v in ipairs(values) do
local t = type(v)
if t == "number" then counts.number = counts.number + 1
elseif t == "string" then counts.string = counts.string + 1
end
end
end
`;
// --- os.time / os.date (167+ occurrences, no perf benchmark) ---
const luaOsDateFormat = `
local results = {}
for i = 1, 2000 do
local t = os.time()
results[i] = os.date("%Y-%m-%d", t)
end
`;
const luaOsDateTable = `
local base = os.time()
local results = {}
for i = 1, 2000 do
local dt = os.date("*t", base + i * 86400)
results[i] = dt.year .. "-" .. dt.month .. "-" .. dt.day
end
`;
// --- tostring/tonumber conversions ---
const luaTypeConversions = `
local results = {}
for i = 1, ${SMALL} do
local s = tostring(i)
local n = tonumber(s)
results[i] = s
end
`;
// --- Deep nested table access 5 levels (community scripts access config 3-5 deep) ---
const luaDeepNestedAccess = `
local root = {
app = {
config = {
editor = {
theme = { name = "dark", fontSize = 14 }
}
}
}
}
local count = 0
for i = 1, ${SMALL} do
local name = root.app.config.editor.theme.name
root.app.config.editor.theme.fontSize = 14 + (i % 10)
if name == "dark" then count = count + 1 end
end
`;
// --- Closure callbacks with captured upvalues (simulates command.define/event.listen) ---
const luaClosureCallbacks = `
local handlers = {}
for i = 1, 1000 do
local prefix = "handler_" .. i
local config = { enabled = i % 2 == 0, priority = i % 5 }
handlers[i] = function(input)
if config.enabled then
return prefix .. ": " .. input .. " (p=" .. config.priority .. ")"
end
return nil
end
end
local results = {}
local n = 0
for _, h in ipairs(handlers) do
local r = h("test_event")
if r then
n = n + 1
results[n] = r
end
end
`;
// --- End-to-end realistic script: read+parse+transform+build ---
const luaRealisticReadParseTransform = `
local pages = {}
for i = 1, 50 do
pages[i] = asyncGetPage(i)
end
local tasks = {}
local n = 0
for _, content in ipairs(pages) do
for date, text in string.gmatch(content, "(%d%d%d%d%-%d%d%-%d%d) ([^\\n]+)") do
n = n + 1
tasks[n] = { date = date, text = string.trim(text), done = false }
end
end
local filtered = {}
local fn = 0
for _, t in ipairs(tasks) do
if string.startsWith(t.date, "2024") then
fn = fn + 1
filtered[fn] = t
end
end
table.sort(filtered, function(a, b) return a.date > b.date end)
local out = {}
for i, t in ipairs(filtered) do
out[i] = string.format("<li>%s: %s</li>", t.date, t.text)
end
local html = "<ul>" .. table.concat(out, "") .. "</ul>"
`;
// --- End-to-end realistic script: config + command callbacks ---
const luaRealisticConfigCallbacks = `
local defaults = { theme = "light", fontSize = 14, showLines = true }
local cfg = syncGetRecord(1)
for k, v in pairs(defaults) do
if cfg[k] == nil then cfg[k] = v end
end
local commands = {}
for i = 1, 100 do
local name = "cmd_" .. i
local captured_cfg = cfg
commands[i] = function()
return string.format("Running %s with theme %s", name, tostring(captured_cfg.theme))
end
end
local results = {}
for i, cmd in ipairs(commands) do
results[i] = cmd()
end
local output = table.concat(results, "\\n")
`;
// =====================================================
// Pre-parse all snippets
// =====================================================
const asts = {
// API calls
syncApiCalls: parseLua(luaSyncApiCalls),
asyncApiCalls: parseLua(luaAsyncApiCalls),
mixedApiCalls: parseLua(luaMixedApiCalls),
apiFilterCollect: parseLua(luaApiFilterCollect),
asyncApiFilterCollect: parseLua(luaAsyncApiFilterCollect),
asyncChainedCalls: parseLua(luaAsyncChainedCalls),
syncApiTableBuild: parseLua(luaSyncApiTableBuild),
// Data structures
tableConstructor: parseLua(luaTableConstructor),
tableInsertLoop: parseLua(luaTableInsertLoop),
ipairsIteration: parseLua(luaIpairsIteration),
pairsIteration: parseLua(luaPairsIteration),
nestedTableAccess: parseLua(luaNestedTableAccess),
tableAsMap: parseLua(luaTableAsMap),
linkedList: parseLua(luaLinkedList),
tableSort: parseLua(luaTableSort),
tableConcat: parseLua(luaTableConcat),
// Strings
stringFormat: parseLua(luaStringFormat),
stringFind: parseLua(luaStringFind),
stringSub: parseLua(luaStringSub),
stringConcat: parseLua(luaStringConcat),
stringSplitProcess: parseLua(luaStringSplitProcess),
stringGsub: parseLua(luaStringGsub),
// Functions / control flow
fibonacci: parseLua(luaFibonacci),
closureCreateCall: parseLua(luaClosureCreateCall),
methodCall: parseLua(luaMethodCall),
varargs: parseLua(luaVarargs),
higherOrderFunctions: parseLua(luaHigherOrderFunctions),
// Realistic workloads
querySimulation: parseLua(luaQuerySimulation),
configProcessing: parseLua(luaConfigProcessing),
templateRendering: parseLua(luaTemplateRendering),
// Async patterns
asyncLoopReentry: parseLua(luaAsyncLoopReentry),
asyncWhileLoop: parseLua(luaAsyncWhileLoop),
asyncMetamethod: parseLua(luaAsyncMetamethod),
asyncQueryPattern: parseLua(luaAsyncQueryPattern),
// Community-driven gap coverage
pcallSuccess: parseLua(luaPcallSuccess),
pcallError: parseLua(luaPcallError),
pcallAsyncApi: parseLua(luaPcallAsyncApi),
stringMatchDate: parseLua(luaStringMatchDate),
stringGmatchMulti: parseLua(luaStringGmatchMulti),
stringMatchComplex: parseLua(luaStringMatchComplex),
tableInsertConcat: parseLua(luaTableInsertConcat),
stringSplitHot: parseLua(luaStringSplitHot),
stringTrimStartsEnds: parseLua(luaStringTrimStartsEnds),
typeChecking: parseLua(luaTypeChecking),
osDateFormat: parseLua(luaOsDateFormat),
osDateTable: parseLua(luaOsDateTable),
typeConversions: parseLua(luaTypeConversions),
deepNestedAccess: parseLua(luaDeepNestedAccess),
closureCallbacks: parseLua(luaClosureCallbacks),
realisticReadParseTransform: parseLua(luaRealisticReadParseTransform),
realisticConfigCallbacks: parseLua(luaRealisticConfigCallbacks),
};
// =====================================================
// Benchmarks
// =====================================================
// --- API Call Patterns (the primary workload) ---
bench("Perf: sync API calls (1k)", () => runApi(asts.syncApiCalls));
bench("Perf: async API calls (1k)", () => runApi(asts.asyncApiCalls));
bench("Perf: mixed sync/async API calls (1k, 10% async)", () =>
runApi(asts.mixedApiCalls),
);
bench("Perf: sync API filter+collect (1k records)", () =>
runApi(asts.apiFilterCollect),
);
bench("Perf: async API filter+collect (1k records)", () =>
runApi(asts.asyncApiFilterCollect),
);
bench("Perf: async chained read+write (1k)", () =>
runApi(asts.asyncChainedCalls),
);
bench("Perf: sync API + table construction (1k)", () =>
runApi(asts.syncApiTableBuild),
);
// --- Data Structure Traversal ---
bench("Perf: table constructor (3 fields, 10k)", () =>
runMinimal(asts.tableConstructor),
);
bench("Perf: table insert via t[#t+1] (100k)", () =>
runMinimal(asts.tableInsertLoop),
);
bench("Perf: ipairs iteration (1000 elems x100)", () =>
runStd(asts.ipairsIteration),
);
bench("Perf: pairs iteration (100 elems x1000)", () =>
runStd(asts.pairsIteration),
);
bench("Perf: nested table access (page metadata)", () =>
runStd(asts.nestedTableAccess),
);
bench("Perf: table as map (5k insert/lookup/delete)", () =>
runMinimal(asts.tableAsMap),
);
bench("Perf: linked list traversal (1k nodes x100)", () =>
runMinimal(asts.linkedList),
);
bench("Perf: table.sort with comparator (1k x20)", () =>
runStd(asts.tableSort),
);
bench("Perf: table.concat (1k parts x100)", () => runStd(asts.tableConcat));
// --- String Manipulation ---
bench("Perf: string.format (10k)", () => runStd(asts.stringFormat));
bench("Perf: string.find (10k)", () => runStd(asts.stringFind));
bench("Perf: string.sub (100k)", () => runStd(asts.stringSub));
bench("Perf: string concat loop (1k)", () => runMinimal(asts.stringConcat));
bench("Perf: string.split + upper + concat (2k)", () =>
runStd(asts.stringSplitProcess),
);
bench("Perf: string.gsub pattern (5k)", () => runStd(asts.stringGsub));
// --- Function Calls / Control Flow ---
bench("Perf: fibonacci(20) recursive", () => runMinimal(asts.fibonacci));
bench("Perf: closure create + call (10k)", () =>
runMinimal(asts.closureCreateCall),
);
bench("Perf: method call colon syntax (100k)", () =>
runMinimal(asts.methodCall),
);
bench("Perf: varargs function (10k)", () => runStd(asts.varargs));
bench("Perf: higher-order filter+map (1k x50)", () =>
runStd(asts.higherOrderFunctions),
);
// --- Realistic Mixed Workloads ---
bench("Perf: query simulation (filter+sort+build)", () =>
runStd(asts.querySimulation),
);
bench("Perf: config processing (pairs+concat)", () =>
runStd(asts.configProcessing),
);
bench("Perf: template rendering (format+concat)", () =>
runStd(asts.templateRendering),
);
// --- Async Patterns ---
bench("Perf: async loop with sync re-entry (1k)", () =>
runApi(asts.asyncLoopReentry),
);
bench("Perf: while loop with async body (1k)", () =>
runApi(asts.asyncWhileLoop),
);
bench("Perf: async __index metamethod (1k)", () =>
runApi(asts.asyncMetamethod),
);
bench("Perf: async query pattern (fetch+filter+build, 1k)", () =>
runApi(asts.asyncQueryPattern),
);
// --- Community-Driven Gap Coverage ---
// pcall
bench("Perf: pcall success path (1k)", () => runApi(asts.pcallSuccess));
bench("Perf: pcall error path (1k)", () => runStd(asts.pcallError));
bench("Perf: pcall wrapping async API (1k)", () => runApi(asts.pcallAsyncApi));
// string.match / string.gmatch
bench("Perf: string.match date parsing (5k)", () =>
runStd(asts.stringMatchDate),
);
bench("Perf: string.gmatch multi-capture (2k)", () =>
runStd(asts.stringGmatchMulti),
);
bench("Perf: string.match complex patterns (2k)", () =>
runStd(asts.stringMatchComplex),
);
// string building
bench("Perf: table.insert + table.concat output (1k x50)", () =>
runStd(asts.tableInsertConcat),
);
// SB string extensions
bench("Perf: string.split hot loop (5k)", () => runStd(asts.stringSplitHot));
bench("Perf: string.trim + startsWith + endsWith (5k)", () =>
runStd(asts.stringTrimStartsEnds),
);
// type() checking
bench("Perf: type() checking (100k)", () => runStd(asts.typeChecking));
// os.time / os.date
bench("Perf: os.date formatting (2k)", () => runStd(asts.osDateFormat));
bench("Perf: os.date('*t') table return (2k)", () => runStd(asts.osDateTable));
// tostring/tonumber
bench("Perf: tostring + tonumber conversions (10k)", () =>
runStd(asts.typeConversions),
);
// deep nested tables
bench("Perf: deep nested table access 5 levels (10k)", () =>
runMinimal(asts.deepNestedAccess),
);
// closure callbacks
bench("Perf: closure callbacks with upvalues (1k)", () =>
runStd(asts.closureCallbacks),
);
// end-to-end realistic scripts
bench("Perf: realistic script (read+parse+transform+build)", () =>
runApi(asts.realisticReadParseTransform),
);
bench("Perf: realistic script (config+command callbacks)", () =>
runApi(asts.realisticConfigCallbacks),
);