Skip to content

Posix memory map optimization and tuning. - #884

Open
evoskuil wants to merge 17 commits into
libbitcoin:masterfrom
evoskuil:master
Open

Posix memory map optimization and tuning.#884
evoskuil wants to merge 17 commits into
libbitcoin:masterfrom
evoskuil:master

Conversation

@evoskuil

@evoskuil evoskuil commented Aug 1, 2026

Copy link
Copy Markdown
Member

No description provided.

evoskuil and others added 17 commits August 1, 2026 11:38
Intent bits age (hot sampling), so a write preempted across release
passes loses its declaration: the page settles clean under the write,
which lands between the settle copy and installation and is silently
discarded (a stale released page, observed as false invalid blocks).
An unaged prepare/mark writer count invalidates the conversion instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mark() uncounts the writer counted by prepare(), so unpaired internal
mark calls (transfer failure restoration, head expansion backfill)
underflow the count: offset negative, in-flight writers read as drained
and release settles under them (observed as a rare stale prevout head
bucket after expansion). Transfer restores marks by remark_ (marking
without the uncount) and the expansion backfill brackets with prepare().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Anonymous heads with a dirty-page writer keep their content current in
the file but leave the pages anonymous, so reclaim under scarcity is
swap; release converts only quiet runs, and hash-uniform head writes are
never quiet during download. Mapping heads writable-shared makes reclaim
kernel writeback of a bounded rewrite-in-place mapping instead, without
returning the unbounded append writeback that dirty ratio tuning exists
to control (bodies remain staged). Selected by head_shared, which
excludes the dirty bitmap, the lazy writer, and release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Transferring a write-hot head amplifies writes without release payoff:
transferred pages re-dirty within seconds and the quiet gate blocks
their release regardless, so scarcity passes previously paid full head
writeback (every page, every second) on top of swap. A hot instance now
leaves its anonymous set to swap (dirty-exempt) until it quiesces, and
quiet instances transfer and release as before, so the phase change
flips each instance from swap-bound to file-backed with no write storm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scarcity clears as swap absorbs the write-hot anonymous set, which
stopped conversion exactly when it becomes profitable: the swapped
pages stay anonymous and the validation phase faults them back one
probe at a time (serial random swap-in for hours). A clean swapped
page settles without read-back (the file already holds its content),
freeing the swap slot and routing reads through the file mapping, so
once engaged a quiet instance now transfers and releases regardless
of the momentary scarcity signal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The eviction sweep is inert on linux: MS_INVALIDATE invalidates other
mappings of the file but leaves clean pages cached, so streaming body
cache pressure stands and the kernel preserves it by swapping the
anonymous heads (the observed disaster: the precious random-access set
pays disk swap to cache pages that are never read again). Pageout
reclaims the mapped range regardless of page table references (dirty
pages write back, clean pages drop, later reads fault from the file).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The kernel cannot infer the store's priority: settled bodies are worth
caching for imminent re-read (validation follows archival) but never at
the price of head residency, while lru ranks a just-read body page above
a cold head bucket and swaps the head set to preserve the body cache.
Deactivating each settled extent encodes the ordering: cached until
pressure, first reclaimed under it, reactivated by an actual read. The
open-time settle pass demotes the whole settled prefix, so a fresh open
starts cold and promotes only what is hot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two defects left the body sweep unable to hold the cache down. It
lapped the whole body, so with hundreds of gigabytes settled and only
the recent tail resident, most of a lap evicted nothing while the tail
refilled faster than the cursor returned. And it triggered at the
reclaim watermark, conceding victim selection to the kernel, which
balances anon against file and swaps the heads it cannot know are the
priority. The lap now covers the tail (a lap holds at most physical
memory, as nothing below is resident) and the floor leads reclaim.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Measured on a 32GB box: 13.1GB active anonymous heads beside 14.9GB of
page cache, of which only 5.6GB is mapped. The unmapped remainder is the
transfer itself, as every written head page leaves a cached copy of the
write, so the head is resident twice (live anonymous, plus file cache
that is never read while the head is resident). That fills memory before
any body is cached, and the kernel resolves it by swapping the live copy
back in from disk. Discarding after each transfer leaves the anonymous
head alone (mapped released runs hold a reference and are skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The population read caches the whole head, duplicating in page cache
what it just copied to anonymous memory, so the head is resident twice
before a block is processed (measured: 14GB of head files read at load
against a 32GB box). The mapped settled prefix holds a reference and is
unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant