Skip to content

Commit b2a31ff

Browse files
committed
cli: the precomputed-tree cache's posture follows the host target (W3 block)
LFM_PRECOMPUTED_TREE_CACHE_CAP, when the environment leaves it unset, is set by the CLI's posture from the host target (block_whir's spill target): 64 entries (about 6.2 GiB once full) from a 64 GiB target up, 16 below it, as #1013's CLI does (49c4bb5). A miss only rebuilds a tree whose root is the key, so nothing moves in a proof. The BLOCK POSTURE line compares the knob against the same rule, and a note says when the posture set 16.
1 parent 76f96a1 commit b2a31ff

3 files changed

Lines changed: 96 additions & 14 deletions

File tree

‎bin/cli/src/main.rs‎

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -490,23 +490,43 @@ fn main() -> ExitCode {
490490
}
491491

492492
/// What the posture does in an environment: the knobs it sets (each posture
493-
/// knob the environment leaves unset) and the ones the environment already
494-
/// sets.
493+
/// knob the environment leaves unset), the ones the environment already sets,
494+
/// and notes on the knobs it set to something other than the table's value.
495495
struct PosturePlan {
496496
set: Vec<(&'static str, &'static str)>,
497497
env: Vec<String>,
498+
notes: Vec<String>,
498499
}
499500

500-
fn posture_plan(env: impl Fn(&str) -> Option<String>) -> PosturePlan {
501+
/// The posture against the environment `env` and the host `target` (bytes):
502+
/// the precomputed-tree cache's cap follows the target
503+
/// ([`prover::lfm::whir_block_tree::posture_tree_cache_cap`]).
504+
fn posture_plan(env: impl Fn(&str) -> Option<String>, target: impl Fn() -> u64) -> PosturePlan {
505+
use prover::lfm::whir_block_tree::{POSTURE, POSTURE_TREE_CACHE_KNOB, posture_tree_cache_cap};
501506
let mut plan = PosturePlan {
502507
set: Vec::new(),
503508
env: Vec::new(),
509+
notes: Vec::new(),
504510
};
505-
for &(name, value) in prover::lfm::whir_block_tree::POSTURE {
506-
match env(name) {
507-
Some(v) => plan.env.push(format!("{name}={v}")),
508-
None => plan.set.push((name, value)),
511+
for &(name, value) in POSTURE {
512+
if let Some(v) = env(name) {
513+
plan.env.push(format!("{name}={v}"));
514+
continue;
515+
}
516+
if name == POSTURE_TREE_CACHE_KNOB {
517+
let target = target();
518+
let cap = posture_tree_cache_cap(target);
519+
if cap != value {
520+
plan.notes.push(format!(
521+
"BLOCK POSTURE: {name}={cap}, not {value}: the host target is {:.1} GiB \
522+
(LAMBDA_VM_BLOCK_SPILL_TARGET_GIB, else the cgroup or MemTotal less 10 GiB)",
523+
target as f64 / (1u64 << 30) as f64
524+
));
525+
}
526+
plan.set.push((name, cap));
527+
continue;
509528
}
529+
plan.set.push((name, value));
510530
}
511531
plan
512532
}
@@ -520,7 +540,10 @@ fn posture_plan(env: impl Fn(&str) -> Option<String>) -> PosturePlan {
520540
///
521541
/// Sets environment variables: no other thread may exist.
522542
unsafe fn apply_posture() -> Vec<String> {
523-
let plan = posture_plan(|name| std::env::var(name).ok());
543+
let plan = posture_plan(
544+
|name| std::env::var(name).ok(),
545+
prover::block_whir::spill_target_bytes,
546+
);
524547
for (name, value) in &plan.set {
525548
// SAFETY: the caller's: no other thread exists.
526549
unsafe { std::env::set_var(name, value) };
@@ -537,11 +560,13 @@ unsafe fn apply_posture() -> Vec<String> {
537560
w.join(" ")
538561
}
539562
};
540-
vec![format!(
563+
let mut lines = vec![format!(
541564
"BLOCK POSTURE SET: {} · from the environment: {} · jemalloc: {decay}",
542565
words(plan.set.iter().map(|(n, v)| format!("{n}={v}")).collect()),
543566
words(plan.env),
544-
)]
567+
)];
568+
lines.extend(plan.notes);
569+
lines
545570
}
546571

547572
fn hex(bytes: &[u8]) -> String {
@@ -1677,14 +1702,37 @@ mod tests {
16771702
#[test]
16781703
fn the_posture_sets_only_unset_knobs() {
16791704
use prover::lfm::whir_block_tree::POSTURE;
1680-
let all = posture_plan(|_| None);
1705+
let roomy = || 110u64 << 30;
1706+
let all = posture_plan(|_| None, roomy);
16811707
assert_eq!(all.set, POSTURE.to_vec());
1682-
assert!(all.env.is_empty());
1683-
let mine = posture_plan(|n| (n == "TABLE_PARALLELISM").then(|| "8".to_string()));
1708+
assert!(all.env.is_empty() && all.notes.is_empty());
1709+
let mine = posture_plan(
1710+
|n| (n == "TABLE_PARALLELISM").then(|| "8".to_string()),
1711+
roomy,
1712+
);
16841713
assert!(mine.set.iter().all(|(n, _)| *n != "TABLE_PARALLELISM"));
16851714
assert_eq!(mine.env, vec!["TABLE_PARALLELISM=8".to_string()]);
16861715
}
16871716

1717+
/// The precomputed-tree cache's posture follows the host target: 64
1718+
/// entries from a 64 GiB target up, 16 below it (with a note), and a value
1719+
/// the environment sets is left alone.
1720+
#[test]
1721+
fn the_tree_cache_posture_follows_the_host_target() {
1722+
use prover::lfm::whir_block_tree::POSTURE_TREE_CACHE_KNOB as CAP;
1723+
let none = |_: &str| None;
1724+
let cap = |plan: &PosturePlan| plan.set.iter().find(|(n, _)| *n == CAP).map(|(_, v)| *v);
1725+
let big = posture_plan(none, || 64u64 << 30);
1726+
assert_eq!(cap(&big), Some("64"));
1727+
assert!(big.notes.is_empty());
1728+
let small = posture_plan(none, || 38u64 << 30);
1729+
assert_eq!(cap(&small), Some("16"));
1730+
assert_eq!(small.notes.len(), 1, "the note says why");
1731+
let set = posture_plan(|n| (n == CAP).then(|| "32".to_string()), || 38u64 << 30);
1732+
assert_eq!(cap(&set), None, "the environment's value stays");
1733+
assert_eq!(set.env, vec![format!("{CAP}=32")]);
1734+
}
1735+
16881736
/// The binary runs the allocator posture it compiles in: jemalloc never
16891737
/// purges (`opt.dirty_decay_ms` and `opt.muzzy_decay_ms` are -1), unless
16901738
/// `_RJEM_MALLOC_CONF` says otherwise. Deleting the `malloc_conf` export

‎prover/src/block_whir.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ fn parse_spill_policy(value: Option<&str>) -> BlockSpillPolicy {
438438
/// smaller of the cgroup's memory limit (v2 or v1, [`cgroup_memory`]) and
439439
/// `MemTotal`, less 10 GiB ([`spill_target_from`]) (#1013's
440440
/// `spill_target_bytes` @ 035aef5d6).
441-
pub(crate) fn spill_target_bytes() -> u64 {
441+
pub fn spill_target_bytes() -> u64 {
442442
if let Some(gib) = std::env::var("LAMBDA_VM_BLOCK_SPILL_TARGET_GIB")
443443
.ok()
444444
.and_then(|v| v.trim().parse::<f64>().ok())

‎prover/src/lfm/whir_block_tree.rs‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ impl WhirTreeSink for StderrSink {
7777
/// too, and a verifier configured otherwise refuses (completeness, never
7878
/// soundness). The measurement-only `LAMBDA_VM_BASE_SPLIT` is not posture, and
7979
/// the allocator's never-purge posture is compiled into the binary.
80+
/// `LFM_PRECOMPUTED_TREE_CACHE_CAP`'s posture follows the host target
81+
/// ([`posture_tree_cache_cap`]): the table's 64 from a 64 GiB target up, 16
82+
/// below it.
8083
pub const POSTURE: &[(&str, &str)] = &[
8184
("TABLE_PARALLELISM", "4"),
8285
("LAMBDA_VM_MAX_ROWS_LOG2", "21"),
@@ -88,11 +91,42 @@ pub const POSTURE: &[(&str, &str)] = &[
8891
("LAMBDA_VM_GRIND_GRID", "1024"),
8992
];
9093

94+
/// The precomputed-tree cache's knob, whose posture follows the host target.
95+
pub const POSTURE_TREE_CACHE_KNOB: &str = "LFM_PRECOMPUTED_TREE_CACHE_CAP";
96+
97+
/// The host target (GiB, [`block_whir::spill_target_bytes`]) from which the
98+
/// posture keeps [`POSTURE`]'s 64 precomputed-tree cache entries.
99+
pub const POSTURE_TREE_CACHE_FULL_TARGET_GIB: u64 = 64;
100+
101+
/// `LFM_PRECOMPUTED_TREE_CACHE_CAP`'s posture for a host `target` in bytes:
102+
/// 64 entries (≈ 100 MiB each, ≈ 6.2 GiB once full) from a
103+
/// [`POSTURE_TREE_CACHE_FULL_TARGET_GIB`] target up, 16 below it, where the
104+
/// ≈ 4.7 GiB they free count for more than the hits they lose. A miss only
105+
/// rebuilds the tree: the cache's key is the root, so nothing a proof commits
106+
/// to moves (#1013's rule, 49c4bb512).
107+
pub fn posture_tree_cache_cap(target: u64) -> &'static str {
108+
if target >= POSTURE_TREE_CACHE_FULL_TARGET_GIB << 30 {
109+
"64"
110+
} else {
111+
"16"
112+
}
113+
}
114+
91115
/// The `BLOCK POSTURE:` line: each posture knob as the process has it — equal
92116
/// to the posture, unset, or another value.
93117
pub fn posture_line() -> String {
94118
let words: Vec<String> = POSTURE
95119
.iter()
120+
.map(|&(name, want)| {
121+
if name == POSTURE_TREE_CACHE_KNOB {
122+
(
123+
name,
124+
posture_tree_cache_cap(block_whir::spill_target_bytes()),
125+
)
126+
} else {
127+
(name, want)
128+
}
129+
})
96130
.map(|(name, want)| match std::env::var(name) {
97131
Ok(v) if v == *want => format!("{name}={v}"),
98132
Ok(v) => format!("{name}={v} (≠ posture {want})"),

0 commit comments

Comments
 (0)