docs: add the blog post "How 3.13.0 got faster"

A post for the documentation (Home > Blog) on how 3.13.0 became faster:
eight themes with the pull requests behind them, the ideas taken from other
libraries, what did not work, how the fast paths are tested, and the
compile-time cost. Every number comes from a run on 2026-10-27 (3.12.0 against
develop plus the open non-draft PRs, Apple M1 Max) or from the linked PR.

- docs/mkdocs/docs/home/blog/how_3_13_0_got_faster.md: the post.
- docs/mkdocs/scripts/figures/perf_blog.py: draws its 12 figures (overview,
  eight timeline panels, the SWAR test, the cliffs, json_view throughput) as
  inline SVGs in the nj-charts style; data/perf_timeline.csv holds the
  timeline.
- tests/benchmarks/history: history.cpp and history.py time one fixed program
  against the header of every commit that changed it, interleaved, and write
  one CSV row per commit with its PR. The timeline of the post comes from it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-30 15:29:45 +01:00
1 parent d20c4dcd89
commit ea848cd2e8
21 files changed
+2297

No files matched your search

+4
View File
@@ -27,6 +27,10 @@
.md-typeset svg.nj-charts--compile, .md-typeset svg.nj-charts--scaling { max-width: 26rem; }
.md-typeset svg.nj-charts--sizes { max-width: 26rem; }
/* home/blog/how_3_13_0_got_faster.md (scripts/figures/perf_blog.py) */
.md-typeset svg.nj-charts--blog { max-width: 34rem; }
.md-typeset svg.nj-charts--swar { max-width: 34rem; }
.md-typeset svg.nj-charts--timeline { max-width: 22rem; }
.nj-charts text { fill: currentColor; font-size: 13px; }
.nj-charts .nj-charts-leg { font-size: 13.5px; }
@@ -0,0 +1,774 @@
# How nlohmann/json 3.13.0 got 2.9× faster
Since 2016, the README of this library has said this about speed:
> There are certainly faster JSON libraries out there. However, if your goal is to speed up your development by adding
> JSON support with a single header, then this library is the way to go.
That sentence is still true, but the gap is a lot smaller now. Swap the 3.12.0 header for the 3.13.0 one and recompile,
and the 182 benchmarks of the library's suite run on average 2.9× as fast, without a line of your code changing.
Parsing the 1.7 MB `citm_catalog.json` takes 1.75 ms instead of 7.20 ms, and `dump()` of documents made of objects
and strings is 4.7 to 6.7× as fast. The new read-only [`json_document`](../../features/json_view.md) parses the
usual test files faster than yyjson and simdjson's DOM on Apple silicon (with a caveat for float-heavy files that we
explain below).
This post is about *how*. None of the techniques is new: almost every one of them is borrowed from simdjson, yyjson,
fast_float, Żmij, serde_json, SQLite, Boost.JSON or a textbook, and we name the source at each step. What we think is
worth writing about is the combination, and the conditions it had to work under: a C++11 header-only library with a
value type that is a template over its string, number, object and allocator types, input from strings, streams,
`FILE*`, iterator pairs, wide strings and user-defined adapters, output that had to stay byte-for-byte the same (with one deliberate exception, see theme 3), and error messages that had to keep pointing at the same byte.
We tried to be careful about what we claim. Every number below comes either from a benchmark we ran for this post
(described in [How we measured](#how-we-measured)) or from the description of the pull request that made the change,
which we link. The PR numbers were measured at the time against the state before the PR, often on more than one
machine, and say so.
## The short version
Apple M1 Max, Apple clang 21, `-O3 -flto`, the compiler's default standard (C++14); the same benchmark source for both
headers. The parse rows do not include destroying the result (the suite pauses the timer for that):
| workload | 3.12.0 | 3.13.0 | speedup |
| --- | --: | --: | --: |
| `json::parse()` of `twitter.json` | 3.16 ms | 1.05 ms | 3.0× |
| `json::parse()` of `citm_catalog.json` | 7.20 ms | 1.75 ms | 4.1× |
| `json::parse()` of `canada.json` | 15.1 ms | 4.35 ms | 3.5× |
| `json::parse()` of `canada.json`, indented by 4 spaces | 33.8 ms | 5.02 ms | 6.7× |
| `ordered_json::parse()` of `citm_catalog.json` | 10.8 ms | 1.64 ms | 6.6× |
| `json::parse()` of `twitter.json` from an `std::ifstream` | 3.34 ms | 1.35 ms | 2.5× |
| `json::parse()` of `citm_catalog.json` from a `FILE*` | 34.6 ms | 7.26 ms | 4.8× |
| `json::accept()` of `citm_catalog.json` | 6.36 ms | 0.91 ms | 7.0× |
| `dump()` of `twitter.json` | 1.39 ms | 0.208 ms | 6.7× |
| `dump()` of `canada.json` | 6.63 ms | 2.25 ms | 2.9× |
| `dump(4)` of `citm_catalog.json` | 1.93 ms | 0.416 ms | 4.6× |
| `to_cbor()` of `twitter.json` | 0.638 ms | 0.142 ms | 4.5× |
| `from_cbor()` of `twitter.json` from memory | 3.00 ms | 0.965 ms | 3.1× |
| `from_cbor()` of `twitter.json` from a `FILE*` | 9.71 ms | 1.54 ms | 6.3× |
| `diff()` of `canada.json` with every eighth scalar changed | 38.3 ms | 3.67 ms | 10.4× |
| iterating over every value of `canada.json` | 0.861 ms | 0.320 ms | 2.7× |
| copying `citm_catalog.json` and destroying the copy | 1.61 ms | 1.45 ms | 1.11× |
| `==` of `citm_catalog.json` and a copy | 0.264 ms | 0.254 ms | 1.04× |
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_overview.svg"
</div>
The average over all 182 benchmarks of the suite is 2.93× (geometric mean); none got slower except the time of a
copy alone, 0.94 to 1.03× (copying and destroying together is 1.09 to 1.29× as fast). We have not measured where the
copy's few percent go; one known cost is the counter that bounds its recursion, about ten nanoseconds per copied
container in [#5389][5389]'s measurement. Copying and comparing gained the least.
One thing to keep in mind about `citm_catalog.json`: as distributed, it is indented, and 71 % of its 1.7 MB are
whitespace. Several changes below help exactly that. A minified copy of it parses 1.9× as fast as with 3.12.0 in our
timeline, against 3.3× for the file as distributed.
Eight themes did most of the work:
1. [Ask the input what it is](#1-ask-the-input-what-it-is), instead of reading every source one character at a time.
2. [Work on words, not bytes](#2-work-on-words-not-bytes): 8 bytes per step with plain integer arithmetic, 16 with
SSE2 on x86-64 and NEON on 64-bit Arm.
3. [Own the number conversions](#3-own-the-number-conversions): no `strtod`, no locale, no `snprintf`.
4. [Don't do work nobody reads](#4-dont-do-work-nobody-reads): positions, copies and conversions that only matter on
an error or for a rare caller.
5. [Read the assembly](#5-read-the-assembly): stack canaries, missed inlining and failed store forwarding.
6. [Write through a cursor](#6-write-through-a-cursor): no virtual call per character, no allocation per call, no
branch per integer.
7. [Fix the algorithm first](#7-fix-the-algorithm-first): seven operations were quadratic or worse.
8. [Build a different data structure](#8-build-a-different-data-structure) when you only want to read.
## Where the time went: a timeline
The suite compares two points. To see which change did what, we compiled one small benchmark against the header of
every commit on `develop` that changed `json.hpp` or `json_view.hpp` since 3.12.0, 315 of them, and ran all of them interleaved
(details in [How we measured](#how-we-measured)). Each panel shows the speedup over 3.12.0 after each commit; the
labels name the pull requests behind the largest steps. Each point is one build, and moving code around changes a
build's speed by a few percent on its own, so only the larger steps mean something. When `accept()` of
`twitter.json` got 2.5 % slower over the last 20 commits, we timed the two ends against each other in one binary
with six code layouts: they were equal within 0.2 %.
<div class="nj-charts-grid-wrap" markdown>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_parse_citm.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_parse_canada.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_accept_twitter.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_parse_twitter_ifstream.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_dump_twitter.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_dump_canada.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_from_cbor_twitter.svg"
</div>
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_timeline_to_cbor_twitter.svg"
</div>
</div>
A few things stand out:
- **There is no single big step.** Parsing `citm_catalog.json` got faster in about a dozen visible steps; the largest
are a buffer that only error messages read ([#5234][5234], −35 %), the contiguous-input fast paths
([#5283][5283], −20 %), and two consecutive PRs that each took 12 %: positions derived only on error
([#5809][5809]) and skipping indentation eight bytes at a time ([#5810][5810]). Most other PRs each moved it by 2
to 7 %.
- **Writing had a few large steps.** `dump()` of `twitter.json` lost 70 % of its time in one PR ([#5285][5285]: the
write buffer and the bulk string copy), and `dump()` of `canada.json` 65 % in another ([#5634][5634]: Żmij).
- **Some steps come from outside.** Two of the larger ones were contributed: [#5592][5592] by
[@mvanslobbe](https://github.com/mvanslobbe) checks for the expected `:` or `,` before the lexer's token switch
(−7 % on `citm_catalog.json`), and [#5328][5328] by [@alexprabhat99](https://github.com/alexprabhat99) stopped
creating a temporary `basic_json` for every object key that `to_cbor()` writes (−55 %).
- **Regressions happen in between.** [#5185][5185] added UTF-8 validation of every text string to the binary
readers, byte by byte. `from_cbor()` of `twitter.json` then took 37 % longer for 66 commits, until
[#5746][5746] (with [#5741][5741]) made the readers lenient again by default, as in 3.12.0, and the check an
option. That decision was about the format specifications, which don't require a decoder to check, not about
speed; the cost went with it. It never shipped in a release.
- **The parse panels include destroying the result.** The benchmark returns `json::parse(text).size()`, so the
destructor PRs ([#5762][5762], [#5835][5835]) show up in the parse panels too, as steps of up to 6 % in either
direction.
- **The input decides how big a step is.** #5234 measured 8 % on its own benchmark; on the indented
`citm_catalog.json` the same change saved 35 %, because every space went through the buffer it removed.
## 1. Ask the input what it is
This is the function the 3.12.0 lexer called for **every** byte of input:
```cpp
char_int_type get()
{
++position.chars_read_total;
++position.chars_read_current_line;
if (next_unget)
{
// just reset the next_unget variable and work with current
next_unget = false;
}
else
{
current = ia.get_character();
}
if (JSON_HEDLEY_LIKELY(current != char_traits<char_type>::eof()))
{
token_string.push_back(char_traits<char_type>::to_char_type(current));
}
if (current == '\n')
{
++position.lines_read;
position.chars_read_current_line = 0;
}
return current;
}
```
Two counters in memory, a branch for a pending `unget()`, a call into the input adapter, a `push_back()` into a
buffer that only an error message reads, and a branch for line breaks. A string or a number then copied each byte a
second time into the token buffer. That is a clean design: one interface works for a `std::string`, an `std::istream`,
a `FILE*`, a pair of iterators over a `std::list<char>`, or a user's own adapter. But it makes every input pay for the
most limited one.
3.13.0 asks the input what it can do and picks a path at compile time:
```mermaid
flowchart LR
src[/"input"/] --> q{{"what is it?"}}
q -->|"std::string, std::vector,<br>pointers, contiguous ranges"| c["contiguous:<br>scan the whole buffer"]
q -->|"std::istream"| w["window:<br>scan the streambuf's get area"]
q -->|"FILE*"| f["one lock per parse,<br>unlocked reads"]
q -->|"other iterators,<br>user adapters"| b["byte at a time,<br>as before"]
```
<p class="nj-caption" markdown>The four kinds of input the lexer distinguishes in 3.13.0. Everything on the fast paths falls back to the byte path on anything unusual, so the result and the error messages stay the same.</p>
**Contiguous input** ([#5283][5283]). `std::string`, `std::vector<char>` and the like now go through a pointer-based
adapter in every C++ standard, not only C++20. On top of that, strings, numbers and runs of whitespace are scanned in
bulk (themes 2 and 3). Every fast path defers to the byte path on anything malformed, so error messages and `byte`
positions stay the same, with one deliberate correction (the column after a number at the end of a line); the PR
checked that on 481,000 documents.
**Positions only when someone asks** ([#5809][5809]). For seekable input, the lexer no longer counts lines and
columns while it reads. The byte offset is the distance from the start; lines and columns are recomputed from the
consumed input when an error message needs them. This is what serde_json's slice reader does. On an M1 Max, it made
parsing `citm_catalog.json` 11.8 % faster and validating an indented `twitter.json` 22 % faster.
**Streams** ([#5828][5828], [#5884][5884]). An `std::istream` already has a buffer: the get area of its `streambuf`
(`gptr()` to `egptr()`). The lexer now scans that window in bulk, keeps the current token as a pointer into it, and
copies only the part that is about to be refilled. Nothing is read ahead, so the stream is left exactly where it was
before. Reaching the protected get area takes no cast: member pointers formed in a class derived from
`std::streambuf`, so the access rule for protected members applies as intended. Before, parsing from an
`std::ifstream` took 1.8 to 2.5 times as long as reading the file into a string first; after #5828, it takes 11 to
18 % longer.
**`FILE*`** ([#5828][5828]). `std::fgetc` locks the `FILE` on every call. 3.13.0 locks it once per parse
(`flockfile`, then `getc_unlocked`; `_lock_file` and `_fgetc_nolock` with the Microsoft runtime). On macOS, this made
parsing `citm_catalog.json` from a `FILE*` 4.4 times as fast (34.1 ms to 7.8 ms). With glibc the gain was only 1.1 to
1.3×, because glibc's per-call locking is already cheap. The lock is held for the whole parse, which the documentation
now mentions, and both changes have opt-out macros.
## 2. Work on words, not bytes
The core of a JSON lexer is "copy bytes until something interesting happens". In a string, the interesting bytes are
the closing quote, a backslash, a control character, and anything above 0x7F (UTF-8). This test checks eight bytes at
once with ordinary 64-bit arithmetic:
```cpp
// return a word whose high bit is set in every byte of v that needs
// individual handling; zero if all 8 bytes can be copied as they are
inline std::uint64_t swar_string_special(std::uint64_t v) noexcept
{
constexpr std::uint64_t ones = 0x0101010101010101ull;
constexpr std::uint64_t high = 0x8080808080808080ull;
const std::uint64_t q = v ^ 0x2222222222222222ull; // '"'
const std::uint64_t b = v ^ 0x5C5C5C5C5C5C5C5Cull; // '\\'
const std::uint64_t has_quote = (q - ones) & ~q & high;
const std::uint64_t has_backslash = (b - ones) & ~b & high;
const std::uint64_t has_control = (v - 0x2020202020202020ull) & ~v & high; // < 0x20
const std::uint64_t has_non_ascii = v & high; // >= 0x80
return has_quote | has_backslash | has_control | has_non_ascii;
}
```
XOR with the quote turns every quote byte into zero, and `(x - 0x01…01) & ~x & 0x80…80` sets the high bit of the
bytes that were zero: the "determine if a word has a zero byte" trick from Sean Eron Anderson's
[Bit Twiddling Hacks](https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord). When the result is not
zero, the number of trailing zero bits divided by eight is the index of the first special byte. The subtraction can
also flag a byte *after* a real hit (a borrow), but never one before it, so the lowest flag is always right.
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_swar.svg"
</div>
The same kind of word test, written in standard C++11 with a `memcpy` load and a fallback for big-endian targets,
now runs in many places:
| where | what it skips | PR |
| --- | --- | --- |
| lexer: strings | runs of ordinary characters, and runs of well-formed multi-byte UTF-8 | [#5283][5283], [#5738][5738] |
| lexer: indentation | eight spaces per step after a newline, an idea from Glaze's `skip_matching_ws` | [#5810][5810] |
| lexer: numbers | eight digits per step, as fast_float does | [#5738][5738], [#5851][5851] |
| `dump()` | runs that need no escaping, with and without `ensure_ascii` | [#5285][5285] |
| `json_view` `dump()` | ASCII runs with `ensure_ascii` | [#5879][5879] |
Where a vector unit is part of the baseline instruction set (SSE2 on x86-64, NEON on 64-bit Arm), the string paths
use 16 bytes per step ([#5853][5853], [#5628][5628]). Non-ASCII text is validated 16 bytes at a time with simdjson's
"lookup4" UTF-8 check (Keiser and Lemire, 2021). That is the one place that needs more than the baseline: on x86-64
it uses SSSE3, which is checked once per process with `cpuid`. `JSON_NO_SIMD` turns all vector code off. On
`twitter.json`, #5853 made `json::parse()` 5 % faster with Clang and 9 % with GCC on an M1 Max; on long strings, 19
to 24 %.
**Short strings are a different problem.** Most keys and many values are short: `citm_catalog.json` averages 8 bytes
per string and `jeopardy.json` 14. For them, setting up a scan costs more than the scan. [#5878][5878] took the idea
of SQLite's `jsonAppendString()`: check each byte of a string of up to 32 bytes against a 256-entry table of bytes
that can be written unchanged, and copy the string in one go if they all pass. 32 bytes was the best threshold on the
M1; 16 gained less, and 64 lost again. [#5883][5883] then replaced the table, where SSE2 or NEON is available, with two
overlapping loads: a string of 16 to 32 characters is two 16-byte vectors that overlap in the middle, tested once and
stored with two stores, together with its quotes. That cut the instructions of `twitter.json`'s `dump()` by 21 %.
The table stays as the fallback for other targets.
**Escapes come from a table, too.** When the scanner stops at a quote, a backslash or a control character,
[#5822][5822] copies its escape sequence from a table of 8-byte rows (padded with zeros, length in the last byte)
straight into the write buffer: one 8-byte copy and one add, an idea from orjson's `QUOTE_TAB`. On an M1 Max, that
made dumping 100,000 log lines full of `\n`, `\t` and quotes 23 % faster. Most of that, the PR found, comes from
writing straight into the write buffer instead of going through an intermediate one; the table alone gained 2 to
7 %.
!!! note "Loop shape beats branch count"
Not every change in this direction worked the first time. The first version of [#5490][5490] removed one
branch per whitespace byte and made deeply nested `dump(4)` output parse 3.2 times *slower* with Clang. The
rewritten loop no longer let either compiler keep the input's read and end pointers in registers, so both
reloaded them from memory on every character. Restoring the original `do`-`while` shape, guarded by an `if`,
fixed it.
## 3. Own the number conversions
3.12.0 converted every floating-point number with `strtod` and every integer with `strtoull` or `strtoll`, after
copying the token into a buffer. `strtod` is correct, but general: it consults the locale for the decimal point, sets
`errno`, and handles any length of input.
`canada.json`, a GeoJSON file of 111,126 numbers, shows what that costs. 108,528 of its numbers have 16 to 19
significant digits, like `-65.613616999999977`. The classic shortcut, Clinger's fast path, needs the digits to fit
into the 53 bits of a `double` (at most 9,007,199,254,740,992) and the power of ten to be exact (at most 10²²): then a
single multiplication or division is correctly rounded. For most of `canada.json`'s numbers, the digits don't fit.
3.13.0 converts numbers itself, without the C library and without the locale:
```mermaid
flowchart TB
t["number token"] --> d{{"at most 19<br>significant digits?"}}
d -->|"yes"| c{{"digits and power of ten<br>exact as double?"}}
c -->|"yes"| cl["Clinger's fast path:<br>one multiplication or division"]
c -->|"no"| el["Eisel-Lemire:<br>128-bit integer arithmetic"]
d -->|"no"| el2["Eisel-Lemire on the first 19 digits"]
el2 --> amb{{"w and w+1 round<br>to the same double?"}}
amb -->|"yes"| done(["done"])
amb -->|"no"| big["compare with the exact midpoint<br>(big-integer arithmetic)"]
```
<p class="nj-caption" markdown>How 3.13.0 converts a decimal token to a `double`. Of `canada.json`'s 111,080 floating-point numbers, 9,742 take Clinger's fast path and 101,338 Eisel-Lemire; none has more than 19 digits.</p>
The Eisel-Lemire algorithm is adapted from fast_float's `compute_float` ([#5617][5617]), and it gives the correctly
rounded result for up to 19 digits without a fallback (Mushtak and Lemire, 2023). For longer numbers,
[#5738][5738] added fast_float's digit comparison. The table of 128-bit powers of five is fast_float's too. Then
[#5851][5851] stopped scanning the digits twice: the lexer now accumulates the value while it validates the token,
eight digits at a time with fast_float's `is_eight_digits` and `parse_eight_digits`, and a float of up to 19 digits
goes straight into the conversion with no call left on the way.
For output, 3.12.0 used Grisu2, which is fast but sometimes writes a 17th digit where 16 suffice.
[#5634][5634] ported [Żmij](https://github.com/vitaut/zmij) by Victor Zverovich to C++11. It writes the shortest
decimal that reads back as the same `double`, and the closest one if there are several. Its digit conversion, by
Xiang JunBo, turns eight digits into text per step, and the SIMD sequence by Dougall Johnson formats all 16 digits
in one vector register, decimal point included. The PR found one more stall on the way: the old code stored the
digits and immediately loaded them again shifted by one byte, and a load that overlaps two smaller stores can't be
forwarded from the store buffer. On an M1 Max, `dump()` of `canada.json` went from 6,987 µs to 2,405 µs (−66 %), and
on an x86-64 Haswell machine by −70 % (GCC 13) and −72 % (Clang 18).
That one is not free of visible change. Of one million random `double` bit patterns, 1,425 (0.14 %) are now written
differently: 810 with fewer digits (`5.3165205877497296e+16` becomes `5.31652058774973e+16`), 615 with the same
number of digits but the closer ones. They all read back as the same `double`, and none of one million short
decimals changes. In `canada.json`, 69 of the 111,126 numbers come out differently.
## 4. Don't do work nobody reads
A lot of what the 3.12.0 parser did was correct, cheap per call, and read by nobody in the common case.
- **The text of float tokens** ([#5852][5852]). The lexer copied every float token into a buffer so that a SAX
handler's `number_float(val, s)` could see the original text. That copy was about 10 % of parsing `canada.json`.
The built-in handlers behind `parse()` and `accept()` never look at it, so they now get an empty string, and the
lexer copies the text only for user handlers that might.
- **The token buffer** ([#5234][5234]). `get()` pushed every character into `token_string`, a buffer that only the
"last read" part of an error message uses. For contiguous input, the lexer now remembers where the token started and
rebuilds that text from the input if an error happens. The PR measured 8 % on its own benchmark; in the timeline,
parsing the indented `citm_catalog.json` got 35 % faster at that commit, because every space of the indentation
went through the buffer. [#5828][5828] did the same for streams, where the copy was about 20 % of the remaining
cost.
- **Positions** (#5809, above): lines and columns are counted only when an error message needs them.
- **Copies that should have been moves.** `from_cbor()` and the other binary readers ended with
`return res ? result : basic_json(value_t::discarded);`. The conditional operator's operand is an lvalue, so there
is no implicit move: every successfully parsed value was deep-copied once and then destroyed. Returning it by move
([#5501][5501]) took decoding a 2 MB CBOR document from 26.99 ms to 14.65 ms.
- **`ordered_json` growth** ([#5609][5609]). `ordered_json` stores its members in a
`std::vector<std::pair<const std::string, json>>`. Because the key is `const`, the pair's move constructor copies
the key and can throw, so `std::vector` copies every element when it grows, including the whole subtree of every
value. The library now grows that storage itself: keys copied, values moved. Parsing `twitter.json` into
`ordered_json` dropped from 60,456 to 19,182 allocations and from 3.20 ms to 1.70 ms.
- **Members that are inserted one at a time** ([#5882][5882]). The DOM parser now allocates each array once, at its
final size, and collects the members of an `ordered_json` object until the object is complete; big objects get
a hash table for the duplicate-key check. An object with very many members went
from 43 million instructions to 1.9 million. Since then, `ordered_json` parses 1 to 15 % *faster* than `json` in our suite, whose `std::map` still allocates a node per member.
!!! note "The same idea, measured twice"
Moving the key out of the lexer's buffer instead of copying it sounds like a free win. In September,
[#5458][5458] measured it: 3.5 % faster on `citm_catalog.json`, but 1.8 % slower on `jeopardy.json` and 11.9 %
slower on 40,000 short strings, so only string values were moved. In October, with much of the lexer around it
rewritten, [#5855][5855] measured again: `parse()` of `twitter.json` got 3.3 % faster, so keys are moved
now too. An idea that lost once is worth measuring again when the code around it changes.
## 5. Read the assembly
A good part of 3.13.0 came from reading the code the compiler produced for the hot loops, with Clang's
`-Rpass-missed=inline`, profiles, and instruction counts. A few things that turned up:
- **A stack canary on every token** ([#5854][5854]). The function that dispatches on the first byte of a token built
the expected text of `true`, `false` and `null` in local `std::array`s. With `-fstack-protector-strong`, the
default on Ubuntu, any local array gives the whole function a stack canary that is set and checked on every call:
the canary's load alone was 11 % of the function's samples. Now the literal is passed as a string literal with its
length as a template parameter. The same PR replaced the parser's `std::vector<bool>` nesting stack, whose
`push_back` GCC called out of line, with `std::vector<std::uint8_t>`.
- **Exceptions that block inlining** ([#5838][5838], [#5840][5840], [#5839][5839]). Building an exception message
inline (`concat(...)`, `create(...)`) made functions like `get_ref`, `operator[]` and the iterator operators too big
to inline. Moving construction and `throw` into never-inlined helpers fixed 27 sites. The same for the lifecycle:
Clang reported about 380 call sites where the copy constructor, the destructor or a comparison was not inlined,
even for a scalar. With the scalar case inline and the rest out of line, `j == 5` went from 3.55 ns to 0.65 ns
with Clang. It is not free: the probe translation unit grew by 13 % at `-O2`, and with GCC, `==` on large
documents got about 7 % slower.
- **Aliasing** ([#5633][5633]). Stores through a `char*` may alias anything, so a write position kept in a member is
reloaded after every store. `json_view`'s writer keeps it in a local variable instead.
- **Inlining that disappears** ([#5878][5878]). Once `dump_escaped()` had grown, GCC 13 and 16 stopped inlining two
small helpers into the pretty-printing loop, and indented output got up to 13 % slower. They are now forced inline.
- **Code size as a cost** ([#5902][5902]). `get<std::array<int, 1000>>()` used a pack expansion that compiled to one
function of about 28,000 instructions: a thousand inlined copies of the bounds and type checks. A loop needs 49.
!!! note "Instruction counts, not only time"
On the x86-64 machine we used for checking, moving code by a few bytes changed the time of unrelated benchmarks
by up to ±5 %. Many decisions in these PRs were therefore made on instruction counts (callgrind on Linux,
`/usr/bin/time -l` on macOS), checked against timings of builds with all functions and loops aligned to 64
bytes, and timed interleaved in one binary with several code layouts. The target, applied strictly in the later
PRs, was: at least 3 % faster, and no benchmark more than 1 % slower. Some PRs accepted a small loss elsewhere
and say so, such as the 7 % on GCC's `==` above.
## 6. Write through a cursor
In 3.12.0, every `dump()` created its output adapter with `std::make_shared`, and the serializer wrote every
structural character through a virtual call:
```cpp
o->write_characters("{\n", 2);
o->write_characters(indent_string.c_str(), new_indent);
o->write_character('\"');
```
The writing side changed in four steps:
```mermaid
flowchart LR
subgraph before["3.12.0"]
direction LR
d1["dump()"] --> a1["make_shared<br>output adapter"] --> v1["virtual call<br>per write"] --> s1[("std::string")]
end
subgraph after["3.13.0"]
direction LR
d2["dump()"] --> a2["adapter on<br>the stack"] --> b2["1 KiB buffer,<br>bulk append"] --> s2[("std::string")]
end
```
<p class="nj-caption" markdown>The text serializer's output path before and after [#5285][5285] and [#5449][5449].</p>
1. **A buffer in the serializer** ([#5285][5285]): all writes go into a 1 KiB buffer that is flushed in bulk, so the
virtual call happens once per KiB.
2. **No allocation per call** ([#5449][5449]): the adapter lives on the stack. Together with an indentation string
that [#5285][5285] creates only when needed, a compact `dump()` of a small object went from 4 heap allocations to
1, the returned string.
3. **Binary writers get a concrete sink** ([#5286][5286]): `to_cbor()` and friends write into a `std::vector` through
a value-type sink, with no virtual call. The same PR replaced `std::reverse` for byte order with
`__builtin_bswap*`, after finding that Clang had emitted no `bswap` instruction at all in the CBOR number path.
4. **A cursor instead of `vector::insert()`** ([#5824][5824], [#5885][5885]). After step 3, writing an array of
integers as CBOR was still no faster than in 3.12.0, and slower with GCC. The profile said why: each number's
one to eight bytes were appended with `vector::insert()`, an out-of-line insert-anywhere routine, which took 30 %
of the time. The sink now reserves space ahead and writes through a pointer, and runs of numbers are encoded in a
loop without a call per element.
The last step also took the branches out of encoding an integer. The size of each integer in a stream is hard to
predict, so the encoder looks the size class up from the number of leading zero bits and stores the head as one byte
plus one 8-byte word, of which the caller keeps only as many bytes as the class needs:
```cpp
// 1, 2, 3 or 4 if x fits 1, 2, 4 or 8 bytes, without a branch
static unsigned integer_size_class(const std::uint64_t x) noexcept
{
static constexpr std::array<std::uint8_t, 64> size_class_by_leading_zeros = { /* ... */ };
return size_class_by_leading_zeros[count_leading_zeros(x | 1u)];
}
std::size_t encode_first_byte_and_tail(CharType* out, std::uint8_t first_byte,
unsigned size_class, std::uint64_t value) const noexcept
{
out[0] = to_char_type(first_byte);
const unsigned unused_bits = 64u - ((8u << size_class) >> 1u);
const std::uint64_t tail = is_little_endian ? byteswap(value) >> unused_bits : value << unused_bits;
std::memcpy(out + 1, &tail, sizeof(tail)); // always 8 bytes; the caller keeps the first 0, 1, 2, 4 or 8
return ((std::size_t{1} << size_class) >> 1u) + 1;
}
```
Against 3.12.0, writing an array of small signed integers as CBOR went from 0.97× to 4.19× with Clang on an M1 Max,
and from 0.94× to 2.77× with GCC 13 on x86-64 ([#5824][5824]).
## 7. Fix the algorithm first
None of the micro-optimizations above matter much next to an algorithm that is quadratic. These are the ones found and
fixed in this release cycle, measured on the same machine:
| operation | 3.12.0 | 3.13.0 | speedup | cause | PR |
| --- | --: | --: | --: | --- | --- |
| `diff()` of an array of 64,000 numbers against an empty array | 4,102 ms | 9.98 ms | 411× | each `remove` was inserted at the front of the patch | [#5461][5461] |
| `diff()` of an array of 20,000 objects against every second one of them | 119 ms | 3.85 ms | 31× | the same | [#5461][5461] |
| `parse()` with a callback that rejects half of 32,000 objects | 1,334 ms | 11.5 ms | 116× | each rejected value was searched for from the start of its parent | [#5457][5457] |
| `<` on two equal values nested 24 levels deep (C++11 to C++17) | 62.6 ms | 0.002 ms | exponential | `std::vector`'s `<` asks `a < b` and `b < a`, and both recurse | [#5390][5390] |
| `<` on `citm_catalog.json` and a copy, 10× (C++11 to C++17) | 274 ms | 3.81 ms | 72× | the same | [#5390][5390] |
| `flatten()` of `citm_catalog.json` as `ordered_json` | 253 ms | 1.37 ms | 184× | every new key was looked up linearly | [#5834][5834] |
| `to_bson()` of a value nested 200 levels deep, 100× | 19.5 ms | 0.566 ms | 34× | each document's size was computed by walking everything below it again | [#5553][5553] |
| 100,000 × `push_back()` on an `ordered_json` with `JSON_DIAGNOSTICS` | 1,514 ms | 4.98 ms | 304× | every insert re-pointed the parent pointers of all elements | [#5832][5832] |
| 10,000 × `at(i)` on an `ordered_json` array with `JSON_DIAGNOSTICS` | 43.2 ms | 0.081 ms | 533× | the same | [#5832][5832] |
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_cliffs.svg"
</div>
The `<` case matters to anyone who keeps `json` values in a `std::set`, uses them as `std::map` keys, or sorts them
before C++20. Each level of nesting doubled the work: two equal values nested 30 levels deep took 3.8 seconds to
compare in [#5390][5390]'s measurement, and nothing about such a value is unusual. Since C++20, `<` is derived from
`<=>`, which asks once.
The other side of this theme is recursion. A value nested 100,000 levels deep used to crash `dump()`, the copy
constructor, `==`, `hash`, `diff()`, `flatten()` and the binary writers with a stack overflow. The obvious fix,
making every walk iterative, measured 2 % to 20 % slower on ordinary documents ([#5285][5285]), 3 % to 9 % for
copying ([#5389][5389]), and 10 % to 14 % for merging ([#5547][5547]). RapidJSON keeps recursion as its default for
the same reason. 3.13.0 therefore bounds the recursion instead: the first 128 levels run the old recursive code, and
only below that does an explicit stack take over. A document nested less deeply pays only for counting the levels.
Destruction got its own trick ([#5762][5762]). Instead of a heap-allocated stack, the destructor uses pointer
reversal, the idea behind the Schorr-Waite graph marking algorithm: while it empties a container, that container's
last child slot stores the link back to its parent. Destroying any value now allocates nothing and never recurses.
Against 3.12.0, destroying a parsed document is 1.25 to 1.56 times as fast in our suite, and destroying an array of
one million small objects 1.39 times (125 ms to 90 ms).
## 8. Build a different data structure
After all of the above, `json::parse()` is still bound by what it builds: one heap allocation per string, array and
object, and a `std::map` node per member. Many programs only parse a document, read some fields and throw it away.
For them, 3.13.0 adds [`json_document`](../../features/json_view.md) and `json_view` in a new header,
`<nlohmann/json_view.hpp>`.
<div class="nj-fig-wrap" markdown>
--8<-- "images/json_view_layout.svg"
</div>
<p class="nj-caption" markdown>Top: the tree of separate heap allocations that `json::parse()` builds. Bottom: `json_document::parse()` keeps the source text and builds one flat index of 16-byte nodes that point back into it, plus a buffer of decoded strings.</p>
The design is not ours either. simdjson's tape and yyjson's immutable document are both flat arrays of fixed-size
entries in document order instead of trees of separate allocations, and yyjson's entries are 16 bytes, too. What
`json_document` does:
- **One 16-byte node per value and per key, in document order** ([#5620][5620]). Strings stay in the input; only
strings with escapes are decoded into an arena. Integers are converted while their digits are in the cache. A
container stores the size of its subtree, so a reader can step over it in constant time. A document makes a
handful of allocations, however many values it has.
- **Floats stay text until you read them.** The parser records where the integer digits, fraction and exponent are,
and converts on access with the same Clinger/Eisel-Lemire code as `json::parse`. We prototyped converting them
during the parse and dropped it ([#5908][5908]): reading every number of `canada.json` got 4 to 7 % faster, but
parsing it 36 to 90 % slower.
- **SIMD where the baseline has it** ([#5628][5628]): strings scanned 16 bytes at a time with SSE2 or NEON, and
non-ASCII text validated with simdjson's lookup4 check. `\u` escapes are decoded with a table, as yyjson's
`read_hex_u16` does.
- **A hash index for big objects** ([#5628][5628]): an object with 128 or more members gets an open-addressing
table after parsing, an idea from Boost.JSON. Looking up every key of a 10,000-member object went from 59.8 ms to
0.16 ms. The hash is not seeded, so the probe length is capped; if crafted keys exceed the cap, that object falls
back to a linear search, and parsing can't become quadratic.
- **First duplicate wins.** Lookups return the first of duplicate keys, as yyjson and simdjson do, because
returning the last one meant scanning every object to its end and made lookups in small objects 1.6 to 3.4 times
slower ([#5622][5622]).
Parse times in µs on the M1 Max (C++17, `-O3`, best of 30 interleaved rounds; a fresh `json_document` and yyjson
document each time, simdjson's parser reused as its documentation recommends):
| file | `json_document` | yyjson 0.13.0 | simdjson 4.6.11 DOM | Boost.JSON 1.92.0 | `json::parse` |
| --- | --: | --: | --: | --: | --: |
| `twitter.json` (632 KB) | **182** | 192 | 216 | 866 | 1,368 |
| `citm_catalog.json` (1.7 MB) | **360** | 478 | 490 | 1,480 | 2,409 |
| `canada.json` (2.3 MB) | **960** | 1,489 | 2,098 | 2,501 | 5,890 |
| `jeopardy.json` (56 MB) | **22,687** | 30,528 | 26,875 | 88,900 | 167,664 |
<div class="nj-fig-wrap" markdown>
--8<-- "images/perf_blog_view.svg"
</div>
On the same four files, reading every value takes yyjson 1.06 to 1.30 times as long as `json_document`, writing
them back with `dump()` 1.06 to 1.35 times, and parsing, editing a few values and writing them 1.34 to 1.85 times (1.0 to 1.57 times for an edit in every
record). Integers are
already converted when you read them, and the writer copies strings and integer tokens straight from the source
([#5633][5633]).
It is not faster everywhere, and the harness ([#5627][5627], [#5914][5914]) is built to show where:
- **Reading a few fields.** simdjson's On-Demand API builds nothing and can only read forward. Picking a few fields
out of `twitter.json`, `canada.json` and `jeopardy.json` takes it 0.67 to 0.80 times as long as `json_document`.
- **Floats are converted later.** `json_document` converts a float when you read it; yyjson and simdjson convert
every number while parsing. For `canada.json`, which is almost all floats, the parse row therefore flatters
`json_document` (1.55× ahead of yyjson); reading every value after parsing is the fair comparison, and there it
is 1.07× ahead.
- **Other CPUs.** On the x86-64 machine with GCC, simdjson's DOM with AVX2 was 19 % ahead on parsing
`twitter.json` and 7 % on `citm_catalog.json` ([#5627][5627]), and yyjson's `dump()` of `canada.json` still is
ahead: `json_document` takes 1.4 times as long there ([#5908][5908]). Our tables are from one Apple silicon
machine.
- **Small documents.** For a 2.5 KB status message, all three engines parse in about a microsecond, and yyjson is
6 % ahead. For a 94-byte message, the engines trade places between runs.
- **`json::parse` itself.** Boost.JSON builds a mutable tree, as `json` does, so it is the fair comparison for
`json::parse`, not for `json_document`. It is still 1.6 to 2.4 times as fast.
## What didn't work
The PRs record the ideas that measured worse, which is as useful as the ones that made it:
| idea | result | where |
| --- | --- | --- |
| make every walk iterative | 2–20 % slower on ordinary documents | [#5285][5285], [#5389][5389], [#5547][5547] |
| move the key out of the lexer's reused buffer | 11.9 % slower (later a win, see above) | [#5458][5458] |
| inline `scan_literal()` | Clang's dispatch grew from 233 to 278 instructions and got slower | [#5854][5854] |
| short-string table for strings up to 64 bytes | lost against 32 | [#5878][5878] |
| leave the serializer's buffers uninitialized | timings moved 18 % apart in opposite directions on two similar workloads, so dropped; done later in [#5883][5883], where instruction counts showed 170 instructions saved per `dump()` | [#5285][5285] |
| out-of-line throw helpers for JSON Pointer lookups | no measurable gain, dropped | [#5836][5836] |
| `json_view`: an "all ASCII" flag per string | `dump()` with `ensure_ascii` faster, but parsing 1–13 % slower | [#5879][5879] |
| `json_view`: convert floats while parsing | `canada.json` parse 36–90 % slower | [#5908][5908] |
| `json_view`: note a large object inline in the parse loop | `canada.json` 37 % slower | [#5628][5628] |
| `json_view`: vector-scan every string in `set()` | 14–16 % slower on 16-byte strings | [#5926][5926] |
## Making fast paths safe
Fast paths are where the bugs are: they read in 8- and 16-byte steps, they skip validation that the slow path did, and
they are taken only for some inputs. A large part of these PRs is checking them:
- **Guard pages** ([#5826][5826]): the input is copied so that its last byte is the last readable byte of a page, and
the next page is unmapped. AddressSanitizer misses an over-read into the slack of the same heap allocation, and
fuzzers rarely end an input exactly at a page boundary; this test crashes on any over-read by one byte.
- **Differential fuzzing of the input kinds** ([#5827][5827]): every fuzz input is also parsed from an
`std::istringstream`, from `std::list` iterators (the byte path) and from a `FILE*`, and the value, the exception
id, the message and the byte offset must be identical.
- **Differential checks at scale**: 6.5 million float tokens against `strtod` including 60,000 exact halfway cases
([#5617][5617]); `json_view`'s batched float output byte-identical to the general writer for 10⁹ generated tokens
([#5908][5908]). For this post, we checked that 3.13.0 writes the same bytes as 3.12.0 in all five binary formats
for 39 test files, and the same JSON text for `twitter.json` and `citm_catalog.json`.
- **Mutation checks**: break the fast path on purpose (skip one byte too many, mark `"` as copyable) and make sure the
tests notice. In [#5878][5878], marking the quote as copyable fails 4,551 assertions.
## The price
None of this is free at compile time. `json.hpp` grew from 25,526 to 45,582 lines (`json_view.hpp` is a separate
header of 11,310 lines that you only pay for if you include it). Apple clang 21 at `-O2`, median of 9 compilations:
| translation unit | 3.12.0 | 3.13.0 | 3.13.0 with `JSON_NO_AUTOMATIC_UDLS` |
| --- | --: | --: | --: |
| only includes `json.hpp` | 528 ms | 709 ms (+34 %) | 508 ms (−4 %) |
| parses, dumps and writes CBOR | 1,175 ms | 1,885 ms (+60 %) | 1,858 ms (+58 %) |
We have not broken the second row down by change. [`JSON_NO_AUTOMATIC_UDLS`](../../api/macros/json_no_automatic_udls.md) moves the
`_json` literals into their own header and more than makes up for the first row; the
[compile-time page](../../integration/compile_times.md) lists the other options.
## How we measured
**What is compared.** "3.12.0" is the released single header. "3.13.0" is `develop` at
[84c542164](https://github.com/nlohmann/json/commit/84c5421644) with seven of the maintainer's open, non-draft pull requests that touch code
merged in ([#5890][5890], [#5908][5908], [#5914][5914], [#5926][5926], [#5936][5936], [#5945][5945],
[#5947][5947]) and amalgamated. Merging #5890 onto today's `develop` left one duplicate member declaration, which we
removed; nothing else was changed. Both are compiled from the *same* benchmark source, the Google Benchmark suite in
[`tests/benchmarks`](https://github.com/nlohmann/json/tree/develop/tests/benchmarks), with the BON8 benchmarks
removed because 3.12.0 has no BON8.
| | |
| --- | --- |
| machine | Apple M1 Max laptop on AC power, macOS 27.0.1 |
| compiler | Apple clang 21, libc++ |
| flags | `-O3 -flto -DNDEBUG`, the compiler's default standard, C++14 (the timeline and `json_view` comparisons: C++17, `-O3`, no LTO) |
| suite | 2 rounds × 4 repetitions per version, random interleaving, each round started only when the load average was below 4; the median of all 8 |
| noise | the coefficient of variation was 3 % or less for 176 of the 182 rows with 3.12.0 and 178 with 3.13.0; the largest was 12 % |
| inputs | `twitter.json`, `citm_catalog.json` and `canada.json` from [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark), a 56 MB `jeopardy.json`, and number-heavy files |
The cliffs come from small extra programs (C++17, `-O3`, median of 3 interleaved runs). The timeline is
[`tests/benchmarks/history`](https://github.com/nlohmann/json/tree/develop/tests/benchmarks/history): one program,
compiled against `single_include` of 3.12.0, of each of the 315 commits on `develop` that changed `json.hpp` or `json_view.hpp` since, and of the candidate, 4 rounds in random order, median of
the rounds. The `json_view` comparison is
[`tests/benchmarks/json_view/compare.py`](https://github.com/nlohmann/json/tree/develop/tests/benchmarks/json_view)
with pinned downloads of yyjson 0.13.0, simdjson 4.6.11 and Boost.JSON 1.92.0, 30 interleaved rounds, best round;
every engine must produce the same values, strings and sums of numbers before anything is timed.
**What we did not measure.** All of our numbers are from one Apple silicon laptop with Clang. The PRs quote numbers
from an x86-64 Haswell server with GCC 13 and Clang 18 where they had them; that server shares its cores with other
work, so we did not run a second set of tables there and quote its timings only where a PR did. Expect smaller
gains for some workloads on x86-64 with glibc: its `FILE*` locking is cheaper to begin with, and its allocator
behaves differently. We did not test Windows for speed at all.
## Thanks
Most of what is in this post was invented by other people. The libraries and papers we took ideas or code from, in
the order they appear above:
- [serde_json](https://github.com/serde-rs/json): deriving positions from the offset only on error.
- Sean Eron Anderson's [Bit Twiddling Hacks](https://graphics.stanford.edu/~seander/bithacks.html): the zero-byte
test behind every word-at-a-time scan.
- [Glaze](https://github.com/stephenberry/glaze): skipping indentation in 8-byte steps (`skip_matching_ws`).
- [simdjson](https://github.com/simdjson/simdjson) by Daniel Lemire, Geoff Langdale, John Keiser and contributors:
the "lookup4" UTF-8 check (J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One Instruction Per Byte",
2021), and the tape that showed what a flat index can do.
- [SQLite](https://sqlite.org)'s `jsonAppendString()`: writing short strings with a table loop.
- [orjson](https://github.com/ijl/orjson)'s `QUOTE_TAB`: escape sequences as fixed-size table rows.
- [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors: the Eisel-Lemire
algorithm, its table of powers of five, its digit comparison, and eight digits at a time (N. Mushtak and D. Lemire,
"Fast number parsing without fallback", 2023). W. D. Clinger for the fast path, M. Eisel for the algorithm.
- [Żmij](https://github.com/vitaut/zmij) by Victor Zverovich, with the digit conversion of Xiang JunBo and the SIMD
sequence of Dougall Johnson.
- [Boost.JSON](https://github.com/boostorg/json): switching to a hash table for large objects.
- [yyjson](https://github.com/ibireme/yyjson) by YaoYuan: table-driven `\u` decoding, fixed-offset unrolled checks,
and a 16-byte flat document to measure against.
- [RapidJSON](https://github.com/Tencent/rapidjson): the measured reason to keep recursion as the default.
And the people who sent or shaped pull requests: [@gregmarr](https://github.com/gregmarr) reviewed most of them and
suggested the sized-sentinel fast path, the peek design for exact stream positions, one SIMD switch for the whole
library, and the vector check of `json_view`'s strings on edit; [@veselink1](https://github.com/veselink1) and
[@michaelsam94](https://github.com/michaelsam94) wrote the allocator and exception-safety fixes for the destructor
that [#5762][5762] took over (the non-recursive destructor itself goes back to
[#1436](https://github.com/nlohmann/json/pull/1436) by [@nickaein](https://github.com/nickaein) in 2019);
[@commit-msuyog](https://github.com/commit-msuyog) gave the 128-bit multiplication behind Eisel-Lemire MSVC
intrinsics ([#5782][5782]); [@ayush-singh-0601](https://github.com/ayush-singh-0601) started the fix for deep values in the binary writers;
[@avionicharshit-byte](https://github.com/avionicharshit-byte) made `diff()` linear for shrinking arrays
([#5461][5461]); [@mvanslobbe](https://github.com/mvanslobbe) sped up the parser's separator check ([#5592][5592]);
and [@alexprabhat99](https://github.com/alexprabhat99) removed the temporary values behind CBOR and MessagePack
object keys ([#5328][5328]).
`twitter.json`, `citm_catalog.json` and `canada.json` come from Milo Yip's
[nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark).
[5185]: https://github.com/nlohmann/json/pull/5185
[5234]: https://github.com/nlohmann/json/pull/5234
[5283]: https://github.com/nlohmann/json/pull/5283
[5285]: https://github.com/nlohmann/json/pull/5285
[5286]: https://github.com/nlohmann/json/pull/5286
[5328]: https://github.com/nlohmann/json/pull/5328
[5389]: https://github.com/nlohmann/json/pull/5389
[5390]: https://github.com/nlohmann/json/pull/5390
[5449]: https://github.com/nlohmann/json/pull/5449
[5457]: https://github.com/nlohmann/json/pull/5457
[5458]: https://github.com/nlohmann/json/pull/5458
[5461]: https://github.com/nlohmann/json/pull/5461
[5490]: https://github.com/nlohmann/json/pull/5490
[5501]: https://github.com/nlohmann/json/pull/5501
[5547]: https://github.com/nlohmann/json/pull/5547
[5553]: https://github.com/nlohmann/json/pull/5553
[5592]: https://github.com/nlohmann/json/pull/5592
[5609]: https://github.com/nlohmann/json/pull/5609
[5617]: https://github.com/nlohmann/json/pull/5617
[5620]: https://github.com/nlohmann/json/pull/5620
[5622]: https://github.com/nlohmann/json/pull/5622
[5627]: https://github.com/nlohmann/json/pull/5627
[5628]: https://github.com/nlohmann/json/pull/5628
[5633]: https://github.com/nlohmann/json/pull/5633
[5634]: https://github.com/nlohmann/json/pull/5634
[5738]: https://github.com/nlohmann/json/pull/5738
[5741]: https://github.com/nlohmann/json/pull/5741
[5746]: https://github.com/nlohmann/json/pull/5746
[5762]: https://github.com/nlohmann/json/pull/5762
[5809]: https://github.com/nlohmann/json/pull/5809
[5810]: https://github.com/nlohmann/json/pull/5810
[5822]: https://github.com/nlohmann/json/pull/5822
[5824]: https://github.com/nlohmann/json/pull/5824
[5826]: https://github.com/nlohmann/json/pull/5826
[5827]: https://github.com/nlohmann/json/pull/5827
[5828]: https://github.com/nlohmann/json/pull/5828
[5832]: https://github.com/nlohmann/json/pull/5832
[5834]: https://github.com/nlohmann/json/pull/5834
[5835]: https://github.com/nlohmann/json/pull/5835
[5836]: https://github.com/nlohmann/json/pull/5836
[5838]: https://github.com/nlohmann/json/pull/5838
[5839]: https://github.com/nlohmann/json/pull/5839
[5840]: https://github.com/nlohmann/json/pull/5840
[5851]: https://github.com/nlohmann/json/pull/5851
[5852]: https://github.com/nlohmann/json/pull/5852
[5853]: https://github.com/nlohmann/json/pull/5853
[5854]: https://github.com/nlohmann/json/pull/5854
[5855]: https://github.com/nlohmann/json/pull/5855
[5878]: https://github.com/nlohmann/json/pull/5878
[5879]: https://github.com/nlohmann/json/pull/5879
[5882]: https://github.com/nlohmann/json/pull/5882
[5883]: https://github.com/nlohmann/json/pull/5883
[5884]: https://github.com/nlohmann/json/pull/5884
[5885]: https://github.com/nlohmann/json/pull/5885
[5890]: https://github.com/nlohmann/json/pull/5890
[5902]: https://github.com/nlohmann/json/pull/5902
[5908]: https://github.com/nlohmann/json/pull/5908
[5914]: https://github.com/nlohmann/json/pull/5914
[5926]: https://github.com/nlohmann/json/pull/5926
[5936]: https://github.com/nlohmann/json/pull/5936
[5945]: https://github.com/nlohmann/json/pull/5945
[5947]: https://github.com/nlohmann/json/pull/5947
@@ -0,0 +1,67 @@
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 600 364" role="img" aria-labelledby="nj-blog-cliffs nj-blog-cliffs-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-cliffs">Operations that grew quadratically or worse: time with 3.12.0 and 3.13.0, log scale</title>
<desc id="nj-blog-cliffs-d">Dot plot on a logarithmic time axis. diff() 64k numbers vs []: 4102 ms to 9.98 ms; diff() 20k objects vs half: 119 ms to 3.85 ms; parse() callback, 32k objects: 1334 ms to 11.5 ms; &lt; nested 24 deep: 62.6 ms to 0.002 ms; &lt; citm, 10x: 274 ms to 3.81 ms; flatten() citm, ordered_json: 253 ms to 1.37 ms; to_bson() 200 deep, 100x: 19.5 ms to 0.566 ms; push_back() 100k, diagnostics: 1514 ms to 4.98 ms; at(i) 10k, diagnostics: 43.2 ms to 0.081 ms.</desc>
<line class="nj-charts-grid" x1="250" y1="30" x2="250" y2="336"/>
<text class="nj-charts-tick" x="250" y="352">1 µs</text>
<line class="nj-charts-grid" x1="297.857" y1="30" x2="297.857" y2="336"/>
<text class="nj-charts-tick" x="297.857" y="352">10 µs</text>
<line class="nj-charts-grid" x1="345.714" y1="30" x2="345.714" y2="336"/>
<text class="nj-charts-tick" x="345.714" y="352">0.1 ms</text>
<line class="nj-charts-grid" x1="393.571" y1="30" x2="393.571" y2="336"/>
<text class="nj-charts-tick" x="393.571" y="352">1 ms</text>
<line class="nj-charts-grid" x1="441.429" y1="30" x2="441.429" y2="336"/>
<text class="nj-charts-tick" x="441.429" y="352">10 ms</text>
<line class="nj-charts-grid" x1="489.286" y1="30" x2="489.286" y2="336"/>
<text class="nj-charts-tick" x="489.286" y="352">0.1 s</text>
<line class="nj-charts-grid" x1="537.143" y1="30" x2="537.143" y2="336"/>
<text class="nj-charts-tick" x="537.143" y="352">1 s</text>
<line class="nj-charts-grid" x1="585" y1="30" x2="585" y2="336"/>
<text class="nj-charts-tick" x="585" y="352">10 s</text>
<circle class="nj-charts-ojson-dot" cx="250" cy="14" r="5"/><text class="nj-charts-leg" x="260" y="18.5">3.12.0</text>
<circle class="nj-charts-json-dot" cx="330" cy="14" r="5"/><text class="nj-charts-leg" x="340" y="18.5">3.13.0</text>
<text class="nj-charts-rowlabel" x="240" y="52" text-anchor="end">diff() 64k numbers vs []</text>
<line class="nj-charts-axis" x1="441.387" y1="48" x2="566.479" y2="48"/>
<circle class="nj-charts-ojson-dot" cx="566.479" cy="48" r="5"><title>diff() 64k numbers vs [], 3.12.0: 4102 ms</title></circle>
<circle class="nj-charts-json-dot" cx="441.387" cy="48" r="5"><title>diff() 64k numbers vs [], 3.13.0: 9.98 ms</title></circle>
<text class="nj-charts-factor" x="503.933" y="41">411&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="86" text-anchor="end">diff() 20k objects vs half</text>
<line class="nj-charts-axis" x1="421.59" y1="82" x2="492.901" y2="82"/>
<circle class="nj-charts-ojson-dot" cx="492.901" cy="82" r="5"><title>diff() 20k objects vs half, 3.12.0: 119 ms</title></circle>
<circle class="nj-charts-json-dot" cx="421.59" cy="82" r="5"><title>diff() 20k objects vs half, 3.13.0: 3.85 ms</title></circle>
<text class="nj-charts-factor" x="457.246" y="75">31&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="120" text-anchor="end">parse() callback, 32k objects</text>
<line class="nj-charts-axis" x1="444.333" y1="116" x2="543.132" y2="116"/>
<circle class="nj-charts-ojson-dot" cx="543.132" cy="116" r="5"><title>parse() callback, 32k objects, 3.12.0: 1334 ms</title></circle>
<circle class="nj-charts-json-dot" cx="444.333" cy="116" r="5"><title>parse() callback, 32k objects, 3.13.0: 11.5 ms</title></circle>
<text class="nj-charts-factor" x="493.733" y="109">116&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="154" text-anchor="end">&lt; nested 24 deep</text>
<line class="nj-charts-axis" x1="264.406" y1="150" x2="479.55" y2="150"/>
<circle class="nj-charts-ojson-dot" cx="479.55" cy="150" r="5"><title>&lt; nested 24 deep, 3.12.0: 62.6 ms</title></circle>
<circle class="nj-charts-json-dot" cx="264.406" cy="150" r="5"><title>&lt; nested 24 deep, 3.13.0: 0.002 ms</title></circle>
<text class="nj-charts-factor" x="371.978" y="143">31300&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="188" text-anchor="end">&lt; citm, 10x</text>
<line class="nj-charts-axis" x1="421.373" y1="184" x2="510.235" y2="184"/>
<circle class="nj-charts-ojson-dot" cx="510.235" cy="184" r="5"><title>&lt; citm, 10x, 3.12.0: 274 ms</title></circle>
<circle class="nj-charts-json-dot" cx="421.373" cy="184" r="5"><title>&lt; citm, 10x, 3.13.0: 3.81 ms</title></circle>
<text class="nj-charts-factor" x="465.804" y="177">72&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="222" text-anchor="end">flatten() citm, ordered_json</text>
<line class="nj-charts-axis" x1="400.114" y1="218" x2="508.578" y2="218"/>
<circle class="nj-charts-ojson-dot" cx="508.578" cy="218" r="5"><title>flatten() citm, ordered_json, 3.12.0: 253 ms</title></circle>
<circle class="nj-charts-json-dot" cx="400.114" cy="218" r="5"><title>flatten() citm, ordered_json, 3.13.0: 1.37 ms</title></circle>
<text class="nj-charts-factor" x="454.346" y="211">185&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="256" text-anchor="end">to_bson() 200 deep, 100x</text>
<line class="nj-charts-axis" x1="381.742" y1="252" x2="455.309" y2="252"/>
<circle class="nj-charts-ojson-dot" cx="455.309" cy="252" r="5"><title>to_bson() 200 deep, 100x, 3.12.0: 19.5 ms</title></circle>
<circle class="nj-charts-json-dot" cx="381.742" cy="252" r="5"><title>to_bson() 200 deep, 100x, 3.13.0: 0.566 ms</title></circle>
<text class="nj-charts-factor" x="418.525" y="245">34&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="290" text-anchor="end">push_back() 100k, diagnostics</text>
<line class="nj-charts-axis" x1="426.939" y1="286" x2="545.763" y2="286"/>
<circle class="nj-charts-ojson-dot" cx="545.763" cy="286" r="5"><title>push_back() 100k, diagnostics, 3.12.0: 1514 ms</title></circle>
<circle class="nj-charts-json-dot" cx="426.939" cy="286" r="5"><title>push_back() 100k, diagnostics, 3.13.0: 4.98 ms</title></circle>
<text class="nj-charts-factor" x="486.351" y="279">304&#215;</text>
<text class="nj-charts-rowlabel" x="240" y="324" text-anchor="end">at(i) 10k, diagnostics</text>
<line class="nj-charts-axis" x1="341.335" y1="320" x2="471.841" y2="320"/>
<circle class="nj-charts-ojson-dot" cx="471.841" cy="320" r="5"><title>at(i) 10k, diagnostics, 3.12.0: 43.2 ms</title></circle>
<circle class="nj-charts-json-dot" cx="341.335" cy="320" r="5"><title>at(i) 10k, diagnostics, 3.13.0: 0.081 ms</title></circle>
<text class="nj-charts-factor" x="406.588" y="313">533&#215;</text>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

@@ -0,0 +1,87 @@
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 560 614" role="img" aria-labelledby="nj-blog-overview nj-blog-overview-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-overview">Speedup of 3.13.0 over 3.12.0 per workload, Apple M1 Max, Apple clang</title>
<desc id="nj-blog-overview-d">Horizontal bars, longer is faster. twitter.json 3.0x; citm_catalog.json 4.1x; canada.json 3.5x; canada.json, indented 6.7x; citm, ordered_json 6.6x; twitter, std::ifstream 2.5x; citm, FILE* 4.8x; citm, accept() 7.0x; dump() twitter 6.7x; dump() canada 3.0x; dump(4) citm 4.6x; to_cbor() twitter 4.5x; twitter, memory 3.1x; twitter, FILE* 6.3x; diff() canada 10x; iterate canada 2.7x; copy + destroy citm 1.11x; == citm 1.04x.</desc>
<text class="nj-charts-panel" x="8" y="46">Parsing</text>
<text class="nj-charts-panel" x="8" y="270">Writing</text>
<text class="nj-charts-panel" x="8" y="398">Reading C&#8203;BOR</text>
<text class="nj-charts-panel" x="8" y="478">Values</text>
<line class="nj-charts-grid" x1="220" y1="34" x2="220" y2="586"/>
<text class="nj-charts-tick" x="220" y="602">1&#215;</text>
<line class="nj-charts-grid" x1="250" y1="34" x2="250" y2="586"/>
<text class="nj-charts-tick" x="250" y="602">2&#215;</text>
<line class="nj-charts-grid" x1="280" y1="34" x2="280" y2="586"/>
<text class="nj-charts-tick" x="280" y="602">3&#215;</text>
<line class="nj-charts-grid" x1="310" y1="34" x2="310" y2="586"/>
<text class="nj-charts-tick" x="310" y="602">4&#215;</text>
<line class="nj-charts-grid" x1="340" y1="34" x2="340" y2="586"/>
<text class="nj-charts-tick" x="340" y="602">5&#215;</text>
<line class="nj-charts-grid" x1="370" y1="34" x2="370" y2="586"/>
<text class="nj-charts-tick" x="370" y="602">6&#215;</text>
<line class="nj-charts-grid" x1="400" y1="34" x2="400" y2="586"/>
<text class="nj-charts-tick" x="400" y="602">7&#215;</text>
<line class="nj-charts-grid" x1="430" y1="34" x2="430" y2="586"/>
<text class="nj-charts-tick" x="430" y="602">8&#215;</text>
<line class="nj-charts-grid" x1="460" y1="34" x2="460" y2="586"/>
<text class="nj-charts-tick" x="460" y="602">9&#215;</text>
<line class="nj-charts-grid" x1="490" y1="34" x2="490" y2="586"/>
<text class="nj-charts-tick" x="490" y="602">10&#215;</text>
<line class="nj-charts-grid" x1="520" y1="34" x2="520" y2="586"/>
<text class="nj-charts-tick" x="520" y="602">11&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="68" text-anchor="end">twitter.json</text>
<path class="nj-charts-c1" d="M190 56 H276.6 Q280.6 56 280.6 60 V67 Q280.6 71 276.6 71 H190 Z"><title>twitter.json: 3.0&#215;</title></path>
<text class="nj-charts-val" x="285.6" y="68">3.0&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="92" text-anchor="end">citm_catalog.json</text>
<path class="nj-charts-c1" d="M190 80 H309.9 Q313.9 80 313.9 84 V91 Q313.9 95 309.9 95 H190 Z"><title>citm_catalog.json: 4.1&#215;</title></path>
<text class="nj-charts-val" x="318.9" y="92">4.1&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="116" text-anchor="end">canada.json</text>
<path class="nj-charts-c1" d="M190 104 H289.8 Q293.8 104 293.8 108 V115 Q293.8 119 289.8 119 H190 Z"><title>canada.json: 3.5&#215;</title></path>
<text class="nj-charts-val" x="298.8" y="116">3.5&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="140" text-anchor="end">canada.json, indented</text>
<path class="nj-charts-c1" d="M190 128 H388.2 Q392.2 128 392.2 132 V139 Q392.2 143 388.2 143 H190 Z"><title>canada.json, indented: 6.7&#215;</title></path>
<text class="nj-charts-val" x="397.2" y="140">6.7&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="164" text-anchor="end">citm, ordered_json</text>
<path class="nj-charts-c1" d="M190 152 H383.4 Q387.4 152 387.4 156 V163 Q387.4 167 383.4 167 H190 Z"><title>citm, ordered_json: 6.6&#215;</title></path>
<text class="nj-charts-val" x="392.4" y="164">6.6&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="188" text-anchor="end">twitter, std::ifstream</text>
<path class="nj-charts-c1" d="M190 176 H259.8 Q263.8 176 263.8 180 V187 Q263.8 191 259.8 191 H190 Z"><title>twitter, std::ifstream: 2.5&#215;</title></path>
<text class="nj-charts-val" x="268.8" y="188">2.5&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="212" text-anchor="end">citm, FILE*</text>
<path class="nj-charts-c1" d="M190 200 H328.8 Q332.8 200 332.8 204 V211 Q332.8 215 328.8 215 H190 Z"><title>citm, FILE*: 4.8&#215;</title></path>
<text class="nj-charts-val" x="337.8" y="212">4.8&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="236" text-anchor="end">citm, accept()</text>
<path class="nj-charts-c1" d="M190 224 H395.7 Q399.7 224 399.7 228 V235 Q399.7 239 395.7 239 H190 Z"><title>citm, accept(): 7.0&#215;</title></path>
<text class="nj-charts-val" x="404.7" y="236">7.0&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="292" text-anchor="end">dump() twitter</text>
<path class="nj-charts-c1" d="M190 280 H386.1 Q390.1 280 390.1 284 V291 Q390.1 295 386.1 295 H190 Z"><title>dump() twitter: 6.7&#215;</title></path>
<text class="nj-charts-val" x="395.1" y="292">6.7&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="316" text-anchor="end">dump() canada</text>
<path class="nj-charts-c1" d="M190 304 H274.5 Q278.5 304 278.5 308 V315 Q278.5 319 274.5 319 H190 Z"><title>dump() canada: 3.0&#215;</title></path>
<text class="nj-charts-val" x="283.5" y="316">3.0&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="340" text-anchor="end">dump(4) citm</text>
<path class="nj-charts-c1" d="M190 328 H324.9 Q328.9 328 328.9 332 V339 Q328.9 343 324.9 343 H190 Z"><title>dump(4) citm: 4.6&#215;</title></path>
<text class="nj-charts-val" x="333.9" y="340">4.6&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="364" text-anchor="end">to_cbor() twitter</text>
<path class="nj-charts-c1" d="M190 352 H321 Q325 352 325 356 V363 Q325 367 321 367 H190 Z"><title>to_cbor() twitter: 4.5&#215;</title></path>
<text class="nj-charts-val" x="330" y="364">4.5&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="420" text-anchor="end">twitter, memory</text>
<path class="nj-charts-c1" d="M190 408 H279.3 Q283.3 408 283.3 412 V419 Q283.3 423 279.3 423 H190 Z"><title>twitter, memory: 3.1&#215;</title></path>
<text class="nj-charts-val" x="288.3" y="420">3.1&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="444" text-anchor="end">twitter, FILE*</text>
<path class="nj-charts-c1" d="M190 432 H375.3 Q379.3 432 379.3 436 V443 Q379.3 447 375.3 447 H190 Z"><title>twitter, FILE*: 6.3&#215;</title></path>
<text class="nj-charts-val" x="384.3" y="444">6.3&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="500" text-anchor="end">diff() canada</text>
<path class="nj-charts-c1" d="M190 488 H499.2 Q503.2 488 503.2 492 V499 Q503.2 503 499.2 503 H190 Z"><title>diff() canada: 10&#215;</title></path>
<text class="nj-charts-val" x="508.2" y="500">10&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="524" text-anchor="end">iterate canada</text>
<path class="nj-charts-c1" d="M190 512 H266.7 Q270.7 512 270.7 516 V523 Q270.7 527 266.7 527 H190 Z"><title>iterate canada: 2.7&#215;</title></path>
<text class="nj-charts-val" x="275.7" y="524">2.7&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="548" text-anchor="end">copy + destroy citm</text>
<path class="nj-charts-c1" d="M190 536 H219.3 Q223.3 536 223.3 540 V547 Q223.3 551 219.3 551 H190 Z"><title>copy + destroy citm: 1.11&#215;</title></path>
<text class="nj-charts-val" x="228.3" y="548">1.11&#215;</text>
<text class="nj-charts-rowlabel nj-charts-tick--r" x="182" y="572" text-anchor="end">== citm</text>
<path class="nj-charts-c1" d="M190 560 H217.2 Q221.2 560 221.2 564 V571 Q221.2 575 217.2 575 H190 Z"><title>== citm: 1.04&#215;</title></path>
<text class="nj-charts-val" x="226.2" y="572">1.04&#215;</text>
<line class="nj-charts-ref" x1="220" y1="34" x2="220" y2="586"/>
<line class="nj-charts-zero" x1="190" y1="34" x2="190" y2="586"/>
<text class="nj-charts-note" x="190" y="18">speedup over 3.12.0 (the line at 1&#215; is 3.12.0)</text>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

@@ -0,0 +1,60 @@
<svg class="nj-fig nj-charts nj-charts--swar" viewBox="0 0 640 312" role="img" aria-labelledby="nj-blog-swar nj-blog-swar-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-swar">Finding the end of a run of ordinary string characters eight bytes at a time</title>
<desc id="nj-blog-swar-d">The eight bytes w, i, t, t, e, r, quote and comma are loaded as one word. The test sets the high bit only in byte 6, the quote. Counting trailing zero bits gives 55, divided by eight gives 6: bytes 0 to 5 are copied at once, then the quote ends the string.</desc>
<text class="fg-h" x="40" y="20">eight input bytes, loaded as one 64-bit word (byte 0 is the lowest)</text>
<rect class="fg-cell" x="40" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="72" y="60" text-anchor="middle">w</text>
<text class="nj-charts-tick" x="72" y="92">0x77</text>
<text class="nj-charts-tick" x="72" y="108">byte 0</text>
<rect class="fg-cell" x="110" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="142" y="60" text-anchor="middle">i</text>
<text class="nj-charts-tick" x="142" y="92">0x69</text>
<text class="nj-charts-tick" x="142" y="108">byte 1</text>
<rect class="fg-cell" x="180" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="212" y="60" text-anchor="middle">t</text>
<text class="nj-charts-tick" x="212" y="92">0x74</text>
<text class="nj-charts-tick" x="212" y="108">byte 2</text>
<rect class="fg-cell" x="250" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="282" y="60" text-anchor="middle">t</text>
<text class="nj-charts-tick" x="282" y="92">0x74</text>
<text class="nj-charts-tick" x="282" y="108">byte 3</text>
<rect class="fg-cell" x="320" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="352" y="60" text-anchor="middle">e</text>
<text class="nj-charts-tick" x="352" y="92">0x65</text>
<text class="nj-charts-tick" x="352" y="108">byte 4</text>
<rect class="fg-cell" x="390" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="422" y="60" text-anchor="middle">r</text>
<text class="nj-charts-tick" x="422" y="92">0x72</text>
<text class="nj-charts-tick" x="422" y="108">byte 5</text>
<rect class="fg-cell" x="460" y="34" width="64" height="40" rx="4"/>
<rect class="nj-charts-c2" fill-opacity=".25" x="460" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="492" y="60" text-anchor="middle">&quot;</text>
<text class="nj-charts-tick" x="492" y="92">0x22</text>
<text class="nj-charts-tick" x="492" y="108">byte 6</text>
<rect class="fg-cell" x="530" y="34" width="64" height="40" rx="4"/>
<text class="fg-code" x="562" y="60" text-anchor="middle">,</text>
<text class="nj-charts-tick" x="562" y="92">0x2C</text>
<text class="nj-charts-tick" x="562" y="108">byte 7</text>
<text class="fg-h" x="40" y="132">swar_string_special(): the high bit of each byte that needs handling</text>
<rect class="fg-cell" x="40" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="72" y="169" text-anchor="middle">0x00</text>
<rect class="fg-cell" x="110" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="142" y="169" text-anchor="middle">0x00</text>
<rect class="fg-cell" x="180" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="212" y="169" text-anchor="middle">0x00</text>
<rect class="fg-cell" x="250" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="282" y="169" text-anchor="middle">0x00</text>
<rect class="fg-cell" x="320" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="352" y="169" text-anchor="middle">0x00</text>
<rect class="fg-cell" x="390" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="422" y="169" text-anchor="middle">0x00</text>
<rect class="fg-cell" x="460" y="146" width="64" height="34" rx="4"/>
<rect class="nj-charts-c2" fill-opacity=".25" x="460" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="492" y="169" text-anchor="middle">0x80</text>
<rect class="fg-cell" x="530" y="146" width="64" height="34" rx="4"/>
<text class="fg-code" x="562" y="169" text-anchor="middle">0x00</text>
<text class="fg-code" x="40" y="216">count_trailing_zeros(0x0080000000000000) = 55</text>
<text class="fg-code" x="40" y="242">55 / 8 = 6: copy bytes 0 to 5 in one go, then handle the quote</text>
<text class="fg-note fg-note--l" x="40" y="272">No branch per byte: a few subtractions, ANDs and ORs per word,</text>
<text class="fg-note fg-note--l" x="40" y="290">then one count of trailing zeros.</text>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -0,0 +1,37 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-accept_twitter nj-blog-tl-accept_twitter-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-accept_twitter">accept() twitter.json: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-accept_twitter-d">accept() twitter.json: speedup over 3.12.0 at each of 317 commits, ending at 5.0x. Labeled steps: #5234, #5283, #5592, #5809, #5810, #5853.</desc>
<text class="nj-charts-panel" x="8" y="18">accept() twitter.json</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="159.333" x2="330" y2="159.333"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="163.333">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="110.667" x2="330" y2="110.667"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="114.667">4&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">8&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,208.1 45.8,208.1 45.8,207.8 46.7,207.8 46.7,208.0 47.6,208.0 47.6,207.7 48.5,207.7 48.5,207.9 49.4,207.9 49.4,208.0 50.3,208.0 50.3,208.1 51.2,208.1 51.2,208.4 52.1,208.4 52.1,207.1 53.1,207.1 53.1,207.9 54.0,207.9 54.0,208.2 54.9,208.2 54.9,207.9 55.8,207.9 55.8,207.9 56.7,207.9 56.7,207.7 57.6,207.7 57.6,207.9 58.5,207.9 58.5,207.8 59.4,207.8 59.4,207.7 60.3,207.7 60.3,208.2 61.2,208.2 61.2,207.2 62.1,207.2 62.1,208.0 63.0,208.0 63.0,208.0 63.9,208.0 63.9,207.8 64.8,207.8 64.8,207.9 65.7,207.9 65.7,207.9 66.6,207.9 66.6,207.1 67.5,207.1 67.5,207.8 68.4,207.8 68.4,207.5 69.3,207.5 69.3,207.8 70.2,207.8 70.2,207.9 71.2,207.9 71.2,207.9 72.1,207.9 72.1,207.3 73.0,207.3 73.0,207.7 73.9,207.7 73.9,207.6 74.8,207.6 74.8,207.7 75.7,207.7 75.7,208.1 76.6,208.1 76.6,207.8 77.5,207.8 77.5,207.1 78.4,207.1 78.4,207.9 79.3,207.9 79.3,207.1 80.2,207.1 80.2,207.8 81.1,207.8 81.1,207.3 82.0,207.3 82.0,208.1 82.9,208.1 82.9,207.6 83.8,207.6 83.8,207.6 84.7,207.6 84.7,208.1 85.6,208.1 85.6,208.3 86.5,208.3 86.5,207.8 87.4,207.8 87.4,207.7 88.3,207.7 88.3,207.9 89.3,207.9 89.3,207.8 90.2,207.8 90.2,207.5 91.1,207.5 91.1,207.9 92.0,207.9 92.0,207.8 92.9,207.8 92.9,208.0 93.8,208.0 93.8,194.2 94.7,194.2 94.7,194.9 95.6,194.9 95.6,195.2 96.5,195.2 96.5,194.7 97.4,194.7 97.4,195.3 98.3,195.3 98.3,194.2 99.2,194.2 99.2,194.3 100.1,194.3 100.1,195.0 101.0,195.0 101.0,194.7 101.9,194.7 101.9,194.8 102.8,194.8 102.8,195.3 103.7,195.3 103.7,194.2 104.6,194.2 104.6,194.8 105.5,194.8 105.5,194.9 106.4,194.9 106.4,195.3 107.4,195.3 107.4,194.9 108.3,194.9 108.3,195.0 109.2,195.0 109.2,194.9 110.1,194.9 110.1,195.5 111.0,195.5 111.0,194.7 111.9,194.7 111.9,195.0 112.8,195.0 112.8,195.9 113.7,195.9 113.7,195.3 114.6,195.3 114.6,194.2 115.5,194.2 115.5,194.3 116.4,194.3 116.4,194.8 117.3,194.8 117.3,195.2 118.2,195.2 118.2,195.2 119.1,195.2 119.1,195.2 120.0,195.2 120.0,194.2 120.9,194.2 120.9,194.0 121.8,194.0 121.8,194.8 122.7,194.8 122.7,195.0 123.6,195.0 123.6,195.5 124.6,195.5 124.6,194.2 125.5,194.2 125.5,194.6 126.4,194.6 126.4,195.1 127.3,195.1 127.3,194.8 128.2,194.8 128.2,195.1 129.1,195.1 129.1,193.1 130.0,193.1 130.0,194.6 130.9,194.6 130.9,194.8 131.8,194.8 131.8,194.5 132.7,194.5 132.7,194.2 133.6,194.2 133.6,194.3 134.5,194.3 134.5,194.7 135.4,194.7 135.4,194.7 136.3,194.7 136.3,194.4 137.2,194.4 137.2,195.0 138.1,195.0 138.1,194.3 139.0,194.3 139.0,194.3 139.9,194.3 139.9,141.4 140.8,141.4 140.8,141.1 141.7,141.1 141.7,140.4 142.7,140.4 142.7,140.1 143.6,140.1 143.6,141.2 144.5,141.2 144.5,140.7 145.4,140.7 145.4,140.5 146.3,140.5 146.3,141.1 147.2,141.1 147.2,141.2 148.1,141.2 148.1,141.4 149.0,141.4 149.0,140.3 149.9,140.3 149.9,141.3 150.8,141.3 150.8,140.5 151.7,140.5 151.7,141.2 152.6,141.2 152.6,140.0 153.5,140.0 153.5,141.3 154.4,141.3 154.4,140.0 155.3,140.0 155.3,141.2 156.2,141.2 156.2,141.1 157.1,141.1 157.1,140.1 158.0,140.1 158.0,140.6 158.9,140.6 158.9,140.5 159.8,140.5 159.8,141.7 160.8,141.7 160.8,141.1 161.7,141.1 161.7,140.5 162.6,140.5 162.6,141.3 163.5,141.3 163.5,140.1 164.4,140.1 164.4,140.6 165.3,140.6 165.3,140.1 166.2,140.1 166.2,140.0 167.1,140.0 167.1,139.4 168.0,139.4 168.0,141.0 168.9,141.0 168.9,139.4 169.8,139.4 169.8,140.9 170.7,140.9 170.7,140.8 171.6,140.8 171.6,140.5 172.5,140.5 172.5,140.8 173.4,140.8 173.4,140.1 174.3,140.1 174.3,139.7 175.2,139.7 175.2,139.3 176.1,139.3 176.1,139.5 177.0,139.5 177.0,140.0 177.9,140.0 177.9,139.7 178.9,139.7 178.9,140.0 179.8,140.0 179.8,140.1 180.7,140.1 180.7,140.1 181.6,140.1 181.6,139.6 182.5,139.6 182.5,140.5 183.4,140.5 183.4,139.9 184.3,139.9 184.3,140.5 185.2,140.5 185.2,140.1 186.1,140.1 186.1,139.8 187.0,139.8 187.0,139.2 187.9,139.2 187.9,139.7 188.8,139.7 188.8,139.7 189.7,139.7 189.7,139.8 190.6,139.8 190.6,138.4 191.5,138.4 191.5,138.8 192.4,138.8 192.4,138.7 193.3,138.7 193.3,139.2 194.2,139.2 194.2,140.0 195.1,140.0 195.1,139.7 196.1,139.7 196.1,139.7 197.0,139.7 197.0,139.1 197.9,139.1 197.9,139.3 198.8,139.3 198.8,138.8 199.7,138.8 199.7,138.5 200.6,138.5 200.6,140.2 201.5,140.2 201.5,139.8 202.4,139.8 202.4,139.3 203.3,139.3 203.3,139.6 204.2,139.6 204.2,138.5 205.1,138.5 205.1,139.1 206.0,139.1 206.0,139.6 206.9,139.6 206.9,139.4 207.8,139.4 207.8,140.2 208.7,140.2 208.7,138.5 209.6,138.5 209.6,139.2 210.5,139.2 210.5,139.7 211.4,139.7 211.4,139.0 212.3,139.0 212.3,139.8 213.2,139.8 213.2,138.5 214.2,138.5 214.2,140.1 215.1,140.1 215.1,139.3 216.0,139.3 216.0,139.6 216.9,139.6 216.9,138.7 217.8,138.7 217.8,139.5 218.7,139.5 218.7,138.1 219.6,138.1 219.6,139.3 220.5,139.3 220.5,138.9 221.4,138.9 221.4,139.5 222.3,139.5 222.3,139.6 223.2,139.6 223.2,139.3 224.1,139.3 224.1,140.2 225.0,140.2 225.0,140.7 225.9,140.7 225.9,140.4 226.8,140.4 226.8,138.7 227.7,138.7 227.7,138.9 228.6,138.9 228.6,139.0 229.5,139.0 229.5,139.2 230.4,139.2 230.4,139.7 231.3,139.7 231.3,139.1 232.3,139.1 232.3,139.6 233.2,139.6 233.2,139.5 234.1,139.5 234.1,140.1 235.0,140.1 235.0,138.7 235.9Line truncated
<circle class="nj-charts-json-dot" cx="93.8" cy="194.2" r="3.5"><title>#5234: 1.22&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="93.8" y1="54.0" x2="93.8" y2="189.2"/>
<text class="nj-charts-note" x="93.8" y="50.0" text-anchor="middle">#5234</text>
<circle class="nj-charts-json-dot" cx="139.9" cy="141.4" r="3.5"><title>#5283: 2.6&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="139.9" y1="54.0" x2="139.9" y2="136.4"/>
<text class="nj-charts-note" x="139.9" y="50.0" text-anchor="middle">#5283</text>
<circle class="nj-charts-json-dot" cx="243.1" cy="133.1" r="3.5"><title>#5592: 2.9&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="243.1" y1="54.0" x2="243.1" y2="128.1"/>
<text class="nj-charts-note" x="243.1" y="50.0" text-anchor="middle">#5592</text>
<circle class="nj-charts-json-dot" cx="265.7" cy="116.2" r="3.5"><title>#5809: 3.7&#215;</title></circle>
<circle class="nj-charts-json-dot" cx="266.6" cy="110.0" r="3.5"><title>#5810: 4.0&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="266.6" y1="41.0" x2="266.6" y2="105.0"/>
<text class="nj-charts-note" x="266.6" y="37.0" text-anchor="middle">#5809, #5810</text>
<circle class="nj-charts-json-dot" cx="299.2" cy="96.1" r="3.5"><title>#5853: 4.9&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="299.2" y1="54.0" x2="299.2" y2="91.1"/>
<text class="nj-charts-note" x="299.2" y="50.0" text-anchor="middle">#5853</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,27 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-dump_canada nj-blog-tl-dump_canada-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-dump_canada">dump() canada.json: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-dump_canada-d">dump() canada.json: speedup over 3.12.0 at each of 317 commits, ending at 3.3x. Labeled steps: #5285, #5634.</desc>
<text class="nj-charts-panel" x="8" y="18">dump() canada.json</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="159.333" x2="330" y2="159.333"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="163.333">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="110.667" x2="330" y2="110.667"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="114.667">4&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">8&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,208.0 45.8,208.0 45.8,207.2 46.7,207.2 46.7,207.9 47.6,207.9 47.6,206.7 48.5,206.7 48.5,207.5 49.4,207.5 49.4,207.7 50.3,207.7 50.3,207.3 51.2,207.3 51.2,207.9 52.1,207.9 52.1,206.5 53.1,206.5 53.1,207.5 54.0,207.5 54.0,208.0 54.9,208.0 54.9,207.6 55.8,207.6 55.8,207.2 56.7,207.2 56.7,207.5 57.6,207.5 57.6,206.6 58.5,206.6 58.5,207.1 59.4,207.1 59.4,207.1 60.3,207.1 60.3,207.2 61.2,207.2 61.2,206.6 62.1,206.6 62.1,207.5 63.0,207.5 63.0,207.6 63.9,207.6 63.9,207.7 64.8,207.7 64.8,207.2 65.7,207.2 65.7,206.5 66.6,206.5 66.6,206.8 67.5,206.8 67.5,207.1 68.4,207.1 68.4,207.0 69.3,207.0 69.3,207.0 70.2,207.0 70.2,206.7 71.2,206.7 71.2,208.2 72.1,208.2 72.1,207.5 73.0,207.5 73.0,206.5 73.9,206.5 73.9,206.7 74.8,206.7 74.8,206.2 75.7,206.2 75.7,208.4 76.6,208.4 76.6,207.3 77.5,207.3 77.5,206.8 78.4,206.8 78.4,208.0 79.3,208.0 79.3,207.7 80.2,207.7 80.2,207.1 81.1,207.1 81.1,207.5 82.0,207.5 82.0,207.9 82.9,207.9 82.9,206.3 83.8,206.3 83.8,206.7 84.7,206.7 84.7,207.5 85.6,207.5 85.6,208.1 86.5,208.1 86.5,206.6 87.4,206.6 87.4,207.1 88.3,207.1 88.3,207.2 89.3,207.2 89.3,207.4 90.2,207.4 90.2,207.0 91.1,207.0 91.1,206.7 92.0,206.7 92.0,206.5 92.9,206.5 92.9,207.6 93.8,207.6 93.8,207.4 94.7,207.4 94.7,207.2 95.6,207.2 95.6,207.5 96.5,207.5 96.5,207.6 97.4,207.6 97.4,208.0 98.3,208.0 98.3,208.2 99.2,208.2 99.2,206.7 100.1,206.7 100.1,207.4 101.0,207.4 101.0,206.6 101.9,206.6 101.9,206.5 102.8,206.5 102.8,208.0 103.7,208.0 103.7,208.0 104.6,208.0 104.6,206.3 105.5,206.3 105.5,208.4 106.4,208.4 106.4,207.8 107.4,207.8 107.4,207.4 108.3,207.4 108.3,206.3 109.2,206.3 109.2,208.1 110.1,208.1 110.1,207.5 111.0,207.5 111.0,207.4 111.9,207.4 111.9,207.5 112.8,207.5 112.8,207.7 113.7,207.7 113.7,208.0 114.6,208.0 114.6,206.9 115.5,206.9 115.5,207.4 116.4,207.4 116.4,207.2 117.3,207.2 117.3,207.7 118.2,207.7 118.2,207.3 119.1,207.3 119.1,208.0 120.0,208.0 120.0,206.7 120.9,206.7 120.9,206.5 121.8,206.5 121.8,207.2 122.7,207.2 122.7,208.1 123.6,208.1 123.6,208.1 124.6,208.1 124.6,206.7 125.5,206.7 125.5,207.8 126.4,207.8 126.4,207.6 127.3,207.6 127.3,207.4 128.2,207.4 128.2,207.0 129.1,207.0 129.1,207.4 130.0,207.4 130.0,208.8 130.9,208.8 130.9,209.2 131.8,209.2 131.8,208.4 132.7,208.4 132.7,208.3 133.6,208.3 133.6,208.0 134.5,208.0 134.5,209.1 135.4,209.1 135.4,208.5 136.3,208.5 136.3,209.1 137.2,209.1 137.2,209.2 138.1,209.2 138.1,208.7 139.0,208.7 139.0,207.7 139.9,207.7 139.9,207.7 140.8,207.7 140.8,207.5 141.7,207.5 141.7,202.0 142.7,202.0 142.7,202.1 143.6,202.1 143.6,201.5 144.5,201.5 144.5,202.2 145.4,202.2 145.4,201.7 146.3,201.7 146.3,201.0 147.2,201.0 147.2,202.0 148.1,202.0 148.1,202.4 149.0,202.4 149.0,201.3 149.9,201.3 149.9,202.1 150.8,202.1 150.8,202.1 151.7,202.1 151.7,201.5 152.6,201.5 152.6,201.1 153.5,201.1 153.5,202.3 154.4,202.3 154.4,201.0 155.3,201.0 155.3,202.8 156.2,202.8 156.2,202.1 157.1,202.1 157.1,201.5 158.0,201.5 158.0,201.6 158.9,201.6 158.9,201.4 159.8,201.4 159.8,202.9 160.8,202.9 160.8,202.5 161.7,202.5 161.7,202.2 162.6,202.2 162.6,202.6 163.5,202.6 163.5,201.9 164.4,201.9 164.4,202.9 165.3,202.9 165.3,202.1 166.2,202.1 166.2,202.2 167.1,202.2 167.1,201.6 168.0,201.6 168.0,202.9 168.9,202.9 168.9,201.6 169.8,201.6 169.8,202.5 170.7,202.5 170.7,202.4 171.6,202.4 171.6,202.8 172.5,202.8 172.5,202.1 173.4,202.1 173.4,202.1 174.3,202.1 174.3,201.4 175.2,201.4 175.2,201.3 176.1,201.3 176.1,202.2 177.0,202.2 177.0,201.5 177.9,201.5 177.9,201.0 178.9,201.0 178.9,202.1 179.8,202.1 179.8,201.4 180.7,201.4 180.7,201.4 181.6,201.4 181.6,201.7 182.5,201.7 182.5,201.9 183.4,201.9 183.4,201.5 184.3,201.5 184.3,201.2 185.2,201.2 185.2,202.1 186.1,202.1 186.1,202.6 187.0,202.6 187.0,202.2 187.9,202.2 187.9,201.5 188.8,201.5 188.8,201.5 189.7,201.5 189.7,202.2 190.6,202.2 190.6,201.5 191.5,201.5 191.5,201.5 192.4,201.5 192.4,202.1 193.3,202.1 193.3,202.0 194.2,202.0 194.2,202.3 195.1,202.3 195.1,201.6 196.1,201.6 196.1,201.9 197.0,201.9 197.0,201.4 197.9,201.4 197.9,202.1 198.8,202.1 198.8,201.7 199.7,201.7 199.7,201.7 200.6,201.7 200.6,202.4 201.5,202.4 201.5,201.4 202.4,201.4 202.4,201.9 203.3,201.9 203.3,202.2 204.2,202.2 204.2,201.6 205.1,201.6 205.1,201.3 206.0,201.3 206.0,202.2 206.9,202.2 206.9,202.0 207.8,202.0 207.8,202.8 208.7,202.8 208.7,201.1 209.6,201.1 209.6,202.0 210.5,202.0 210.5,203.5 211.4,203.5 211.4,201.7 212.3,201.7 212.3,202.3 213.2,202.3 213.2,201.5 214.2,201.5 214.2,202.6 215.1,202.6 215.1,202.2 216.0,202.2 216.0,202.3 216.9,202.3 216.9,202.0 217.8,202.0 217.8,203.3 218.7,203.3 218.7,201.9 219.6,201.9 219.6,202.1 220.5,202.1 220.5,201.7 221.4,201.7 221.4,202.1 222.3,202.1 222.3,201.6 223.2,201.6 223.2,202.3 224.1,202.3 224.1,202.2 225.0,202.2 225.0,202.7 225.9,202.7 225.9,202.8 226.8,202.8 226.8,202.0 227.7,202.0 227.7,202.2 228.6,202.2 228.6,202.1 229.5,202.1 229.5,202.6 230.4,202.6 230.4,202.3 231.3,202.3 231.3,202.3 232.3,202.3 232.3,201.3 233.2,201.3 233.2,203.0 234.1,203.0 234.1,203.0 235.0,203.0 235.0,201.3 235.9Line truncated
<circle class="nj-charts-json-dot" cx="141.7" cy="202.0" r="3.5"><title>#5285: 1.09&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="141.7" y1="54.0" x2="141.7" y2="197.0"/>
<text class="nj-charts-note" x="141.7" y="50.0" text-anchor="middle">#5285</text>
<circle class="nj-charts-json-dot" cx="269.4" cy="128.5" r="3.5"><title>#5634: 3.1&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="269.4" y1="54.0" x2="269.4" y2="123.5"/>
<text class="nj-charts-note" x="269.4" y="50.0" text-anchor="middle">#5634</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

@@ -0,0 +1,35 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-dump_twitter nj-blog-tl-dump_twitter-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-dump_twitter">dump() twitter.json: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-dump_twitter-d">dump() twitter.json: speedup over 3.12.0 at each of 317 commits, ending at 6.7x. Labeled steps: #5285, #5738, #5878, #5883.</desc>
<text class="nj-charts-panel" x="8" y="18">dump() twitter.json</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="171.5" x2="330" y2="171.5"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="175.5">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="135" x2="330" y2="135"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="139">4&#215;</text>
<line class="nj-charts-grid" x1="44" y1="98.5" x2="330" y2="98.5"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="102.5">8&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">16&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,208.0 45.8,208.0 45.8,207.2 46.7,207.2 46.7,207.9 47.6,207.9 47.6,206.8 48.5,206.8 48.5,208.0 49.4,208.0 49.4,207.4 50.3,207.4 50.3,207.3 51.2,207.3 51.2,208.0 52.1,208.0 52.1,206.8 53.1,206.8 53.1,207.1 54.0,207.1 54.0,208.0 54.9,208.0 54.9,207.2 55.8,207.2 55.8,207.5 56.7,207.5 56.7,206.8 57.6,206.8 57.6,206.8 58.5,206.8 58.5,207.5 59.4,207.5 59.4,207.3 60.3,207.3 60.3,207.8 61.2,207.8 61.2,206.8 62.1,206.8 62.1,207.8 63.0,207.8 63.0,207.3 63.9,207.3 63.9,208.0 64.8,208.0 64.8,207.4 65.7,207.4 65.7,207.3 66.6,207.3 66.6,206.9 67.5,206.9 67.5,207.2 68.4,207.2 68.4,207.4 69.3,207.4 69.3,207.6 70.2,207.6 70.2,207.5 71.2,207.5 71.2,207.6 72.1,207.6 72.1,207.4 73.0,207.4 73.0,207.2 73.9,207.2 73.9,207.1 74.8,207.1 74.8,207.3 75.7,207.3 75.7,207.7 76.6,207.7 76.6,207.8 77.5,207.8 77.5,206.8 78.4,206.8 78.4,207.6 79.3,207.6 79.3,207.0 80.2,207.0 80.2,206.9 81.1,206.9 81.1,207.3 82.0,207.3 82.0,207.5 82.9,207.5 82.9,206.8 83.8,206.8 83.8,207.7 84.7,207.7 84.7,207.4 85.6,207.4 85.6,207.4 86.5,207.4 86.5,207.1 87.4,207.1 87.4,207.3 88.3,207.3 88.3,207.5 89.3,207.5 89.3,207.3 90.2,207.3 90.2,207.3 91.1,207.3 91.1,206.8 92.0,206.8 92.0,207.4 92.9,207.4 92.9,207.9 93.8,207.9 93.8,207.3 94.7,207.3 94.7,206.7 95.6,206.7 95.6,207.6 96.5,207.6 96.5,207.7 97.4,207.7 97.4,208.7 98.3,208.7 98.3,208.6 99.2,208.6 99.2,208.6 100.1,208.6 100.1,209.2 101.0,209.2 101.0,208.5 101.9,208.5 101.9,208.2 102.8,208.2 102.8,209.3 103.7,209.3 103.7,208.4 104.6,208.4 104.6,208.4 105.5,208.4 105.5,209.0 106.4,209.0 106.4,209.1 107.4,209.1 107.4,208.7 108.3,208.7 108.3,208.9 109.2,208.9 109.2,208.7 110.1,208.7 110.1,209.0 111.0,209.0 111.0,208.8 111.9,208.8 111.9,208.7 112.8,208.7 112.8,209.3 113.7,209.3 113.7,208.7 114.6,208.7 114.6,208.5 115.5,208.5 115.5,208.5 116.4,208.5 116.4,208.0 117.3,208.0 117.3,208.9 118.2,208.9 118.2,208.7 119.1,208.7 119.1,209.0 120.0,209.0 120.0,208.1 120.9,208.1 120.9,208.0 121.8,208.0 121.8,208.6 122.7,208.6 122.7,208.5 123.6,208.5 123.6,209.2 124.6,209.2 124.6,208.0 125.5,208.0 125.5,209.1 126.4,209.1 126.4,209.0 127.3,209.0 127.3,208.5 128.2,208.5 128.2,209.1 129.1,209.1 129.1,209.0 130.0,209.0 130.0,208.9 130.9,208.9 130.9,208.6 131.8,208.6 131.8,208.7 132.7,208.7 132.7,208.2 133.6,208.2 133.6,209.1 134.5,209.1 134.5,209.3 135.4,209.3 135.4,209.1 136.3,209.1 136.3,209.2 137.2,209.2 137.2,209.3 138.1,209.3 138.1,208.5 139.0,208.5 139.0,208.8 139.9,208.8 139.9,209.5 140.8,209.5 140.8,208.4 141.7,208.4 141.7,145.7 142.7,145.7 142.7,146.6 143.6,146.6 143.6,146.7 144.5,146.7 144.5,145.8 145.4,145.8 145.4,145.8 146.3,145.8 146.3,146.0 147.2,146.0 147.2,146.5 148.1,146.5 148.1,146.5 149.0,146.5 149.0,145.6 149.9,145.6 149.9,146.4 150.8,146.4 150.8,145.5 151.7,145.5 151.7,145.8 152.6,145.8 152.6,145.6 153.5,145.6 153.5,146.7 154.4,146.7 154.4,145.7 155.3,145.7 155.3,146.6 156.2,146.6 156.2,146.8 157.1,146.8 157.1,146.0 158.0,146.0 158.0,146.1 158.9,146.1 158.9,146.4 159.8,146.4 159.8,146.9 160.8,146.9 160.8,146.3 161.7,146.3 161.7,146.4 162.6,146.4 162.6,146.5 163.5,146.5 163.5,146.6 164.4,146.6 164.4,147.3 165.3,147.3 165.3,147.0 166.2,147.0 166.2,146.5 167.1,146.5 167.1,146.4 168.0,146.4 168.0,147.3 168.9,147.3 168.9,146.0 169.8,146.0 169.8,147.5 170.7,147.5 170.7,147.4 171.6,147.4 171.6,147.1 172.5,147.1 172.5,146.4 173.4,146.4 173.4,147.0 174.3,147.0 174.3,146.0 175.2,146.0 175.2,146.2 176.1,146.2 176.1,146.0 177.0,146.0 177.0,146.6 177.9,146.6 177.9,146.4 178.9,146.4 178.9,146.5 179.8,146.5 179.8,146.7 180.7,146.7 180.7,146.6 181.6,146.6 181.6,146.4 182.5,146.4 182.5,147.3 183.4,147.3 183.4,146.1 184.3,146.1 184.3,147.2 185.2,147.2 185.2,146.6 186.1,146.6 186.1,146.3 187.0,146.3 187.0,146.1 187.9,146.1 187.9,146.3 188.8,146.3 188.8,146.0 189.7,146.0 189.7,146.5 190.6,146.5 190.6,145.3 191.5,145.3 191.5,146.2 192.4,146.2 192.4,146.2 193.3,146.2 193.3,146.2 194.2,146.2 194.2,146.0 195.1,146.0 195.1,146.3 196.1,146.3 196.1,145.7 197.0,145.7 197.0,145.8 197.9,145.8 197.9,145.8 198.8,145.8 198.8,145.3 199.7,145.3 199.7,145.3 200.6,145.3 200.6,146.6 201.5,146.6 201.5,146.0 202.4,146.0 202.4,146.3 203.3,146.3 203.3,146.5 204.2,146.5 204.2,146.0 205.1,146.0 205.1,146.1 206.0,146.1 206.0,146.6 206.9,146.6 206.9,146.6 207.8,146.6 207.8,146.8 208.7,146.8 208.7,146.4 209.6,146.4 209.6,146.2 210.5,146.2 210.5,146.5 211.4,146.5 211.4,145.8 212.3,145.8 212.3,146.4 213.2,146.4 213.2,145.9 214.2,145.9 214.2,146.7 215.1,146.7 215.1,146.5 216.0,146.5 216.0,146.6 216.9,146.6 216.9,147.0 217.8,147.0 217.8,147.7 218.7,147.7 218.7,147.0 219.6,147.0 219.6,147.8 220.5,147.8 220.5,146.5 221.4,146.5 221.4,146.8 222.3,146.8 222.3,147.4 223.2,147.4 223.2,147.1 224.1,147.1 224.1,147.5 225.0,147.5 225.0,147.7 225.9,147.7 225.9,147.5 226.8,147.5 226.8,146.9 227.7,146.9 227.7,147.2 228.6,147.2 228.6,146.9 229.5,146.9 229.5,147.2 230.4,147.2 230.4,147.3 231.3,147.3 231.3,147.3 232.3,147.3 232.3,146.8 233.2,146.8 233.2,147.5 234.1,147.5 234.1,147.5 235.0,147.5 235.0,146.6 235.9Line truncated
<circle class="nj-charts-json-dot" cx="141.7" cy="145.7" r="3.5"><title>#5285: 3.3&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="141.7" y1="54.0" x2="141.7" y2="140.7"/>
<text class="nj-charts-note" x="141.7" y="50.0" text-anchor="middle">#5285</text>
<circle class="nj-charts-json-dot" cx="268.5" cy="137.1" r="3.5"><title>#5738: 3.8&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="268.5" y1="54.0" x2="268.5" y2="132.1"/>
<text class="nj-charts-note" x="268.5" y="50.0" text-anchor="middle">#5738</text>
<circle class="nj-charts-json-dot" cx="301.9" cy="128.8" r="3.5"><title>#5878: 4.5&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="301.9" y1="41.0" x2="301.9" y2="123.8"/>
<text class="nj-charts-note" x="301.9" y="37.0" text-anchor="middle">#5878</text>
<circle class="nj-charts-json-dot" cx="308.3" cy="106.8" r="3.5"><title>#5883: 6.8&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="308.3" y1="28.0" x2="308.3" y2="101.8"/>
<text class="nj-charts-note" x="308.3" y="24.0" text-anchor="middle">#5883</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,31 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-from_cbor_twitter nj-blog-tl-from_cbor_twitter-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-from_cbor_twitter">from_cbor() twitter: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-from_cbor_twitter-d">from_cbor() twitter: speedup over 3.12.0 at each of 317 commits, ending at 2.6x. Labeled steps: #5233, #5501, #5185, #5746.</desc>
<text class="nj-charts-panel" x="8" y="18">from_cbor() twitter</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="135" x2="330" y2="135"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="139">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">4&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,209.1 45.8,209.1 45.8,208.2 46.7,208.2 46.7,208.6 47.6,208.6 47.6,208.0 48.5,208.0 48.5,208.5 49.4,208.5 49.4,208.6 50.3,208.6 50.3,208.3 51.2,208.3 51.2,209.1 52.1,209.1 52.1,207.8 53.1,207.8 53.1,208.5 54.0,208.5 54.0,208.6 54.9,208.6 54.9,208.7 55.8,208.7 55.8,207.7 56.7,207.7 56.7,207.1 57.6,207.1 57.6,207.3 58.5,207.3 58.5,206.9 59.4,206.9 59.4,207.1 60.3,207.1 60.3,207.0 61.2,207.0 61.2,207.1 62.1,207.1 62.1,207.1 63.0,207.1 63.0,207.5 63.9,207.5 63.9,207.5 64.8,207.5 64.8,206.8 65.7,206.8 65.7,206.9 66.6,206.9 66.6,206.2 67.5,206.2 67.5,207.1 68.4,207.1 68.4,208.5 69.3,208.5 69.3,208.0 70.2,208.0 70.2,207.7 71.2,207.7 71.2,208.5 72.1,208.5 72.1,207.8 73.0,207.8 73.0,207.0 73.9,207.0 73.9,208.1 74.8,208.1 74.8,208.2 75.7,208.2 75.7,208.7 76.6,208.7 76.6,208.1 77.5,208.1 77.5,207.2 78.4,207.2 78.4,208.3 79.3,208.3 79.3,207.4 80.2,207.4 80.2,207.9 81.1,207.9 81.1,207.7 82.0,207.7 82.0,208.7 82.9,208.7 82.9,207.3 83.8,207.3 83.8,207.9 84.7,207.9 84.7,208.4 85.6,208.4 85.6,208.5 86.5,208.5 86.5,208.0 87.4,208.0 87.4,207.0 88.3,207.0 88.3,208.1 89.3,208.1 89.3,208.1 90.2,208.1 90.2,207.8 91.1,207.8 91.1,207.9 92.0,207.9 92.0,207.9 92.9,207.9 92.9,208.7 93.8,208.7 93.8,208.1 94.7,208.1 94.7,167.8 95.6,167.8 95.6,169.3 96.5,169.3 96.5,168.9 97.4,168.9 97.4,167.9 98.3,167.9 98.3,168.6 99.2,168.6 99.2,168.0 100.1,168.0 100.1,169.0 101.0,169.0 101.0,167.7 101.9,167.7 101.9,166.7 102.8,166.7 102.8,169.3 103.7,169.3 103.7,168.4 104.6,168.4 104.6,168.6 105.5,168.6 105.5,171.9 106.4,171.9 106.4,169.4 107.4,169.4 107.4,169.3 108.3,169.3 108.3,169.1 109.2,169.1 109.2,169.2 110.1,169.2 110.1,169.7 111.0,169.7 111.0,169.4 111.9,169.4 111.9,169.3 112.8,169.3 112.8,170.1 113.7,170.1 113.7,169.2 114.6,169.2 114.6,167.5 115.5,167.5 115.5,167.8 116.4,167.8 116.4,167.7 117.3,167.7 117.3,169.4 118.2,169.4 118.2,168.1 119.1,168.1 119.1,168.7 120.0,168.7 120.0,167.1 120.9,167.1 120.9,167.2 121.8,167.2 121.8,168.8 122.7,168.8 122.7,169.2 123.6,169.2 123.6,169.4 124.6,169.4 124.6,168.1 125.5,168.1 125.5,169.4 126.4,169.4 126.4,169.0 127.3,169.0 127.3,168.9 128.2,168.9 128.2,168.9 129.1,168.9 129.1,168.6 130.0,168.6 130.0,168.9 130.9,168.9 130.9,168.1 131.8,168.1 131.8,168.5 132.7,168.5 132.7,168.9 133.6,168.9 133.6,168.9 134.5,168.9 134.5,169.4 135.4,169.4 135.4,169.7 136.3,169.7 136.3,169.4 137.2,169.4 137.2,169.7 138.1,169.7 138.1,168.1 139.0,168.1 139.0,126.4 139.9,126.4 139.9,118.6 140.8,118.6 140.8,118.0 141.7,118.0 141.7,118.3 142.7,118.3 142.7,118.5 143.6,118.5 143.6,117.8 144.5,117.8 144.5,113.9 145.4,113.9 145.4,114.9 146.3,114.9 146.3,114.8 147.2,114.8 147.2,115.4 148.1,115.4 148.1,115.8 149.0,115.8 149.0,115.5 149.9,115.5 149.9,117.6 150.8,117.6 150.8,116.4 151.7,116.4 151.7,117.1 152.6,117.1 152.6,116.5 153.5,116.5 153.5,118.5 154.4,118.5 154.4,116.0 155.3,116.0 155.3,118.7 156.2,118.7 156.2,117.6 157.1,117.6 157.1,115.9 158.0,115.9 158.0,116.8 158.9,116.8 158.9,116.5 159.8,116.5 159.8,117.7 160.8,117.7 160.8,116.5 161.7,116.5 161.7,116.9 162.6,116.9 162.6,117.3 163.5,117.3 163.5,117.9 164.4,117.9 164.4,118.2 165.3,118.2 165.3,117.1 166.2,117.1 166.2,116.3 167.1,116.3 167.1,115.9 168.0,115.9 168.0,117.7 168.9,117.7 168.9,116.9 169.8,116.9 169.8,117.4 170.7,117.4 170.7,117.2 171.6,117.2 171.6,117.3 172.5,117.3 172.5,116.7 173.4,116.7 173.4,117.0 174.3,117.0 174.3,115.5 175.2,115.5 175.2,148.6 176.1,148.6 176.1,148.9 177.0,148.9 177.0,149.0 177.9,149.0 177.9,149.7 178.9,149.7 178.9,149.8 179.8,149.8 179.8,149.1 180.7,149.1 180.7,150.6 181.6,150.6 181.6,150.2 182.5,150.2 182.5,150.9 183.4,150.9 183.4,149.0 184.3,149.0 184.3,151.3 185.2,151.3 185.2,152.7 186.1,152.7 186.1,151.3 187.0,151.3 187.0,151.3 187.9,151.3 187.9,152.0 188.8,152.0 188.8,150.1 189.7,150.1 189.7,152.3 190.6,152.3 190.6,150.0 191.5,150.0 191.5,151.0 192.4,151.0 192.4,152.0 193.3,152.0 193.3,150.2 194.2,150.2 194.2,151.2 195.1,151.2 195.1,152.1 196.1,152.1 196.1,151.2 197.0,151.2 197.0,150.9 197.9,150.9 197.9,151.2 198.8,151.2 198.8,150.0 199.7,150.0 199.7,151.6 200.6,151.6 200.6,153.1 201.5,153.1 201.5,150.5 202.4,150.5 202.4,151.4 203.3,151.4 203.3,152.4 204.2,152.4 204.2,151.5 205.1,151.5 205.1,151.1 206.0,151.1 206.0,152.5 206.9,152.5 206.9,152.5 207.8,152.5 207.8,152.8 208.7,152.8 208.7,151.0 209.6,151.0 209.6,152.8 210.5,152.8 210.5,152.6 211.4,152.6 211.4,150.3 212.3,150.3 212.3,152.5 213.2,152.5 213.2,151.5 214.2,151.5 214.2,150.7 215.1,150.7 215.1,152.2 216.0,152.2 216.0,152.3 216.9,152.3 216.9,152.4 217.8,152.4 217.8,154.0 218.7,154.0 218.7,151.8 219.6,151.8 219.6,153.9 220.5,153.9 220.5,151.2 221.4,151.2 221.4,152.5 222.3,152.5 222.3,150.7 223.2,150.7 223.2,151.6 224.1,151.6 224.1,151.9 225.0,151.9 225.0,152.1 225.9,152.1 225.9,153.1 226.8,153.1 226.8,151.2 227.7,151.2 227.7,153.9 228.6,153.9 228.6,151.0 229.5,151.0 229.5,151.8 230.4,151.8 230.4,151.7 231.3,151.7 231.3,153.0 232.3,153.0 232.3,151.0 233.2,151.0 233.2,151.5 234.1,151.5 234.1,152.8 235.0,152.8 235.0,119.6 235.9Line truncated
<circle class="nj-charts-json-dot" cx="94.7" cy="167.8" r="3.5"><title>#5233: 1.47&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="94.7" y1="54.0" x2="94.7" y2="162.8"/>
<text class="nj-charts-note" x="94.7" y="50.0" text-anchor="middle">#5233</text>
<circle class="nj-charts-json-dot" cx="139.0" cy="126.4" r="3.5"><title>#5501: 2.2&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="139.0" y1="54.0" x2="139.0" y2="121.4"/>
<text class="nj-charts-note" x="139.0" y="50.0" text-anchor="middle">#5501</text>
<circle class="nj-charts-json-dot" cx="175.2" cy="148.6" r="3.5"><title>#5185: 1.76&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="175.2" y1="41.0" x2="175.2" y2="143.6"/>
<text class="nj-charts-note" x="175.2" y="37.0" text-anchor="middle">#5185</text>
<circle class="nj-charts-json-dot" cx="235.0" cy="119.6" r="3.5"><title>#5746: 2.3&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="235.0" y1="54.0" x2="235.0" y2="114.6"/>
<text class="nj-charts-note" x="235.0" y="50.0" text-anchor="middle">#5746</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 9.9 KiB

@@ -0,0 +1,37 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-parse_canada nj-blog-tl-parse_canada-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-parse_canada">parse() canada.json: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-parse_canada-d">parse() canada.json: speedup over 3.12.0 at each of 317 commits, ending at 2.8x. Labeled steps: #5234, #5283, #5617, #5738, #5851, #5882.</desc>
<text class="nj-charts-panel" x="8" y="18">parse() canada.json</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="135" x2="330" y2="135"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="139">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">4&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,208.2 45.8,208.2 45.8,208.1 46.7,208.1 46.7,207.7 47.6,207.7 47.6,207.2 48.5,207.2 48.5,208.0 49.4,208.0 49.4,208.2 50.3,208.2 50.3,207.8 51.2,207.8 51.2,208.5 52.1,208.5 52.1,207.3 53.1,207.3 53.1,208.0 54.0,208.0 54.0,209.2 54.9,209.2 54.9,208.5 55.8,208.5 55.8,208.1 56.7,208.1 56.7,207.7 57.6,207.7 57.6,209.8 58.5,209.8 58.5,208.6 59.4,208.6 59.4,208.1 60.3,208.1 60.3,208.1 61.2,208.1 61.2,207.0 62.1,207.0 62.1,207.8 63.0,207.8 63.0,207.6 63.9,207.6 63.9,208.1 64.8,208.1 64.8,208.4 65.7,208.4 65.7,207.7 66.6,207.7 66.6,207.4 67.5,207.4 67.5,208.3 68.4,208.3 68.4,207.3 69.3,207.3 69.3,207.8 70.2,207.8 70.2,207.9 71.2,207.9 71.2,207.9 72.1,207.9 72.1,207.4 73.0,207.4 73.0,207.5 73.9,207.5 73.9,208.1 74.8,208.1 74.8,208.1 75.7,208.1 75.7,207.9 76.6,207.9 76.6,207.8 77.5,207.8 77.5,207.0 78.4,207.0 78.4,207.6 79.3,207.6 79.3,207.8 80.2,207.8 80.2,207.3 81.1,207.3 81.1,207.6 82.0,207.6 82.0,208.6 82.9,208.6 82.9,206.5 83.8,206.5 83.8,208.0 84.7,208.0 84.7,208.7 85.6,208.7 85.6,208.0 86.5,208.0 86.5,207.7 87.4,207.7 87.4,208.4 88.3,208.4 88.3,207.9 89.3,207.9 89.3,207.5 90.2,207.5 90.2,207.9 91.1,207.9 91.1,207.4 92.0,207.4 92.0,207.5 92.9,207.5 92.9,208.5 93.8,208.5 93.8,197.2 94.7,197.2 94.7,197.7 95.6,197.7 95.6,196.5 96.5,196.5 96.5,196.7 97.4,196.7 97.4,197.5 98.3,197.5 98.3,195.7 99.2,195.7 99.2,197.0 100.1,197.0 100.1,197.8 101.0,197.8 101.0,197.1 101.9,197.1 101.9,196.7 102.8,196.7 102.8,197.2 103.7,197.2 103.7,197.0 104.6,197.0 104.6,197.4 105.5,197.4 105.5,197.4 106.4,197.4 106.4,197.3 107.4,197.3 107.4,196.6 108.3,196.6 108.3,197.6 109.2,197.6 109.2,197.6 110.1,197.6 110.1,197.3 111.0,197.3 111.0,197.3 111.9,197.3 111.9,197.3 112.8,197.3 112.8,197.8 113.7,197.8 113.7,197.7 114.6,197.7 114.6,195.0 115.5,195.0 115.5,196.3 116.4,196.3 116.4,197.6 117.3,197.6 117.3,196.7 118.2,196.7 118.2,196.0 119.1,196.0 119.1,196.5 120.0,196.5 120.0,196.2 120.9,196.2 120.9,196.3 121.8,196.3 121.8,197.4 122.7,197.4 122.7,197.3 123.6,197.3 123.6,197.8 124.6,197.8 124.6,195.5 125.5,195.5 125.5,197.2 126.4,197.2 126.4,197.3 127.3,197.3 127.3,196.4 128.2,196.4 128.2,196.7 129.1,196.7 129.1,197.7 130.0,197.7 130.0,196.7 130.9,196.7 130.9,196.9 131.8,196.9 131.8,196.3 132.7,196.3 132.7,196.7 133.6,196.7 133.6,196.9 134.5,196.9 134.5,197.3 135.4,197.3 135.4,197.5 136.3,197.5 136.3,196.9 137.2,196.9 137.2,197.3 138.1,197.3 138.1,197.0 139.0,197.0 139.0,196.7 139.9,196.7 139.9,167.5 140.8,167.5 140.8,168.6 141.7,168.6 141.7,169.1 142.7,169.1 142.7,167.8 143.6,167.8 143.6,167.2 144.5,167.2 144.5,168.9 145.4,168.9 145.4,167.6 146.3,167.6 146.3,167.2 147.2,167.2 147.2,168.6 148.1,168.6 148.1,169.8 149.0,169.8 149.0,167.0 149.9,167.0 149.9,169.1 150.8,169.1 150.8,168.0 151.7,168.0 151.7,169.0 152.6,169.0 152.6,167.3 153.5,167.3 153.5,168.7 154.4,168.7 154.4,169.0 155.3,169.0 155.3,168.7 156.2,168.7 156.2,168.0 157.1,168.0 157.1,166.0 158.0,166.0 158.0,168.7 158.9,168.7 158.9,167.5 159.8,167.5 159.8,169.2 160.8,169.2 160.8,168.7 161.7,168.7 161.7,167.6 162.6,167.6 162.6,168.2 163.5,168.2 163.5,171.3 164.4,171.3 164.4,170.2 165.3,170.2 165.3,168.0 166.2,168.0 166.2,166.7 167.1,166.7 167.1,168.2 168.0,168.2 168.0,168.0 168.9,168.0 168.9,167.4 169.8,167.4 169.8,167.6 170.7,167.6 170.7,167.5 171.6,167.5 171.6,167.9 172.5,167.9 172.5,167.4 173.4,167.4 173.4,169.8 174.3,169.8 174.3,168.8 175.2,168.8 175.2,166.9 176.1,166.9 176.1,167.7 177.0,167.7 177.0,167.4 177.9,167.4 177.9,167.3 178.9,167.3 178.9,168.8 179.8,168.8 179.8,168.0 180.7,168.0 180.7,167.4 181.6,167.4 181.6,167.5 182.5,167.5 182.5,168.3 183.4,168.3 183.4,168.0 184.3,168.0 184.3,169.1 185.2,169.1 185.2,177.1 186.1,177.1 186.1,176.5 187.0,176.5 187.0,166.6 187.9,166.6 187.9,167.4 188.8,167.4 188.8,167.6 189.7,167.6 189.7,167.4 190.6,167.4 190.6,167.7 191.5,167.7 191.5,166.9 192.4,166.9 192.4,167.1 193.3,167.1 193.3,166.5 194.2,166.5 194.2,166.3 195.1,166.3 195.1,167.0 196.1,167.0 196.1,167.2 197.0,167.2 197.0,167.0 197.9,167.0 197.9,167.0 198.8,167.0 198.8,166.4 199.7,166.4 199.7,165.4 200.6,165.4 200.6,168.1 201.5,168.1 201.5,167.5 202.4,167.5 202.4,167.3 203.3,167.3 203.3,167.0 204.2,167.0 204.2,166.2 205.1,166.2 205.1,166.8 206.0,166.8 206.0,167.1 206.9,167.1 206.9,167.4 207.8,167.4 207.8,167.9 208.7,167.9 208.7,165.4 209.6,165.4 209.6,168.3 210.5,168.3 210.5,168.2 211.4,168.2 211.4,166.1 212.3,166.1 212.3,167.4 213.2,167.4 213.2,166.0 214.2,166.0 214.2,168.3 215.1,168.3 215.1,167.8 216.0,167.8 216.0,166.3 216.9,166.3 216.9,167.3 217.8,167.3 217.8,167.3 218.7,167.3 218.7,165.8 219.6,165.8 219.6,167.6 220.5,167.6 220.5,164.5 221.4,164.5 221.4,165.8 222.3,165.8 222.3,165.4 223.2,165.4 223.2,166.3 224.1,166.3 224.1,166.5 225.0,166.5 225.0,166.0 225.9,166.0 225.9,166.2 226.8,166.2 226.8,165.4 227.7,165.4 227.7,165.0 228.6,165.0 228.6,167.3 229.5,167.3 229.5,166.0 230.4,166.0 230.4,165.5 231.3,165.5 231.3,165.3 232.3,165.3 232.3,164.9 233.2,164.9 233.2,166.2 234.1,166.2 234.1,166.1 235.0,166.1 235.0,166.1 235.9Line truncated
<circle class="nj-charts-json-dot" cx="93.8" cy="197.2" r="3.5"><title>#5234: 1.11&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="93.8" y1="54.0" x2="93.8" y2="192.2"/>
<text class="nj-charts-note" x="93.8" y="50.0" text-anchor="middle">#5234</text>
<circle class="nj-charts-json-dot" cx="139.9" cy="167.5" r="3.5"><title>#5283: 1.47&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="139.9" y1="54.0" x2="139.9" y2="162.5"/>
<text class="nj-charts-note" x="139.9" y="50.0" text-anchor="middle">#5283</text>
<circle class="nj-charts-json-dot" cx="187.0" cy="166.6" r="3.5"><title>#5617: 1.48&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="187.0" y1="54.0" x2="187.0" y2="161.6"/>
<text class="nj-charts-note" x="187.0" y="50.0" text-anchor="middle">#5617</text>
<circle class="nj-charts-json-dot" cx="268.5" cy="137.9" r="3.5"><title>#5738: 1.95&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="268.5" y1="54.0" x2="268.5" y2="132.9"/>
<text class="nj-charts-note" x="268.5" y="50.0" text-anchor="middle">#5738</text>
<circle class="nj-charts-json-dot" cx="297.4" cy="125.1" r="3.5"><title>#5851: 2.2&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="297.4" y1="41.0" x2="297.4" y2="120.1"/>
<text class="nj-charts-note" x="297.4" y="37.0" text-anchor="middle">#5851</text>
<circle class="nj-charts-json-dot" cx="312.8" cy="99.2" r="3.5"><title>#5882: 2.8&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="312.8" y1="54.0" x2="312.8" y2="94.2"/>
<text class="nj-charts-note" x="312.0" y="50.0" text-anchor="middle">#5882</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,37 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-parse_citm nj-blog-tl-parse_citm-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-parse_citm">parse() citm_catalog.json: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-parse_citm-d">parse() citm_catalog.json: speedup over 3.12.0 at each of 317 commits, ending at 3.3x. Labeled steps: #5234, #5283, #5592, #5809, #5810, #5882.</desc>
<text class="nj-charts-panel" x="8" y="18">parse() citm_catalog.json</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="159.333" x2="330" y2="159.333"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="163.333">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="110.667" x2="330" y2="110.667"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="114.667">4&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">8&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,208.2 45.8,208.2 45.8,207.8 46.7,207.8 46.7,207.5 47.6,207.5 47.6,206.4 48.5,206.4 48.5,208.3 49.4,208.3 49.4,207.3 50.3,207.3 50.3,206.8 51.2,206.8 51.2,207.8 52.1,207.8 52.1,206.8 53.1,206.8 53.1,207.3 54.0,207.3 54.0,207.6 54.9,207.6 54.9,207.8 55.8,207.8 55.8,207.0 56.7,207.0 56.7,207.2 57.6,207.2 57.6,207.2 58.5,207.2 58.5,207.3 59.4,207.3 59.4,207.5 60.3,207.5 60.3,207.4 61.2,207.4 61.2,206.5 62.1,206.5 62.1,207.6 63.0,207.6 63.0,207.6 63.9,207.6 63.9,207.1 64.8,207.1 64.8,207.0 65.7,207.0 65.7,207.6 66.6,207.6 66.6,206.6 67.5,206.6 67.5,207.3 68.4,207.3 68.4,206.8 69.3,206.8 69.3,207.3 70.2,207.3 70.2,207.7 71.2,207.7 71.2,207.9 72.1,207.9 72.1,207.2 73.0,207.2 73.0,206.4 73.9,206.4 73.9,207.6 74.8,207.6 74.8,206.4 75.7,206.4 75.7,208.0 76.6,208.0 76.6,207.4 77.5,207.4 77.5,206.4 78.4,206.4 78.4,207.5 79.3,207.5 79.3,207.1 80.2,207.1 80.2,206.9 81.1,206.9 81.1,207.6 82.0,207.6 82.0,207.6 82.9,207.6 82.9,207.0 83.8,207.0 83.8,206.8 84.7,206.8 84.7,208.0 85.6,208.0 85.6,207.6 86.5,207.6 86.5,206.6 87.4,206.6 87.4,206.7 88.3,206.7 88.3,207.3 89.3,207.3 89.3,206.9 90.2,206.9 90.2,207.6 91.1,207.6 91.1,207.1 92.0,207.1 92.0,207.6 92.9,207.6 92.9,207.9 93.8,207.9 93.8,177.3 94.7,177.3 94.7,176.7 95.6,176.7 95.6,176.9 96.5,176.9 96.5,176.8 97.4,176.8 97.4,176.8 98.3,176.8 98.3,176.2 99.2,176.2 99.2,176.0 100.1,176.0 100.1,176.8 101.0,176.8 101.0,176.7 101.9,176.7 101.9,175.9 102.8,175.9 102.8,177.0 103.7,177.0 103.7,176.1 104.6,176.1 104.6,175.9 105.5,175.9 105.5,177.2 106.4,177.2 106.4,177.2 107.4,177.2 107.4,176.7 108.3,176.7 108.3,176.8 109.2,176.8 109.2,176.6 110.1,176.6 110.1,176.8 111.0,176.8 111.0,177.0 111.9,177.0 111.9,176.7 112.8,176.7 112.8,177.1 113.7,177.1 113.7,176.8 114.6,176.8 114.6,175.0 115.5,175.0 115.5,176.6 116.4,176.6 116.4,176.7 117.3,176.7 117.3,176.8 118.2,176.8 118.2,176.0 119.1,176.0 119.1,176.6 120.0,176.6 120.0,176.5 120.9,176.5 120.9,176.5 121.8,176.5 121.8,176.0 122.7,176.0 122.7,177.1 123.6,177.1 123.6,176.4 124.6,176.4 124.6,175.7 125.5,175.7 125.5,176.6 126.4,176.6 126.4,176.6 127.3,176.6 127.3,176.6 128.2,176.6 128.2,176.6 129.1,176.6 129.1,176.2 130.0,176.2 130.0,176.9 130.9,176.9 130.9,175.3 131.8,175.3 131.8,176.1 132.7,176.1 132.7,176.6 133.6,176.6 133.6,176.9 134.5,176.9 134.5,176.9 135.4,176.9 135.4,177.0 136.3,177.0 136.3,176.4 137.2,176.4 137.2,176.6 138.1,176.6 138.1,176.6 139.0,176.6 139.0,176.3 139.9,176.3 139.9,161.2 140.8,161.2 140.8,160.9 141.7,160.9 141.7,160.4 142.7,160.4 142.7,160.9 143.6,160.9 143.6,159.8 144.5,159.8 144.5,159.4 145.4,159.4 145.4,158.0 146.3,158.0 146.3,158.9 147.2,158.9 147.2,159.0 148.1,159.0 148.1,159.3 149.0,159.3 149.0,158.1 149.9,158.1 149.9,159.1 150.8,159.1 150.8,158.2 151.7,158.2 151.7,158.6 152.6,158.6 152.6,158.5 153.5,158.5 153.5,158.8 154.4,158.8 154.4,158.3 155.3,158.3 155.3,159.4 156.2,159.4 156.2,158.8 157.1,158.8 157.1,157.7 158.0,157.7 158.0,158.4 158.9,158.4 158.9,158.5 159.8,158.5 159.8,159.2 160.8,159.2 160.8,158.9 161.7,158.9 161.7,158.6 162.6,158.6 162.6,159.1 163.5,159.1 163.5,159.2 164.4,159.2 164.4,159.2 165.3,159.2 165.3,158.3 166.2,158.3 166.2,158.2 167.1,158.2 167.1,158.7 168.0,158.7 168.0,158.9 168.9,158.9 168.9,158.7 169.8,158.7 169.8,159.2 170.7,159.2 170.7,158.5 171.6,158.5 171.6,158.9 172.5,158.9 172.5,158.7 173.4,158.7 173.4,159.5 174.3,159.5 174.3,157.8 175.2,157.8 175.2,158.3 176.1,158.3 176.1,157.8 177.0,157.8 177.0,158.6 177.9,158.6 177.9,158.0 178.9,158.0 178.9,158.3 179.8,158.3 179.8,158.1 180.7,158.1 180.7,158.7 181.6,158.7 181.6,157.7 182.5,157.7 182.5,158.0 183.4,158.0 183.4,158.9 184.3,158.9 184.3,160.0 185.2,160.0 185.2,160.4 186.1,160.4 186.1,160.3 187.0,160.3 187.0,158.9 187.9,158.9 187.9,159.7 188.8,159.7 188.8,159.9 189.7,159.9 189.7,160.4 190.6,160.4 190.6,159.8 191.5,159.8 191.5,159.1 192.4,159.1 192.4,160.3 193.3,160.3 193.3,159.3 194.2,159.3 194.2,160.1 195.1,160.1 195.1,160.0 196.1,160.0 196.1,160.0 197.0,160.0 197.0,160.4 197.9,160.4 197.9,159.9 198.8,159.9 198.8,158.7 199.7,158.7 199.7,159.6 200.6,159.6 200.6,160.2 201.5,160.2 201.5,159.3 202.4,159.3 202.4,160.6 203.3,160.6 203.3,160.1 204.2,160.1 204.2,158.7 205.1,158.7 205.1,159.2 206.0,159.2 206.0,160.7 206.9,160.7 206.9,160.2 207.8,160.2 207.8,160.7 208.7,160.7 208.7,160.0 209.6,160.0 209.6,161.1 210.5,161.1 210.5,160.8 211.4,160.8 211.4,159.5 212.3,159.5 212.3,160.2 213.2,160.2 213.2,160.1 214.2,160.1 214.2,161.0 215.1,161.0 215.1,160.1 216.0,160.1 216.0,160.1 216.9,160.1 216.9,160.5 217.8,160.5 217.8,159.4 218.7,159.4 218.7,159.2 219.6,159.2 219.6,159.1 220.5,159.1 220.5,157.9 221.4,157.9 221.4,158.1 222.3,158.1 222.3,159.0 223.2,159.0 223.2,158.7 224.1,158.7 224.1,159.0 225.0,159.0 225.0,159.1 225.9,159.1 225.9,159.5 226.8,159.5 226.8,158.5 227.7,158.5 227.7,158.0 228.6,158.0 228.6,159.2 229.5,159.2 229.5,158.8 230.4,158.8 230.4,159.4 231.3,159.4 231.3,159.3 232.3,159.3 232.3,158.1 233.2,158.1 233.2,159.7 234.1,159.7 234.1,160.0 235.0,160.0 235.0,159.7 235.9Line truncated
<circle class="nj-charts-json-dot" cx="93.8" cy="177.3" r="3.5"><title>#5234: 1.55&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="93.8" y1="54.0" x2="93.8" y2="172.3"/>
<text class="nj-charts-note" x="93.8" y="50.0" text-anchor="middle">#5234</text>
<circle class="nj-charts-json-dot" cx="139.9" cy="161.2" r="3.5"><title>#5283: 1.95&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="139.9" y1="54.0" x2="139.9" y2="156.2"/>
<text class="nj-charts-note" x="139.9" y="50.0" text-anchor="middle">#5283</text>
<circle class="nj-charts-json-dot" cx="243.1" cy="153.9" r="3.5"><title>#5592: 2.2&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="243.1" y1="54.0" x2="243.1" y2="148.9"/>
<text class="nj-charts-note" x="243.1" y="50.0" text-anchor="middle">#5592</text>
<circle class="nj-charts-json-dot" cx="265.7" cy="145.4" r="3.5"><title>#5809: 2.4&#215;</title></circle>
<circle class="nj-charts-json-dot" cx="266.6" cy="136.3" r="3.5"><title>#5810: 2.8&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="266.6" y1="41.0" x2="266.6" y2="131.3"/>
<text class="nj-charts-note" x="266.6" y="37.0" text-anchor="middle">#5809, #5810</text>
<circle class="nj-charts-json-dot" cx="312.8" cy="123.3" r="3.5"><title>#5882: 3.3&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="312.8" y1="54.0" x2="312.8" y2="118.3"/>
<text class="nj-charts-note" x="312.0" y="50.0" text-anchor="middle">#5882</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,25 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-parse_twitter_ifstream nj-blog-tl-parse_twitter_ifstream-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-parse_twitter_ifstream">parse() twitter.json, std::ifstream: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-parse_twitter_ifstream-d">parse() twitter.json, std::ifstream: speedup over 3.12.0 at each of 317 commits, ending at 2.2x. Labeled steps: #5828, #5884.</desc>
<text class="nj-charts-panel" x="8" y="18">parse() twitter.json, std::ifstream</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="135" x2="330" y2="135"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="139">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">4&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,209.3 45.8,209.3 45.8,206.6 46.7,206.6 46.7,208.4 47.6,208.4 47.6,205.9 48.5,205.9 48.5,209.4 49.4,209.4 49.4,208.3 50.3,208.3 50.3,207.9 51.2,207.9 51.2,209.5 52.1,209.5 52.1,205.7 53.1,205.7 53.1,206.7 54.0,206.7 54.0,209.2 54.9,209.2 54.9,208.2 55.8,208.2 55.8,206.9 56.7,206.9 56.7,207.4 57.6,207.4 57.6,207.4 58.5,207.4 58.5,207.5 59.4,207.5 59.4,206.9 60.3,206.9 60.3,206.9 61.2,206.9 61.2,208.4 62.1,208.4 62.1,208.6 63.0,208.6 63.0,209.2 63.9,209.2 63.9,207.7 64.8,207.7 64.8,207.0 65.7,207.0 65.7,206.7 66.6,206.7 66.6,206.0 67.5,206.0 67.5,207.5 68.4,207.5 68.4,206.3 69.3,206.3 69.3,206.7 70.2,206.7 70.2,206.7 71.2,206.7 71.2,209.3 72.1,209.3 72.1,206.7 73.0,206.7 73.0,206.8 73.9,206.8 73.9,206.6 74.8,206.6 74.8,206.8 75.7,206.8 75.7,209.2 76.6,209.2 76.6,207.6 77.5,207.6 77.5,205.9 78.4,205.9 78.4,209.1 79.3,209.1 79.3,206.6 80.2,206.6 80.2,207.4 81.1,207.4 81.1,206.9 82.0,206.9 82.0,209.1 82.9,209.1 82.9,205.7 83.8,205.7 83.8,206.9 84.7,206.9 84.7,208.0 85.6,208.0 85.6,208.5 86.5,208.5 86.5,206.5 87.4,206.5 87.4,206.0 88.3,206.0 88.3,207.9 89.3,207.9 89.3,206.6 90.2,206.6 90.2,206.5 91.1,206.5 91.1,206.5 92.0,206.5 92.0,207.1 92.9,207.1 92.9,208.0 93.8,208.0 93.8,208.1 94.7,208.1 94.7,206.7 95.6,206.7 95.6,207.8 96.5,207.8 96.5,206.6 97.4,206.6 97.4,208.1 98.3,208.1 98.3,205.9 99.2,205.9 99.2,206.7 100.1,206.7 100.1,208.0 101.0,208.0 101.0,207.3 101.9,207.3 101.9,206.7 102.8,206.7 102.8,209.3 103.7,209.3 103.7,206.5 104.6,206.5 104.6,207.2 105.5,207.2 105.5,208.2 106.4,208.2 106.4,209.3 107.4,209.3 107.4,207.8 108.3,207.8 108.3,206.9 109.2,206.9 109.2,206.7 110.1,206.7 110.1,209.0 111.0,209.0 111.0,208.0 111.9,208.0 111.9,208.1 112.8,208.1 112.8,209.2 113.7,209.2 113.7,208.1 114.6,208.1 114.6,205.5 115.5,205.5 115.5,206.9 116.4,206.9 116.4,206.6 117.3,206.6 117.3,207.8 118.2,207.8 118.2,206.7 119.1,206.7 119.1,206.9 120.0,206.9 120.0,206.7 120.9,206.7 120.9,206.3 121.8,206.3 121.8,207.1 122.7,207.1 122.7,208.6 123.6,208.6 123.6,208.7 124.6,208.7 124.6,206.6 125.5,206.6 125.5,209.3 126.4,209.3 126.4,208.0 127.3,208.0 127.3,206.7 128.2,206.7 128.2,207.7 129.1,207.7 129.1,206.9 130.0,206.9 130.0,208.9 130.9,208.9 130.9,208.7 131.8,208.7 131.8,207.7 132.7,207.7 132.7,208.8 133.6,208.8 133.6,207.8 134.5,207.8 134.5,210.4 135.4,210.4 135.4,209.0 136.3,209.0 136.3,209.0 137.2,209.0 137.2,209.1 138.1,209.1 138.1,208.0 139.0,208.0 139.0,207.7 139.9,207.7 139.9,208.3 140.8,208.3 140.8,207.0 141.7,207.0 141.7,207.2 142.7,207.2 142.7,207.5 143.6,207.5 143.6,208.0 144.5,208.0 144.5,207.6 145.4,207.6 145.4,207.9 146.3,207.9 146.3,207.6 147.2,207.6 147.2,208.5 148.1,208.5 148.1,209.2 149.0,209.2 149.0,206.3 149.9,206.3 149.9,208.8 150.8,208.8 150.8,207.6 151.7,207.6 151.7,207.4 152.6,207.4 152.6,207.5 153.5,207.5 153.5,209.0 154.4,209.0 154.4,206.8 155.3,206.8 155.3,209.3 156.2,209.3 156.2,210.0 157.1,210.0 157.1,207.0 158.0,207.0 158.0,207.3 158.9,207.3 158.9,208.1 159.8,208.1 159.8,209.8 160.8,209.8 160.8,208.1 161.7,208.1 161.7,208.1 162.6,208.1 162.6,208.6 163.5,208.6 163.5,208.2 164.4,208.2 164.4,210.4 165.3,210.4 165.3,207.7 166.2,207.7 166.2,207.2 167.1,207.2 167.1,206.6 168.0,206.6 168.0,210.0 168.9,210.0 168.9,206.5 169.8,206.5 169.8,210.0 170.7,210.0 170.7,208.8 171.6,208.8 171.6,208.8 172.5,208.8 172.5,209.5 173.4,209.5 173.4,208.9 174.3,208.9 174.3,206.3 175.2,206.3 175.2,207.2 176.1,207.2 176.1,206.9 177.0,206.9 177.0,207.2 177.9,207.2 177.9,206.5 178.9,206.5 178.9,207.1 179.8,207.1 179.8,208.0 180.7,208.0 180.7,207.6 181.6,207.6 181.6,207.9 182.5,207.9 182.5,207.5 183.4,207.5 183.4,205.6 184.3,205.6 184.3,208.0 185.2,208.0 185.2,209.7 186.1,209.7 186.1,207.9 187.0,207.9 187.0,207.6 187.9,207.6 187.9,208.5 188.8,208.5 188.8,207.9 189.7,207.9 189.7,210.5 190.6,210.5 190.6,206.7 191.5,206.7 191.5,207.7 192.4,207.7 192.4,209.2 193.3,209.2 193.3,206.7 194.2,206.7 194.2,209.3 195.1,209.3 195.1,208.0 196.1,208.0 196.1,208.0 197.0,208.0 197.0,207.9 197.9,207.9 197.9,208.2 198.8,208.2 198.8,207.9 199.7,207.9 199.7,207.0 200.6,207.0 200.6,208.8 201.5,208.8 201.5,208.0 202.4,208.0 202.4,208.0 203.3,208.0 203.3,209.2 204.2,209.2 204.2,206.7 205.1,206.7 205.1,206.6 206.0,206.6 206.0,208.5 206.9,208.5 206.9,208.9 207.8,208.9 207.8,210.1 208.7,210.1 208.7,206.4 209.6,206.4 209.6,209.4 210.5,209.4 210.5,210.1 211.4,210.1 211.4,207.6 212.3,207.6 212.3,209.5 213.2,209.5 213.2,208.7 214.2,208.7 214.2,211.2 215.1,211.2 215.1,210.1 216.0,210.1 216.0,211.8 216.9,211.8 216.9,208.8 217.8,208.8 217.8,209.2 218.7,209.2 218.7,207.7 219.6,207.7 219.6,208.1 220.5,208.1 220.5,206.3 221.4,206.3 221.4,206.7 222.3,206.7 222.3,207.3 223.2,207.3 223.2,207.8 224.1,207.8 224.1,208.7 225.0,208.7 225.0,210.1 225.9,210.1 225.9,210.1 226.8,210.1 226.8,206.8 227.7,206.8 227.7,209.0 228.6,209.0 228.6,208.7 229.5,208.7 229.5,209.2 230.4,209.2 230.4,208.6 231.3,208.6 231.3,209.0 232.3,209.0 232.3,207.3 233.2,207.3 233.2,209.0 234.1,209.0 234.1,209.3 235.0,209.3 235.0,207.3 235.9Line truncated
<circle class="nj-charts-json-dot" cx="287.5" cy="136.8" r="3.5"><title>#5828: 1.97&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="287.5" y1="54.0" x2="287.5" y2="131.8"/>
<text class="nj-charts-note" x="287.5" y="50.0" text-anchor="middle">#5828</text>
<circle class="nj-charts-json-dot" cx="306.5" cy="124.7" r="3.5"><title>#5884: 2.2&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="306.5" y1="41.0" x2="306.5" y2="119.7"/>
<text class="nj-charts-note" x="306.5" y="37.0" text-anchor="middle">#5884</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

@@ -0,0 +1,30 @@
<svg class="nj-fig nj-charts nj-charts--timeline" viewBox="0 0 340 252" role="img" aria-labelledby="nj-blog-tl-to_cbor_twitter nj-blog-tl-to_cbor_twitter-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-tl-to_cbor_twitter">to_cbor() twitter: speedup over 3.12.0 by commit</title>
<desc id="nj-blog-tl-to_cbor_twitter-d">to_cbor() twitter: speedup over 3.12.0 at each of 317 commits, ending at 5.0x. Labeled steps: #5328, #5824, #5885.</desc>
<text class="nj-charts-panel" x="8" y="18">to_cbor() twitter</text>
<line class="nj-charts-grid" x1="44" y1="208" x2="330" y2="208"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="212">1&#215;</text>
<line class="nj-charts-grid" x1="44" y1="159.333" x2="330" y2="159.333"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="163.333">2&#215;</text>
<line class="nj-charts-grid" x1="44" y1="110.667" x2="330" y2="110.667"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="114.667">4&#215;</text>
<line class="nj-charts-grid" x1="44" y1="62" x2="330" y2="62"/>
<text class="nj-charts-tick nj-charts-tick--r" x="38" y="66">8&#215;</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="44" y2="212"/>
<text class="nj-charts-tick" x="44" y="225">3.12.0</text>
<line class="nj-charts-axis" x1="126.361" y1="208" x2="126.361" y2="212"/>
<text class="nj-charts-tick" x="126.361" y="225">Sep 2026</text>
<line class="nj-charts-axis" x1="215.962" y1="208" x2="215.962" y2="212"/>
<text class="nj-charts-tick" x="215.962" y="225">Oct</text>
<polyline class="nj-charts-json" points="44.0,208.0 44.9,208.0 44.9,208.0 45.8,208.0 45.8,204.8 46.7,204.8 46.7,205.2 47.6,205.2 47.6,204.6 48.5,204.6 48.5,205.2 49.4,205.2 49.4,204.9 50.3,204.9 50.3,204.8 51.2,204.8 51.2,205.0 52.1,205.0 52.1,204.9 53.1,204.9 53.1,205.0 54.0,205.0 54.0,205.3 54.9,205.3 54.9,204.9 55.8,204.9 55.8,205.1 56.7,205.1 56.7,211.7 57.6,211.7 57.6,206.7 58.5,206.7 58.5,204.1 59.4,204.1 59.4,207.6 60.3,207.6 60.3,204.0 61.2,204.0 61.2,203.7 62.1,203.7 62.1,204.0 63.0,204.0 63.0,205.0 63.9,205.0 63.9,204.3 64.8,204.3 64.8,204.2 65.7,204.2 65.7,203.9 66.6,203.9 66.6,202.6 67.5,202.6 67.5,204.1 68.4,204.1 68.4,204.1 69.3,204.1 69.3,204.1 70.2,204.1 70.2,204.3 71.2,204.3 71.2,206.1 72.1,206.1 72.1,204.2 73.0,204.2 73.0,204.0 73.9,204.0 73.9,204.2 74.8,204.2 74.8,204.2 75.7,204.2 75.7,204.2 76.6,204.2 76.6,204.1 77.5,204.1 77.5,204.3 78.4,204.3 78.4,204.7 79.3,204.7 79.3,208.4 80.2,208.4 80.2,203.4 81.1,203.4 81.1,206.1 82.0,206.1 82.0,204.3 82.9,204.3 82.9,203.1 83.8,203.1 83.8,204.0 84.7,204.0 84.7,204.1 85.6,204.1 85.6,203.6 86.5,203.6 86.5,204.1 87.4,204.1 87.4,203.0 88.3,203.0 88.3,204.1 89.3,204.1 89.3,204.3 90.2,204.3 90.2,203.6 91.1,203.6 91.1,204.3 92.0,204.3 92.0,204.6 92.9,204.6 92.9,204.5 93.8,204.5 93.8,208.7 94.7,208.7 94.7,205.1 95.6,205.1 95.6,205.4 96.5,205.4 96.5,205.1 97.4,205.1 97.4,205.2 98.3,205.2 98.3,206.8 99.2,206.8 99.2,204.5 100.1,204.5 100.1,205.4 101.0,205.4 101.0,205.0 101.9,205.0 101.9,204.6 102.8,204.6 102.8,205.6 103.7,205.6 103.7,204.9 104.6,204.9 104.6,204.8 105.5,204.8 105.5,206.4 106.4,206.4 106.4,205.5 107.4,205.5 107.4,204.9 108.3,204.9 108.3,209.3 109.2,209.3 109.2,205.8 110.1,205.8 110.1,206.4 111.0,206.4 111.0,205.5 111.9,205.5 111.9,205.2 112.8,205.2 112.8,205.3 113.7,205.3 113.7,205.1 114.6,205.1 114.6,204.3 115.5,204.3 115.5,204.5 116.4,204.5 116.4,204.7 117.3,204.7 117.3,204.0 118.2,204.0 118.2,203.6 119.1,203.6 119.1,204.3 120.0,204.3 120.0,202.7 120.9,202.7 120.9,203.3 121.8,203.3 121.8,203.9 122.7,203.9 122.7,204.2 123.6,204.2 123.6,202.8 124.6,202.8 124.6,203.1 125.5,203.1 125.5,204.3 126.4,204.3 126.4,204.4 127.3,204.4 127.3,204.0 128.2,204.0 128.2,208.3 129.1,208.3 129.1,203.7 130.0,203.7 130.0,206.9 130.9,206.9 130.9,205.9 131.8,205.9 131.8,205.4 132.7,205.4 132.7,205.2 133.6,205.2 133.6,205.8 134.5,205.8 134.5,205.3 135.4,205.3 135.4,205.6 136.3,205.6 136.3,206.3 137.2,206.3 137.2,205.4 138.1,205.4 138.1,204.7 139.0,204.7 139.0,204.5 139.9,204.5 139.9,206.0 140.8,206.0 140.8,204.5 141.7,204.5 141.7,204.0 142.7,204.0 142.7,204.2 143.6,204.2 143.6,205.5 144.5,205.5 144.5,203.8 145.4,203.8 145.4,203.9 146.3,203.9 146.3,203.8 147.2,203.8 147.2,204.5 148.1,204.5 148.1,204.0 149.0,204.0 149.0,203.8 149.9,203.8 149.9,204.8 150.8,204.8 150.8,203.1 151.7,203.1 151.7,203.2 152.6,203.2 152.6,204.6 153.5,204.6 153.5,208.3 154.4,208.3 154.4,204.2 155.3,204.2 155.3,207.4 156.2,207.4 156.2,204.3 157.1,204.3 157.1,208.3 158.0,208.3 158.0,204.9 158.9,204.9 158.9,204.8 159.8,204.8 159.8,208.9 160.8,208.9 160.8,207.9 161.7,207.9 161.7,206.2 162.6,206.2 162.6,204.1 163.5,204.1 163.5,204.0 164.4,204.0 164.4,204.1 165.3,204.1 165.3,202.0 166.2,202.0 166.2,205.5 167.1,205.5 167.1,202.9 168.0,202.9 168.0,203.2 168.9,203.2 168.9,202.5 169.8,202.5 169.8,203.5 170.7,203.5 170.7,203.1 171.6,203.1 171.6,203.0 172.5,203.0 172.5,204.8 173.4,204.8 173.4,202.8 174.3,202.8 174.3,202.1 175.2,202.1 175.2,202.3 176.1,202.3 176.1,202.8 177.0,202.8 177.0,202.8 177.9,202.8 177.9,202.2 178.9,202.2 178.9,202.8 179.8,202.8 179.8,203.6 180.7,203.6 180.7,204.0 181.6,204.0 181.6,203.8 182.5,203.8 182.5,203.6 183.4,203.6 183.4,203.2 184.3,203.2 184.3,203.7 185.2,203.7 185.2,204.9 186.1,204.9 186.1,208.0 187.0,208.0 187.0,205.1 187.9,205.1 187.9,206.4 188.8,206.4 188.8,205.3 189.7,205.3 189.7,205.4 190.6,205.4 190.6,206.9 191.5,206.9 191.5,204.8 192.4,204.8 192.4,205.4 193.3,205.4 193.3,203.5 194.2,203.5 194.2,205.7 195.1,205.7 195.1,204.4 196.1,204.4 196.1,204.9 197.0,204.9 197.0,205.1 197.9,205.1 197.9,204.8 198.8,204.8 198.8,204.4 199.7,204.4 199.7,204.8 200.6,204.8 200.6,205.2 201.5,205.2 201.5,203.8 202.4,203.8 202.4,204.7 203.3,204.7 203.3,204.7 204.2,204.7 204.2,203.5 205.1,203.5 205.1,203.3 206.0,203.3 206.0,203.7 206.9,203.7 206.9,205.0 207.8,205.0 207.8,205.4 208.7,205.4 208.7,203.9 209.6,203.9 209.6,204.8 210.5,204.8 210.5,204.9 211.4,204.9 211.4,206.4 212.3,206.4 212.3,205.0 213.2,205.0 213.2,204.4 214.2,204.4 214.2,204.7 215.1,204.7 215.1,205.1 216.0,205.1 216.0,204.9 216.9,204.9 216.9,204.5 217.8,204.5 217.8,203.7 218.7,203.7 218.7,203.3 219.6,203.3 219.6,203.5 220.5,203.5 220.5,204.1 221.4,204.1 221.4,205.9 222.3,205.9 222.3,205.1 223.2,205.1 223.2,204.7 224.1,204.7 224.1,207.3 225.0,207.3 225.0,205.1 225.9,205.1 225.9,208.6 226.8,208.6 226.8,204.2 227.7,204.2 227.7,203.8 228.6,203.8 228.6,203.0 229.5,203.0 229.5,203.7 230.4,203.7 230.4,205.8 231.3,205.8 231.3,204.8 232.3,204.8 232.3,204.7 233.2,204.7 233.2,203.5 234.1,203.5 234.1,203.6 235.0,203.6 235.0,208.2 235.9Line truncated
<circle class="nj-charts-json-dot" cx="256.7" cy="152.2" r="3.5"><title>#5328: 2.2&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="256.7" y1="54.0" x2="256.7" y2="147.2"/>
<text class="nj-charts-note" x="256.7" y="50.0" text-anchor="middle">#5328</text>
<circle class="nj-charts-json-dot" cx="295.6" cy="121.0" r="3.5"><title>#5824: 3.5&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="295.6" y1="41.0" x2="295.6" y2="116.0"/>
<text class="nj-charts-note" x="295.6" y="37.0" text-anchor="middle">#5824</text>
<circle class="nj-charts-json-dot" cx="309.2" cy="93.7" r="3.5"><title>#5885: 5.1&#215;</title></circle>
<line class="nj-charts-grid" stroke-dasharray="2 2" x1="309.2" y1="54.0" x2="309.2" y2="88.7"/>
<text class="nj-charts-note" x="309.2" y="50.0" text-anchor="middle">#5885</text>
<line class="nj-charts-axis" x1="44" y1="208" x2="330" y2="208"/>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

@@ -0,0 +1,80 @@
<svg class="nj-fig nj-charts nj-charts--blog" viewBox="0 0 580 404" role="img" aria-labelledby="nj-blog-view nj-blog-view-d" xmlns="http://www.w3.org/2000/svg">
<title id="nj-blog-view">Parse throughput of json_document, yyjson, simdjson&#x27;s D&#8203;OM, Boost.J&#8203;SON and json::parse, Apple M1 Max</title>
<desc id="nj-blog-view-d">Grouped horizontal bars in GB/s. twitter.json json_document 3.48 GB/s; twitter.json yyjson 3.29 GB/s; twitter.json simdjson D&#8203;OM 2.92 GB/s; twitter.json Boost.J&#8203;SON 0.73 GB/s; twitter.json json::parse 0.46 GB/s; citm_catalog.json json_document 4.80 GB/s; citm_catalog.json yyjson 3.61 GB/s; citm_catalog.json simdjson D&#8203;OM 3.52 GB/s; citm_catalog.json Boost.J&#8203;SON 1.17 GB/s; citm_catalog.json json::parse 0.72 GB/s; canada.json json_document 2.35 GB/s; canada.json yyjson 1.51 GB/s; canada.json simdjson D&#8203;OM 1.07 GB/s; canada.json Boost.J&#8203;SON 0.90 GB/s; canada.json json::parse 0.38 GB/s; jeopardy.json json_document 2.45 GB/s; jeopardy.json yyjson 1.82 GB/s; jeopardy.json simdjson D&#8203;OM 2.07 GB/s; jeopardy.json Boost.J&#8203;SON 0.62 GB/s; jeopardy.json json::parse 0.33 GB/s.</desc>
<rect class="nj-charts-c1" x="8" y="10" width="12" height="12" rx="3"/>
<text class="nj-charts-leg" x="26" y="20.5">json_document</text>
<rect class="nj-charts-c2" x="138.2" y="10" width="12" height="12" rx="3"/>
<text class="nj-charts-leg" x="156.2" y="20.5">yyjson</text>
<rect class="nj-charts-c3" x="216.6" y="10" width="12" height="12" rx="3"/>
<text class="nj-charts-leg" x="234.6" y="20.5">simdjson D&#8203;OM</text>
<rect class="nj-charts-c4" x="339.4" y="10" width="12" height="12" rx="3"/>
<text class="nj-charts-leg" x="357.4" y="20.5">Boost.J&#8203;SON</text>
<rect class="nj-charts-c5" x="447.4" y="10" width="12" height="12" rx="3"/>
<text class="nj-charts-leg" x="465.4" y="20.5">json::parse</text>
<line class="nj-charts-grid" x1="182.3" y1="40" x2="182.3" y2="360"/>
<text class="nj-charts-tick" x="182.3" y="376">0.5</text>
<line class="nj-charts-grid" x1="222.6" y1="40" x2="222.6" y2="360"/>
<text class="nj-charts-tick" x="222.6" y="376">1</text>
<line class="nj-charts-grid" x1="262.9" y1="40" x2="262.9" y2="360"/>
<text class="nj-charts-tick" x="262.9" y="376">1.5</text>
<line class="nj-charts-grid" x1="303.2" y1="40" x2="303.2" y2="360"/>
<text class="nj-charts-tick" x="303.2" y="376">2</text>
<line class="nj-charts-grid" x1="343.5" y1="40" x2="343.5" y2="360"/>
<text class="nj-charts-tick" x="343.5" y="376">2.5</text>
<line class="nj-charts-grid" x1="383.8" y1="40" x2="383.8" y2="360"/>
<text class="nj-charts-tick" x="383.8" y="376">3</text>
<line class="nj-charts-grid" x1="424.1" y1="40" x2="424.1" y2="360"/>
<text class="nj-charts-tick" x="424.1" y="376">3.5</text>
<line class="nj-charts-grid" x1="464.4" y1="40" x2="464.4" y2="360"/>
<text class="nj-charts-tick" x="464.4" y="376">4</text>
<line class="nj-charts-grid" x1="504.7" y1="40" x2="504.7" y2="360"/>
<text class="nj-charts-tick" x="504.7" y="376">4.5</text>
<line class="nj-charts-grid" x1="545" y1="40" x2="545" y2="360"/>
<text class="nj-charts-tick" x="545" y="376">5</text>
<text class="nj-charts-tick" x="343.5" y="394">GB/s, longer is faster</text>
<text class="nj-charts-rowlabel" x="134" y="83.5" text-anchor="end">twitter.json</text>
<path class="nj-charts-c1" d="M142 44 H418.132 Q422.132 44 422.132 48 V51 Q422.132 55 418.132 55 H142 Z"><title>json_document, twitter.json: 3.48 GB/s</title></path>
<text class="nj-charts-val" x="426.132" y="53.5">3.48</text>
<path class="nj-charts-c2" d="M142 57 H403.104 Q407.104 57 407.104 61 V64 Q407.104 68 403.104 68 H142 Z"><title>yyjson, twitter.json: 3.29 GB/s</title></path>
<text class="nj-charts-val" x="411.104" y="66.5">3.29</text>
<path class="nj-charts-c3" d="M142 70 H373.213 Q377.213 70 377.213 74 V77 Q377.213 81 373.213 81 H142 Z"><title>simdjson D&#8203;OM, twitter.json: 2.92 GB/s</title></path>
<text class="nj-charts-val" x="381.213" y="79.5">2.92</text>
<path class="nj-charts-c4" d="M142 83 H196.776 Q200.776 83 200.776 87 V90 Q200.776 94 196.776 94 H142 Z"><title>Boost.J&#8203;SON, twitter.json: 0.73 GB/s</title></path>
<text class="nj-charts-val" x="204.776" y="92.5">0.73</text>
<path class="nj-charts-c5" d="M142 96 H175.208 Q179.208 96 179.208 100 V103 Q179.208 107 175.208 107 H142 Z"><title>json::parse, twitter.json: 0.46 GB/s</title></path>
<text class="nj-charts-val" x="183.208" y="105.5">0.46</text>
<text class="nj-charts-rowlabel" x="134" y="162.5" text-anchor="end">citm_catalog.json</text>
<path class="nj-charts-c1" d="M142 123 H524.809 Q528.809 123 528.809 127 V130 Q528.809 134 524.809 134 H142 Z"><title>json_document, citm_catalog.json: 4.80 GB/s</title></path>
<text class="nj-charts-val" x="532.809" y="132.5">4.80</text>
<path class="nj-charts-c2" d="M142 136 H429.179 Q433.179 136 433.179 140 V143 Q433.179 147 429.179 147 H142 Z"><title>yyjson, citm_catalog.json: 3.61 GB/s</title></path>
<text class="nj-charts-val" x="437.179" y="145.5">3.61</text>
<path class="nj-charts-c3" d="M142 149 H421.992 Q425.992 149 425.992 153 V156 Q425.992 160 421.992 160 H142 Z"><title>simdjson D&#8203;OM, citm_catalog.json: 3.52 GB/s</title></path>
<text class="nj-charts-val" x="429.992" y="158.5">3.52</text>
<path class="nj-charts-c4" d="M142 162 H232.063 Q236.063 162 236.063 166 V169 Q236.063 173 232.063 173 H142 Z"><title>Boost.J&#8203;SON, citm_catalog.json: 1.17 GB/s</title></path>
<text class="nj-charts-val" x="240.063" y="171.5">1.17</text>
<path class="nj-charts-c5" d="M142 175 H195.789 Q199.789 175 199.789 179 V182 Q199.789 186 195.789 186 H142 Z"><title>json::parse, citm_catalog.json: 0.72 GB/s</title></path>
<text class="nj-charts-val" x="203.789" y="184.5">0.72</text>
<text class="nj-charts-rowlabel" x="134" y="241.5" text-anchor="end">canada.json</text>
<path class="nj-charts-c1" d="M142 202 H327.093 Q331.093 202 331.093 206 V209 Q331.093 213 327.093 213 H142 Z"><title>json_document, canada.json: 2.35 GB/s</title></path>
<text class="nj-charts-val" x="335.093" y="211.5">2.35</text>
<path class="nj-charts-c2" d="M142 215 H259.85 Q263.85 215 263.85 219 V222 Q263.85 226 259.85 226 H142 Z"><title>yyjson, canada.json: 1.51 GB/s</title></path>
<text class="nj-charts-val" x="267.85" y="224.5">1.51</text>
<path class="nj-charts-c3" d="M142 228 H224.48 Q228.48 228 228.48 232 V235 Q228.48 239 224.48 239 H142 Z"><title>simdjson D&#8203;OM, canada.json: 1.07 GB/s</title></path>
<text class="nj-charts-val" x="232.48" y="237.5">1.07</text>
<path class="nj-charts-c4" d="M142 241 H210.545 Q214.545 241 214.545 245 V248 Q214.545 252 210.545 252 H142 Z"><title>Boost.J&#8203;SON, canada.json: 0.90 GB/s</title></path>
<text class="nj-charts-val" x="218.545" y="250.5">0.90</text>
<path class="nj-charts-c5" d="M142 254 H168.804 Q172.804 254 172.804 258 V261 Q172.804 265 168.804 265 H142 Z"><title>json::parse, canada.json: 0.38 GB/s</title></path>
<text class="nj-charts-val" x="176.804" y="263.5">0.38</text>
<text class="nj-charts-rowlabel" x="134" y="320.5" text-anchor="end">jeopardy.json</text>
<path class="nj-charts-c1" d="M142 281 H335.369 Q339.369 281 339.369 285 V288 Q339.369 292 335.369 292 H142 Z"><title>json_document, jeopardy.json: 2.45 GB/s</title></path>
<text class="nj-charts-val" x="343.369" y="290.5">2.45</text>
<path class="nj-charts-c2" d="M142 294 H284.675 Q288.675 294 288.675 298 V301 Q288.675 305 284.675 305 H142 Z"><title>yyjson, jeopardy.json: 1.82 GB/s</title></path>
<text class="nj-charts-val" x="292.675" y="303.5">1.82</text>
<path class="nj-charts-c3" d="M142 307 H304.612 Q308.612 307 308.612 311 V314 Q308.612 318 304.612 318 H142 Z"><title>simdjson D&#8203;OM, jeopardy.json: 2.07 GB/s</title></path>
<text class="nj-charts-val" x="312.612" y="316.5">2.07</text>
<path class="nj-charts-c4" d="M142 320 H188.368 Q192.368 320 192.368 324 V327 Q192.368 331 188.368 331 H142 Z"><title>Boost.J&#8203;SON, jeopardy.json: 0.62 GB/s</title></path>
<text class="nj-charts-val" x="196.368" y="329.5">0.62</text>
<path class="nj-charts-c5" d="M142 333 H164.706 Q168.706 333 168.706 337 V340 Q168.706 344 164.706 344 H142 Z"><title>json::parse, jeopardy.json: 0.33 GB/s</title></path>
<text class="nj-charts-val" x="172.706" y="342.5">0.33</text>
<line class="nj-charts-zero" x1="142" y1="40" x2="142" y2="360"/>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB