Skip to content

Faster store writes, a faster allocator, smaller binaries, and benchmarks - #24

Merged
sepehr-safari merged 3 commits into
mainfrom
faster-store-writes-and-smaller-binaries
Sep 23, 2026
Merged

sepehr-safari merged 3 commits into
mainfrom
faster-store-writes-and-smaller-binaries

Conversation

@sepehr-safari

@sepehr-safari sepehr-safari commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Closes #23.

What changes

  • Store writes are batched. Events kept with --store are held until 512 have passed their checks, a relay has nothing more ready, or the run ends, then written in one transaction with the library's new Store.ingestBatch (nostr 0.14.6) and printed: still stored before they are shown. One transaction per event meant one disk sync per event.
  • smp_allocator instead of page_allocator, which mapped pages for every allocation.
  • Stripped release binaries, through a new -Dstrip build option that the release workflow passes. The Linux binaries carried about 9 MB of debug sections.
  • BENCHMARKS.md and bench/run.py: the numbers, and a script that reproduces them with a local relay.

Measured

Same machine, same 100,000-event corpus, before and after:

v0.3.0 this branch
storing 100,000 events from a loopback relay 2,300/s 16,864/s
decoding 10,000 npubs 68,900/s 586,718/s
verifying 10,000 events 27,000/s 33,270/s
Linux x86_64 binary 12.0 MB 2.7 MB
macOS arm64 binary 2.6 MB 2.3 MB
startup, peak memory 2.3 ms, 1.7 MB unchanged

The store file for the 100,000 events grew from 86 MB to between 94 and 102 MB across my runs: LMDB keeps more free pages after larger transactions.

Tests

events kept in a store are written in batches, and every one is stored and printed serves 1,200 events through a local relay, two full batches and part of a third, and checks every one reached the store and stdout. Without the final flushes it fails. All 94 tests pass.

Release

The last commit bumps the version to 0.3.1.

…release binaries

Events kept in a store were written one transaction each, and LMDB syncs on every commit, so storing ran at about 2,300 events a second. They are now held until 512 have passed their checks, a relay has nothing more ready, or the run ends, and written in one transaction with the library's new Store.ingestBatch, then printed: still stored before they are shown. Storing 100,000 events from a local relay now runs at about 16,000 a second.

The allocator was page_allocator, which maps pages for every allocation. It is now smp_allocator. Decoding 10,000 npubs went from 135 ms to 17 ms; signing and verifying are about 15 percent faster.

Release binaries are built with the new -Dstrip option. The Linux ones carried about 9 MB of debug sections and go from 12 MB to under 3 MB.

The nostr pin moves to 0.14.6 for ingestBatch. A test serves 1,200 events through a local relay, two full batches and part of a third, and checks every one is stored and printed; without the final flushes it fails.

Closes #23.
BENCHMARKS.md gives what deed costs to run: binary and download sizes, startup, memory, stream throughput, storing and querying 100,000 events, and relays over loopback. Every number comes from bench/run.py, which runs the same measurements against any build, with a local relay (bench/relay.py behind websocat) so nothing leaves the machine. The README gains a short section pointing to it.
Faster store writes, a faster allocator, stripped release binaries, and benchmarks.
@sepehr-safari sepehr-safari added this to the v0.3.1 milestone Sep 23, 2026
@sepehr-safari
sepehr-safari merged commit d4c336a into main Sep 23, 2026
2 checks passed
@sepehr-safari
sepehr-safari deleted the faster-store-writes-and-smaller-binaries branch September 23, 2026 14:34
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.

Storing events, allocating memory and the Linux downloads all cost more than they need to

1 participant