dowdiness/js_engine/benchmarks does not have a README file

    BenchArgs

    pub struct BenchArgs {
    run_all : Bool
    csv : Bool
    startup_phases : Bool
    list_only : Bool
    rows : Array[String]?
    }

    BenchConfig

    pub struct BenchConfig {
    warmup : Int
    iterations : Int
    group_size : Int
    }

    Parameters that control how a benchmark is measured.

    BenchResult

    pub struct BenchResult {
    name : String
    category : Category
    stage : BenchStage
    warmup : Int
    iterations : Int
    group_size : Int
    mean_ms : Double
    min_ms : Double
    max_ms : Double
    stddev_ms : Double
    cv_pct : Double
    noisy : Bool
    }

    Immutable result snapshot produced by a single benchmark run.

    BenchStage

    pub enum BenchStage {
    Startup
    Frontend
    Execution
    }

    Pipeline stage measured by a benchmark.

    Stage separates the kind of work from the stability category: Startup: interpreter construction, realm setup, and builtin installation. Frontend: lexing, parsing, or compile/closure-conversion preparation. Execution: AST/compiled evaluation, function calls, properties, builtins.

    BenchStage::label

    fn BenchStage::label(self : BenchStage) -> String

    Category

    pub enum Category {
    Regression
    Component
    Workflow
    }

    Benchmark category — controls which benchmarks run in a given context.

    Regression: low-noise, stable scenarios. Run by default and in CI. Component: isolated subsystem benchmarks. Run with --all. Workflow: end-to-end scenarios with higher variance. Run with --all.

    Category::label

    fn Category::label(self : Category) -> String

    ARRAY_MAP_FILTER_SRC

    let ARRAY_MAP_FILTER_SRC : String

    Array.prototype.map + Array.prototype.filter on 1 000 elements.

    Category: Component Exercises: native callable dispatch (UserFuncExt vs NativeCallable), array iteration in builtins_array.mbt, closure capture, new Array allocation on each map/filter call.

    Pipeline: arr.map(fn) → NativeCallable dispatch → call each element's fn → filter(fn) → same path, with boolean result check.

    Expected result: "334" arr = [0..999], map(x => x*2) = [0,2,4,...,1998], filter(x => x%6===0): keeps x where 2k%6=0 i.e. k%3=0 → 334 values.

    BYTECODE_CAPTURED_ACCESS_SRC

    let BYTECODE_CAPTURED_ACCESS_SRC : String

    Captured-binding read/write workload.

    Category: Component Exercises: a counter that must stay in the Environment because a nested function captures it. Loop control remains slot-backed, isolating the current captured/upvalue fallback from generic no-slot mode.

    Expected result: "100000"

    BYTECODE_DISPATCH_STACK_SRC

    let BYTECODE_DISPATCH_STACK_SRC : String

    Bytecode dispatch / stack push-pop control workload.

    Category: Component Exercises: a hot loop whose body is a comma expression over constants. This keeps runtime-helper work low while forcing repeated LoadConst/Pop/SetCompletion bytecode dispatch and value-stack traffic around the loop-control instructions.

    Expected result: "100000"

    BYTECODE_ENV_ACCESS_SRC

    let BYTECODE_ENV_ACCESS_SRC : String

    Environment-name read/write workload.

    Category: Component Exercises: LoadName/StoreName for the same counter loop as BYTECODE_LOCAL_ACCESS_SRC. The unused arguments reference keeps the current prototype from planning function-local slots, so the hot variables are served by Environment name lookup instead.

    Expected result: "100000"

    BYTECODE_LOCAL_ACCESS_SRC

    let BYTECODE_LOCAL_ACCESS_SRC : String

    Function-local slot read/write workload.

    Category: Component Exercises: LoadLocal/StoreLocal for a function-local counter in the same loop shape as the environment and captured-access workloads.

    Expected result: "100000"

    BYTECODE_MICRO_ACCESS_EXPECTED

    let BYTECODE_MICRO_ACCESS_EXPECTED : String

    BYTECODE_MICRO_CALL_EXPECTED

    let BYTECODE_MICRO_CALL_EXPECTED : String

    BYTECODE_PLAIN_CALL_CONTROL_SRC

    let BYTECODE_PLAIN_CALL_CONTROL_SRC : String

    Bytecode plain-function call control workload.

    Category: Component Exercises: the same loop, local assignment, and function creation as plain_call, without invoking the function. Subtract this row from plain_call to estimate the incremental call cost.

    Expected result: "10000"

    BYTECODE_PLAIN_CALL_SRC

    let BYTECODE_PLAIN_CALL_SRC : String

    Bytecode plain-function call workload.

    Category: Component Exercises: repeated calls to a one-parameter identity function. Property lookup, arithmetic in the callee, method receivers, and builtin dispatch are excluded so the difference from plain_call_control focuses on ordinary call activation.

    Expected result: "10000"

    BYTECODE_RUNTIME_HELPERS_SRC

    let BYTECODE_RUNTIME_HELPERS_SRC : String

    Runtime-helper-heavy bytecode workload.

    Category: Component Exercises: property get, property set, method-call receiver preparation, and call_value() in a compact loop. This row intentionally reuses the shared runtime helpers instead of adding bytecode-specific fast paths.

    Expected result: "10000"

    CLOSURE_FACTORY_SRC

    let CLOSURE_FACTORY_SRC : String

    Closure factory: create 1 000 closures, then call each once.

    Category: Component Exercises: closure Environment allocation (one per makeCounter call), captured-variable lookup (count via parent-chain traversal), function call overhead for both the factory and the returned fn.

    Expected result: "500500" Each counter(i) returns i+1, so sum = Σ(i+1) for i=0..999 = 500500.

    FIBONACCI_SMALL_SRC

    let FIBONACCI_SMALL_SRC : String

    FIBONACCI_SRC

    let FIBONACCI_SRC : String

    LEXER_LARGE_SRC

    let LEXER_LARGE_SRC : String

    Large lexer workload — 1 167 tokens (verified; see LEXER_LARGE_TOKEN_COUNT).

    A realistic utility-library style JS source: constants, pure functions, array helpers, algorithms, and usage code. Exercises the full range of token types (template literals excluded) at a realistic source size.

    LEXER_LARGE_TOKEN_COUNT

    let LEXER_LARGE_TOKEN_COUNT : Int

    Exact token count for LEXER_LARGE_SRC (including EOF token). Used by benchmarks.mbt to verify tokenizer correctness before timing.

    LEXER_SMALL_SRC

    let LEXER_SMALL_SRC : String

    Small lexer workload — 110 tokens (verified; see LEXER_SMALL_TOKEN_COUNT).

    Covers: keywords, identifiers, numeric literals, string literals, operators, delimiters, and single-line comments. Designed as a regression guard: stable, low-noise, and fast per call.

    LEXER_SMALL_TOKEN_COUNT

    let LEXER_SMALL_TOKEN_COUNT : Int

    Exact token count for LEXER_SMALL_SRC (including EOF token). Used by benchmarks.mbt to verify tokenizer correctness before timing.

    MICROTASK_DRAIN_200_OUTPUT

    let MICROTASK_DRAIN_200_OUTPUT : String

    MICROTASK_DRAIN_200_SRC

    let MICROTASK_DRAIN_200_SRC : String

    200 Promise reactions increment a shared counter before a timer observes it.

    Expected console output: "200".

    OBJECT_CONSTRUCTION_SRC

    let OBJECT_CONSTRUCTION_SRC : String

    Object literal construction: 1 000 objects with 5 properties each.

    Expected result: "1000".

    PARSER_HEAVY_SRC

    let PARSER_HEAVY_SRC : String

    Expression-heavy source for parser throughput benchmarking.

    Category: Workflow Exercises: Pratt precedence climbing with ternary, binary arithmetic, logical, comparison, and assignment operators interleaved. Also covers: function declarations with complex bodies, block statements, return expressions.

    This source is passed to parse() (which includes tokenize()) to measure combined lexer + parser throughput. It is never passed to run().

    PIPELINE_EXEC_EXPECTED

    let PIPELINE_EXEC_EXPECTED : String

    PIPELINE_EXEC_SRC

    let PIPELINE_EXEC_SRC : String

    Closure-heavy execution source for pipeline stage breakdown.

    Category: Workflow Exercises: tokenization, parsing of function expressions and loops, closure creation, captured-variable lookup, array push/index access, and function calls. The script is wrapped in an IIFE so repeated execution from a pre-parsed AST does not leave top-level user bindings in the benchmark interpreter.

    Expected result: "500500"

    PROPERTY_CHAIN_SRC

    let PROPERTY_CHAIN_SRC : String

    Deep property chain access in a 10 000-iteration JS loop.

    Category: Regression Exercises: get_property() with 5-level object literal nesting. Each JS loop body performs 5 property reads: obj.a → obj.a.b → obj.a.b.c → obj.a.b.c.d → obj.a.b.c.d.e Total: 50 000 get_property() calls per run().

    This workload is deliberately chosen over prototype-chain access to isolate the own-property lookup hot path and keep the scenario stable. Prototype-chain depth and property descriptors are excluded by design.

    Expected result: "420000" (10000 * 42).

    STARTUP_TINY_SRC

    let STARTUP_TINY_SRC : String

    Minimal program used as the interpreter-startup guardrail.

    Category: Regression Exercises: @lib.run() on a tiny source, so cost is dominated by new_interpreter() and setup_builtins() rather than JS execution. Guards: built-in realm-stamping startup regressions (issue #141 / PR #153).

    Expected result: "2"

    STRING_OPS_SRC

    let STRING_OPS_SRC : String

    String method dispatch in a 100-iteration loop.

    Category: Component Exercises: String.prototype.toUpperCase, string concatenation, string length property access.

    Expected result: "600" (100 × "HELLO " = 100 × 6 chars).

    TIMER_DRAIN_200_OUTPUT

    let TIMER_DRAIN_200_OUTPUT : String

    TIMER_DRAIN_200_SRC

    let TIMER_DRAIN_200_SRC : String

    200 setTimeout calls with delays 0, 1, ..., 199 (registered ascending), plus a sentinel at delay=200 that logs the counter after all 200 have fired.

    Expected console output: "200"

    This workload gates the @priority_queue timer-queue migration (issue #329). After migration: 201 pops each O(log n) on the leftist heap = O(n log n) total. Before migration (historical): sort_by + remove(0) per pop = O(n² log n) total.

    VARHOIST_NESTED_BODY

    let VARHOIST_NESTED_BODY : String

    Nesting-heavy body: var declarations scattered across for / if-else / while / try-catch, forcing the recursive descent through every nested scope to collect the function-scoped var-declared names.

    VARHOIST_SMALL_BODY

    let VARHOIST_SMALL_BODY : String

    Small hot-path body: two flat var declarations, the common case for a recursive helper (cf. fib). Represents the body re-walked on each of the millions of calls such a function receives.

    all_bench_names

    fn all_bench_names() -> Array[(String, String, String)]

    Stable list of all benchmark names, in canonical run order. Used by main.mbt to print --list output.

    bench_array_map_filter

    fn bench_array_map_filter() -> BenchResult raise

    bench_bytecode_captured_access

    fn bench_bytecode_captured_access() -> BenchResult raise

    bench_bytecode_closure_factory

    fn bench_bytecode_closure_factory() -> BenchResult raise

    bench_bytecode_dispatch_stack

    fn bench_bytecode_dispatch_stack() -> BenchResult raise

    bench_bytecode_env_access

    fn bench_bytecode_env_access() -> BenchResult raise

    bench_bytecode_local_access

    fn bench_bytecode_local_access() -> BenchResult raise

    bench_bytecode_plain_call

    fn bench_bytecode_plain_call() -> BenchResult raise

    bench_bytecode_plain_call_control

    fn bench_bytecode_plain_call_control() -> BenchResult raise

    bench_bytecode_runtime_helpers

    fn bench_bytecode_runtime_helpers() -> BenchResult raise

    bench_closure_factory

    fn bench_closure_factory() -> BenchResult raise

    bench_closure_legacy_closure_factory

    fn bench_closure_legacy_closure_factory() -> BenchResult raise

    bench_fibonacci

    fn bench_fibonacci() -> BenchResult raise

    bench_for_of

    fn bench_for_of() -> BenchResult raise

    bench_lexer_large

    fn bench_lexer_large() -> BenchResult raise

    bench_lexer_small

    fn bench_lexer_small() -> BenchResult raise

    bench_object_construction

    fn bench_object_construction() -> BenchResult raise

    bench_parse_heavy

    fn bench_parse_heavy() -> BenchResult raise

    bench_pipeline_bytecode_compile

    fn bench_pipeline_bytecode_compile() -> BenchResult raise

    bench_pipeline_bytecode_evaluate

    fn bench_pipeline_bytecode_evaluate() -> BenchResult raise

    bench_pipeline_closure_legacy_evaluate

    fn bench_pipeline_closure_legacy_evaluate() -> BenchResult raise

    bench_pipeline_exec_evaluate

    fn bench_pipeline_exec_evaluate() -> BenchResult raise

    bench_pipeline_exec_lex

    fn bench_pipeline_exec_lex() -> BenchResult raise

    bench_pipeline_exec_parse

    fn bench_pipeline_exec_parse() -> BenchResult raise

    bench_property_chain

    fn bench_property_chain() -> BenchResult raise

    bench_startup_event_loop_drain_empty

    fn bench_startup_event_loop_drain_empty() -> BenchResult raise

    bench_startup_execute_preparsed_tiny

    fn bench_startup_execute_preparsed_tiny() -> BenchResult raise

    bench_startup_new_interpreter

    fn bench_startup_new_interpreter() -> BenchResult raise

    bench_startup_parse_tiny

    fn bench_startup_parse_tiny() -> BenchResult raise

    bench_startup_result_stringify_output

    fn bench_startup_result_stringify_output() -> BenchResult raise

    bench_startup_tiny

    fn bench_startup_tiny() -> BenchResult raise

    bench_string_ops

    fn bench_string_ops() -> BenchResult raise

    now_ms

    fn now_ms() -> Double

    Millisecond timer for the native backend.

    Uses the core environment clock. This is wall-clock time, but sufficient for the CLI benchmark runner on native builds.

    parse_bench_args

    fn parse_bench_args() -> BenchArgs raise

    Parse CLI arguments for the native backend. Args start at index 1 (the executable path is argv[0]).
    fn print_csv_header() -> Unit

    Print the CSV header line (call once before any print_result_csv calls).
    fn print_result(r : BenchResult) -> Unit

    Print one result in aligned human-readable form.

    Example: [regression] lexer/small 100 itr × 100 reps mean: 0.04ms min: 0.03ms max: 0.07ms CV: 5.2%
    fn print_result_csv(r : BenchResult) -> Unit

    Print one result as a CSV row for baseline tracking.

    Columns: name, category, stage, warmup, iterations, group_size, mean_ms, min_ms, max_ms, stddev_ms, cv_pct, noisy

    run_all

    fn run_all() -> Array[BenchResult] raise

    run_bench

    fn run_bench(name : String, category : Category, config : BenchConfig, f : () -> Unit raise, stage? : BenchStage) -> BenchResult raise

    Run a benchmark scenario and return timing statistics.

    Measurement strategy:
    1. Run config.warmup groups to prime JIT and CPU caches. Discard.
    2. For each of config.iterations outer iterations: a. Record t0 = now_ms() b. Call f() exactly config.group_size times c. Record t1 = now_ms() d. Store per-call time: (t1 - t0) / group_size
    3. Compute mean, min, max, stddev, CV over stored per-call times.

    Errors from f() propagate immediately and abort the benchmark. Benchmark correctness (expected output) must be verified by the caller before invoking run_bench, outside the timed region.

    run_component

    fn run_component() -> Array[BenchResult] raise

    run_regression

    fn run_regression() -> Array[BenchResult] raise

    run_startup_phases

    fn run_startup_phases() -> Array[BenchResult] raise

    run_workflow

    fn run_workflow() -> Array[BenchResult] raise

    timer_supported

    fn timer_supported() -> Bool

    Returns true: a millisecond timer is available on the native target.

    verify

    fn verify(src : String, expected : String) -> Unit raise

    Verify that run(src) produces the expected result string.

    Called once outside any timed region (before bench loops or before b.bench() calls) to confirm the workload is exercising correct behaviour. Aborts with a clear message on mismatch — a mismatch means the workload constant or the interpreter has a bug, not a performance problem.

    Not timed: the interpreter startup and parse cost here is intentionally not representative; that cost is already covered by the pipeline benchmarks.