Skip to content

Reduce encoder memory allocations#301

Merged
nickva merged 1 commit into
masterfrom
enc-grow-buffer
Jun 15, 2026
Merged

Reduce encoder memory allocations#301
nickva merged 1 commit into
masterfrom
enc-grow-buffer

Conversation

@nickva

@nickva nickva commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Flush the buffer into iolist segments with a chunk size that doubles (up to 64KB). Large documents should now have O(log n) allocations.

We can use tprof to check allocation with OTP 27+. Example of memory profiling before and after with canada.json from bench data:

{ok, B} = file:read_file("data/canada.json").
T = jiffy:decode(B, [return_maps]).
tprof:profile(jiffy, encode, [T], #{type => call_memory}).

Before

****** Process <0.114.0>  --  100.00% of total ***
FUNCTION                 CALLS  WORDS  PER CALL  [    %]
lists:reverse/1              1      4      4.00  [ 0.03]
lists:reverse/2              1   2062   2062.00  [14.21]
jiffy:nif_encode_init/2      1  12441  12441.00  [85.76]
                                14507            [100.0]

After

****** Process <0.101.0>  --  100.00% of total ***
FUNCTION                 CALLS  WORDS  PER CALL  [    %]
lists:reverse/1              1      4      4.00  [ 0.29]
lists:reverse/2              1     68     68.00  [ 4.88]
jiffy:nif_encode_init/2      1   1321   1321.00  [94.83]

This shows a good speed improvment on some benchmarks especially ones with larger objects. Saw about ~6% speedup on on object-heavy benchmarks (citm, pokedex, github):. Larger ones like "canada" showed about a ~1.25x speedup.

Flush the buffer into iolist segments with a chunk size that doubles (up to
64KB). Large documents should now have O(log n) allocation.

We can use tprof to check allocation with OTP 27+. Example of memory profiling
before and after with `canada.json` from bench data:

```
{ok, B} = file:read_file("data/canada.json").
T = jiffy:decode(B, [return_maps]).
tprof:profile(jiffy, encode, [T], #{type => call_memory}).
```

Before
```
****** Process <0.114.0>  --  100.00% of total ***
FUNCTION                 CALLS  WORDS  PER CALL  [    %]
lists:reverse/1              1      4      4.00  [ 0.03]
lists:reverse/2              1   2062   2062.00  [14.21]
jiffy:nif_encode_init/2      1  12441  12441.00  [85.76]
                                14507            [100.0]
```

After
```
****** Process <0.101.0>  --  100.00% of total ***
FUNCTION                 CALLS  WORDS  PER CALL  [    %]
lists:reverse/1              1      4      4.00  [ 0.29]
lists:reverse/2              1     68     68.00  [ 4.88]
jiffy:nif_encode_init/2      1   1321   1321.00  [94.83]
```

This shows a good speed improvment on some benchmarks especially ones with
larger objects. Saw about ~6% speedup on on object-heavy benchmarks
(citm, pokedex, github):. Larger ones like "canada" showed about a ~1.25x
speedup.
@nickva nickva requested a review from davisp June 15, 2026 04:53

@davisp davisp left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 Nice work!

@nickva nickva merged commit 172c8bd into master Jun 15, 2026
24 checks passed
@nickva nickva deleted the enc-grow-buffer branch June 15, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants