aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-08 18:35:27 +0000
committertv <tv@krebsco.de>2026-11-08 19:36:58 +0100
commit9b58d2e870fddb5f04e846b0566dd6a613d2f699 (patch)
treed94d8faa5fee1135128e05a090ed8cd66a28df94 /src
parente9813db05f2cc8590b785639a3c41ba60812909a (diff)
bench: what screening costs while catching up
Review 2, N7, measured first: a node whose document has no policy (a joiner's empty one) applies what it receives unjudged. Screening it too would cost finding every change's paths and judging it. bench_screening takes 1 000, 10 000 and 100 000 changes into an empty document, as one delta brings them: unjudged, as Automerge applies them, against screened (access::screen::apply), with no policy in them (each passes, read at its dependencies, after its paths were found) and with the policy their first change wrote (each judged by the rules).
Diffstat (limited to 'src')
-rw-r--r--src/document/bench.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/document/bench.rs b/src/document/bench.rs
index bc1f6fa..467dc2f 100644
--- a/src/document/bench.rs
+++ b/src/document/bench.rs
@@ -29,6 +29,13 @@
//! rebuilds them, and from the bytes they came as; and inspected before they are
//! applied (`inspect::Inspector`), their objects made among them or looked up in the
//! document, each once.
+//!
+//! `screening`: what judging every change received costs while catching up
+//! (`access::screen`), against applying them unjudged, as a node whose document has no
+//! policy does now: many changes into an empty document, as one delta brings them;
+//! judged with no policy in them (each passes, read at its dependencies, after its
+//! paths were found), and with the policy their first change wrote (each judged by the
+//! rules).
use std::time::{Duration, Instant};
@@ -607,3 +614,104 @@ fn bench_change_paths() {
);
}
}
+
+/// `changes` changes on `doc`, each writing `servers/<region>/<n>/cpu`, by `actors`
+/// actors in turn, as `nested` does.
+fn nested_on(mut doc: Document, changes: usize, actors: usize) -> Document {
+ let per = changes.div_ceil(actors.max(1));
+ for i in 0..changes {
+ if i % per == 0 {
+ doc.set_actor(automerge::ActorId::random());
+ }
+ let path = [
+ "servers".to_owned(),
+ format!("region{}", i % 4),
+ format!("{}", (i / 4) % 50),
+ "cpu".to_owned(),
+ ];
+ transact(&mut doc, UnixMillis(1), &WRITER, |doc| {
+ store::write_path(doc, &path, &serde_json::json!(i))
+ })
+ .expect("write");
+ }
+ doc
+}
+
+/// The time to take `doc`'s changes into an empty document: unjudged, as Automerge
+/// applies them; and screened (`access::screen::apply`).
+fn screening_costs(workload: &str, doc: &Document) {
+ let raw: Vec<Vec<u8>> = doc
+ .get_changes(&[])
+ .iter()
+ .map(|change| change.raw_bytes().to_vec())
+ .collect();
+ let changes = || -> Vec<automerge::Change> {
+ raw.iter()
+ .map(|bytes| automerge::Change::from_bytes(bytes.clone()).expect("change"))
+ .collect()
+ };
+ let unjudged = median(3, || {
+ let changes = changes();
+ let mut empty = Document::new();
+ let started = Instant::now();
+ empty.apply_changes(changes).expect("apply");
+ started.elapsed()
+ });
+ let screened = median(3, || {
+ let changes = changes();
+ let mut empty = Document::new();
+ let started = Instant::now();
+ let screened = crate::access::screen::apply(
+ &mut empty,
+ Epoch::GENESIS,
+ changes,
+ &mut crate::access::screen::Waiting::default(),
+ &mut crate::access::screen::Refusals::default(),
+ None,
+ &mut crate::document::PatchLog::inactive(),
+ )
+ .expect("screen");
+ let elapsed = started.elapsed();
+ assert_eq!(screened.applied, raw.len(), "every change taken");
+ elapsed
+ });
+ report_signatures(
+ workload,
+ unjudged,
+ &[("screened", screened.saturating_sub(unjudged))],
+ );
+}
+
+#[test]
+#[ignore = "a benchmark: cargo benchmarks"]
+fn bench_screening() {
+ println!();
+ for changes in [1_000, 10_000, 100_000] {
+ screening_costs(
+ &format!("{changes} changes, no policy"),
+ &nested(changes, changes / 100),
+ );
+ // The swarm, its policy and the writer's member entry first: every change after
+ // is judged by the rules (no registry: the writer's node, a member, may).
+ let mut start = Document::new();
+ transact(&mut start, UnixMillis(1), &WRITER, |doc| {
+ let path = |text: &str| -> Vec<String> { text.split('/').map(str::to_owned).collect() };
+ store::write_path(doc, &path("_continuum/swarm"), &serde_json::json!("bench"))?;
+ store::write_path(
+ doc,
+ &path("_continuum/access/policy"),
+ &serde_json::json!({"version": 1}),
+ )?;
+ store::write_path(
+ doc,
+ &crate::swarm::members::member_path(WRITER.node),
+ &serde_json::json!({}),
+ )
+ })
+ .expect("start");
+ screening_costs(
+ &format!("{changes} changes, the policy first"),
+ &nested_on(start, changes, changes / 100),
+ );
+ }
+}