Skip to content

add tests for core/list#2

Merged
Yoorkin merged 1 commit into
mainfrom
yubin/test-list
Jan 30, 2024
Merged

add tests for core/list#2
Yoorkin merged 1 commit into
mainfrom
yubin/test-list

Conversation

@Yoorkin
Copy link
Copy Markdown
Collaborator

@Yoorkin Yoorkin commented Jan 30, 2024

No description provided.

@Yoorkin Yoorkin merged commit ecb8b63 into main Jan 30, 2024
mizchi added a commit to mizchi/core that referenced this pull request May 26, 2026
Three related allocation removals along the JSON number lex chain,
all stemming from the same observation: the native target boxes every
returned struct / tuple / enum payload by default.

## 1. Drop the `(Double, StringView?)` tuple

The number-lex chain (`lex_zero` / `lex_decimal_*` / `lex_number_end` /
`lex_integer_end`) returned `(Double, StringView?)`, where the second
component is the source-text view that's only ever `Some(_)` on the
infinity-overflow path. Native boxes every tuple, so every parsed
JSON number cost one heap allocation just to carry a value that was
structurally `None`.

Side-channel the optional view through a new private
`ParseContext.last_number_repr` field. Each leaf return writes it
(usually `None`); `lex_main.mbt` reads + clears when constructing the
`Number` token.

## 2. Stack-allocate `JsonNumberScan` via `#valtype`

`scan_json_number` builds + returns a five-field `JsonNumberScan`
struct on every JSON number, used immediately by `lex_number_end`
and discarded. Without `#valtype` the native target boxes that
struct as a ~32-byte heap object per call.

## 3. NaN sentinel for `try_fast_double`

`JsonNumberScan::try_fast_double` returned `Double?` — `Some(d)` on
the fast path, `None` to fall back to strconv. The fast path can
only produce 0 or a finite `Double` (the `checked_mul` guard rules
out infinity), so `NaN` is a free sentinel. Returning a plain
`Double` (with NaN meaning "not handled") skips the boxed
`Option<Double>` allocation on every JSON number that hits this path.

## Numbers

Measured on a native-target alloc profiler over mizchi/pprof-mbt's
bench suite (`--sample-rate 100`):

| bench | metric | before | after | Δ |
|---|---|---|---|---|
| `json_numbers` (10 k integers × 30) | allocs | 1 200 200 | 600 200 | **−50 %** |
| | bytes | 18.31 MB | 9.16 MB | **−50 %** |
| `json_parse` (1 000-obj × 50) | allocs | 3 250 200 | 2 300 200 | **−29 %** |
| | bytes | 58.56 MB | 46.93 MB | **−20 %** |

(`moonbitlang#3` only fires for non-integer / non-overflow numbers, so it
adds 100 k allocs / 681 kB on top of #1+moonbitlang#2 on `json_parse` and
nothing on `json_numbers`.)

Tests: `moon test --target native -p json` and
`moon test --target wasm-gc -p json` both pass (171 / 171 each).
`moon fmt --check` clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant