Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ oxlint and oxfmt enforce most of the style. Fix what they report; don't silence
boundary as JSON text (`maple-worker-protocol.ts`, `benchmark-protocol.ts`).
- `@netwin/js-interpreter` is a dev dependency used only to compare against; `js-interpreter-engine.ts` is the one
place in the app that calls it.
- `scripts/` holds Node-only tools such as `measure-memory.ts`. Node ignores the `paths` mapping, so they import
- `scripts/` holds Node-only tools such as `measure-run.ts`. Node ignores the `paths` mapping, so they import
`packages/maple/src/index.ts` by relative path.
- Use standalone components and signals, styled with Tailwind. Don't add `.component` / `.service` suffixes to file
names.
Expand All @@ -62,8 +62,9 @@ oxlint and oxfmt enforce most of the style. Fix what they report; don't silence

- Use kebab-case for file names.
- Use `#private` for state fields only, and spell out `public` / `protected` / `private` on all other members.
- Use [Conventional Commits](https://www.conventionalcommits.org) with a scope: the package, such as `maple`, or `*` for
the whole repository. The version and changelog come from them, so never edit `version` in
- Use [Conventional Commits](https://www.conventionalcommits.org) with a scope naming the area the commit changes, such
as `evaluator`, `json`, `playground` or `parity`, or `*` for the whole repository. Older commits name the package
(`maple`); new ones name the area. The version and changelog come from them, so never edit `version` in
`packages/maple/package.json` or `CHANGELOG.md` by hand.
- Don't add attribution to commits or pull requests: no `Co-Authored-By:` or other trailers, no "Generated with" lines
and no session links.
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ It runs `test` on Node 22, 24 and 26: the minimum from the library's `engines` f
## Commits & releases

Commits must follow [Conventional Commits](https://www.conventionalcommits.org), which commitlint enforces in the `commit-msg` hook.
Every commit needs a scope: the package it changes, such as `maple`, `playground` or `parity`, or `*` when it spans the repository.
Every commit needs a scope: the area it changes, such as `evaluator`, `boundary`, `builtins` or `json` in the library, `playground` or `parity`, or `*` when it spans the repository.
Older commits name the library's package, `maple`, instead; new ones name the area, so the changelog says what changed.

Pull requests target `increment`, the default branch, and are merged with a merge commit or rebased, never squashed, so their commits reach `master` as they are.
A release is a merge of `increment` into `master`, with a merge commit as well.
Expand Down
46 changes: 31 additions & 15 deletions apps/maple-playground/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,44 @@ context and converting the result.
The benchmark has its own worker and a 60 s timeout. js-interpreter has no operation limit, so for an endless loop that
timeout is the only stop.

### Memory: `measure-memory`
### Time and memory: `measure-run`

Browsers have no usable per-run memory measurement, so memory is measured in Node, for a script and context from files:
Browsers have no usable per-run memory measurement, so `measure-run` measures time and memory in Node, for a script
and a context from files:

```sh
pnpm --filter @netwin/maple-playground measure-memory <script.js> [context.json] [--runs 5]
pnpm --filter @netwin/maple-playground measure-run <script.js> [context.json] [--runs 5] [--time-runs 200] [--ast-cache]
```

The context defaults to `scripts/contact-context.json`, a synthetic contact record of 46 flat fields, an `address`,
450 `activities` and 300 `customFields` (about 75 KB as JSON). `contact-flat-key.js`, `contact-nested-key.js` and
`contact-large-key.js` beside it read a flat key, a nested key and the large array. `contact-ten-lines.js` is a
10-line script over several keys, written with `const` and arrow functions, which js-interpreter cannot run.
`contact-summary.js` is a 120-line summary that reads every key, the large ones included. The file is read and parsed
once, before any run, so both engines receive a plain object, as a host's `run` call does.

```
┌──────────────────┬────────────┬───────────┬─────────────┬──────┐
│ engine │ allocated │ retained │ time │ note │
├──────────────────┼────────────┼───────────┼─────────────┼──────┤
│ 'Maple' │ '64.84 MB' │ '0.02 MB' │ '78.15 ms' │ '' │
│ 'js-interpreter' │ '83.86 MB' │ '0.02 MB' │ '825.29 ms' │ '' │
└──────────────────┴────────────┴───────────┴─────────────┴──────┘
┌──────────────────┬───────────┬───────────┬────────────┬────────────┬─────────────┬──────┐
│ engine │ allocated │ retained │ median │ min │ max │ note │
├──────────────────┼───────────┼───────────┼────────────┼────────────┼─────────────┼──────┤
│ 'Maple' │ '1.24 MB' │ '0.01 MB' │ '0.785 ms' │ '0.634 ms' │ '1.215 ms' │ '' │
│ 'js-interpreter' │ '3.99 MB' │ '0.01 MB' │ '3.562 ms' │ '2.667 ms' │ '65.447 ms' │ '' │
└──────────────────┴───────────┴───────────┴────────────┴────────────┴─────────────┴──────┘

Maple is 4.54× faster than js-interpreter (median).
```

Each figure is the median of the measured runs, after 3 warm-up runs:
Both loops start after 3 warm-up runs:

- **allocated** is everything one run creates, short-lived garbage included: the heap growth between a forced garbage
collection before the run and the end of the run.
- **retained** is what is still alive after another forced collection. It should be close to zero; anything more means
the engine keeps something from a run alive.
- **time** is only a rough guide here because of the memory settings below; use the Benchmark tab for speed.
collection before the run and the end of the run. It is the median of `--runs` runs.
- **retained** is what is still alive after another forced collection, again the median of `--runs` runs. It should
be close to zero; anything more means the engine keeps something from a run alive.
- **median**, **min** and **max** time `--time-runs` runs in a second loop, without the forced collections that would
disturb them. Because of the memory settings below they are somewhat optimistic: compare them before and after a
change on one machine, not with production figures.

Below the table, the medians give the factor by which Maple is faster or slower than js-interpreter.

The figure that decides whether a large input runs out of memory is the peak during the run, which lies between
retained and allocated. Node cannot measure it directly for synchronous code.
Expand All @@ -68,4 +82,6 @@ itself with a young generation large enough for that (`--expose-gc --min-semi-sp
--max-semi-space-size=1024`) and watches V8's collection events. If one happened during a run anyway, the **note**
column says so, and that engine's allocation figure is too low.

Maple runs from its sources with no practical operation limit, so long scripts are not cut off.
Maple runs from its sources with no practical operation limit, so long scripts are not cut off. Each run creates its
own `Maple`, so every run parses the script; with `--ast-cache`, all runs share one instance with the AST cache
on, so only the first one parses.
2 changes: 1 addition & 1 deletion apps/maple-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"build": "ng build",
"watch": "ng build --watch --configuration development",
"typecheck": "ngc -p tsconfig.app.json --noEmit && tsc -p tsconfig.worker.json --noEmit && tsc -p tsconfig.scripts.json --noEmit",
"measure-memory": "node scripts/measure-memory.ts"
"measure-run": "node scripts/measure-run.ts"
},
"dependencies": {
"@angular/common": "^22.2.2",
Expand Down
Loading
Loading