sx: step 13 — buffer primitives + buffer-based inspect
Added short aliases make-buffer / buffer? / buffer-append! / buffer->string / buffer-length on both OCaml and JS hosts, sharing the existing StringBuffer value type. buffer-append! auto-coerces non-strings via inspect. Rewrote the OCaml host inspect function to walk a single shared Buffer.t instead of allocating O(n) intermediate strings via String.concat at every recursion level. inspect underlies sx-serialize and error-path formatting, so this benefits the tightest serialization paths. Median improvements (bin/bench_inspect.exe, best-of-3 of 9-run min): tree-d8 (75KB): 5.31ms -> 1.30ms (-76%) tree-d10 (679KB): 81.89ms -> 16.02ms (-80%) dict-1000: 0.80ms -> 0.31ms (-61%) list-2000: 0.74ms -> 0.33ms (-55%) Tests: OCaml 4545 -> 4550. JS 2591 -> 2596. Zero regressions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -214,6 +214,29 @@ Add `make-buffer`, `buffer-append!`, `buffer->string` primitives. Eliminates the
|
||||
`(str a b c d ...)` quadratic allocation pattern in serializers and renderers.
|
||||
Wire into `sx_primitives.ml` and the JS platform.
|
||||
|
||||
**Outcome:** Short aliases `make-buffer`/`buffer?`/`buffer-append!`/`buffer->string`/
|
||||
`buffer-length` added on both hosts, sharing the existing `StringBuffer` value type.
|
||||
`buffer-append!` accepts any value (auto-coerces non-strings via inspect), unlike
|
||||
`string-buffer-append!` which is strict. The hot path converted was the OCaml
|
||||
host-internal `inspect` function in `sx_types.ml`: rewrote from `(... ^ String.concat
|
||||
" " (List.map inspect items) ^ ...)` (which allocates O(n) intermediate strings per
|
||||
recursion level) to a single shared `Buffer.t` accumulator (`inspect_into buf v`
|
||||
walks the value tree appending into one buffer). `inspect` is called by
|
||||
`sx-serialize` on both spec and host paths, plus error-path formatting.
|
||||
|
||||
Median improvements (`bin/bench_inspect.exe`, best of 3 runs of 9-run min):
|
||||
|
||||
| Benchmark | Baseline (best min) | Buffer (best min) | Change |
|
||||
|-------------------|--------------------:|------------------:|-------:|
|
||||
| tree-d8 (75KB) | 5.31ms | 1.30ms | -76% |
|
||||
| tree-d10 (679KB) | 81.89ms | 16.02ms | -80% |
|
||||
| dict-1000 | 0.80ms | 0.31ms | -61% |
|
||||
| list-2000 | 0.74ms | 0.33ms | -55% |
|
||||
|
||||
5 new tests in `spec/tests/test-string-buffer.sx` covering the new aliases (incl
|
||||
non-string coercion and interop with the existing `string-buffer-*` API).
|
||||
OCaml: 4545 → 4550. JS: 2591 → 2596. Zero regressions.
|
||||
|
||||
### Step 14: Inline common primitives in JIT
|
||||
|
||||
`hosts/ocaml/lib/sx_vm.ml`: add `OP_ADD`, `OP_SUB`, `OP_EQ`, `OP_APPEND` specialised
|
||||
@@ -238,7 +261,7 @@ these when operands are known numbers/lists.
|
||||
| 10 — compiler + as converter registry | [x] | d22361e4 |
|
||||
| 11 — plugin migration + worker | [x] | 6328b810 |
|
||||
| 12 — frame records | [x] | a66c0f66 (fib -66%, loop -69%, reduce -86% via prim_call fast path) |
|
||||
| 13 — buffer primitive | [ ] | — |
|
||||
| 13 — buffer primitive | [x] | (pending) (inspect rewrite: tree-d10 -80%, tree-d8 -76%, dict-1000 -61%, list-2000 -55%) |
|
||||
| 14 — inline primitives JIT | [ ] | — |
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user