perf(mdbx): disable prefault for parallel subtxns

Pages allocated from GC are likely still hot in page cache.
mincore() syscall overhead dominated ~47% of storage trie write time.

Empirical test - revert if page faults increase significantly.
This commit is contained in:
joshieDo
2026-02-05 16:07:29 +00:00
parent 120316153b
commit 230593ac05
3 changed files with 4 additions and 42 deletions

View File

@@ -450,17 +450,6 @@ pub(crate) use reth_db_api::transaction::{ArenaHintEstimationStats, ArenaHintSou
impl EdgeArenaMetrics {
/// Record stats from a single subtransaction.
pub(crate) fn record(&self, stats: &reth_libmdbx::SubTransactionStats) {
println!(
"[ARENA] page_allocations={} refill_events={} initial_pages={} unused={} refill_pages={} hint={} from_gc={} from_eof={}",
stats.arena_page_allocations,
stats.arena_refill_events,
stats.arena_initial_pages,
stats.pages_unused,
stats.arena_refill_pages,
stats.arena_hint,
stats.pages_from_gc,
stats.pages_from_eof
);
self.arena_page_allocations.increment(stats.arena_page_allocations as u64);
self.arena_refill_events.increment(stats.arena_refill_events as u64);
self.arena_refills_per_batch.record(stats.arena_refill_events as f64);

View File

@@ -15839,7 +15839,10 @@ static int create_subtxn_with_dbi(MDBX_txn *parent, MDBX_dbi dbi, MDBX_txn **sub
txn->tw.gc.retxl = nullptr;
txn->tw.gc.last_reclaimed = 0;
txn->tw.spilled.list = nullptr;
txn->tw.prefault_write_activated = parent->tw.prefault_write_activated;
/* Disable prefault for parallel subtxns: pages allocated from GC are likely
* still hot in page cache, and mincore() syscall overhead dominates (~47% of
* storage trie write time). Empirical test - revert if page faults increase. */
txn->tw.prefault_write_activated = false;
/* WRITEMAP mode: no ioring needed (no spill-to-disk) */
txn->tw.txn_ioring = nullptr;

View File

@@ -421,42 +421,12 @@ where
let subtxns = self.subtxns.read();
let mut stats_vec = Vec::with_capacity(subtxns.len());
let mut total_page_allocations = 0usize;
let mut total_refill_events = 0usize;
let mut total_initial_pages = 0usize;
let mut total_refill_pages = 0usize;
let mut total_unused = 0usize;
let mut total_from_gc = 0usize;
let mut total_from_eof = 0usize;
for subtxn in subtxns.values() {
let stats = subtxn.get_stats()?;
total_page_allocations += stats.arena_page_allocations;
total_refill_events += stats.arena_refill_events;
total_initial_pages += stats.arena_initial_pages;
total_refill_pages += stats.arena_refill_pages;
total_unused += stats.pages_unused;
total_from_gc += stats.pages_from_gc;
total_from_eof += stats.pages_from_eof;
subtxn.commit()?;
stats_vec.push((subtxn.dbi(), stats));
}
// Temporary debug output for stress testing
if !stats_vec.is_empty() {
let hit_rate = if total_page_allocations + total_refill_events > 0 {
(total_page_allocations as f64 /
(total_page_allocations + total_refill_events) as f64) *
100.0
} else {
0.0
};
eprintln!(
"[ARENA] subtxns={} page_allocations={} refill_events={} hit_rate={:.1}% initial_pages={} refill_pages={} unused={} from_gc={} from_eof={}",
stats_vec.len(), total_page_allocations, total_refill_events, hit_rate, total_initial_pages, total_refill_pages, total_unused, total_from_gc, total_from_eof
);
}
Ok(stats_vec)
}
}