aboutsummaryrefslogtreecommitdiffstats
path: root/src/remake/bench.rs
blob: 3c3f513bff56bcc38f3ac5d18aa1efdb51600516 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! Benchmarks, as ignored tests (`cargo benchmarks`, `docs/development.md`).
//!
//! `replay`: carrying writes over into a new epoch's document, last writer wins
//! (`delta::replay_newest`): what a rejoin does with the writes made during it, and a
//! switch with those made after the remake's base. It finds the paths of every write
//! carried, and of every newer change in the new document, by a diff each.

use std::time::Instant;

use automerge::transaction::Transactable;
use automerge::{ObjType, ROOT, ReadDoc, ScalarValue};

use super::delta::{Superseded, replay_newest};
use super::kind::Translator;
use crate::document::Document;
use crate::document::bench::{document, median};
use crate::document::store;

/// `count` changes to `doc`, each writing one key under `data` (from `first` on), made
/// at `at` and after.
fn writes(doc: &mut Document, first: usize, count: usize, at: i64) {
    for i in 0..count {
        let mut transaction = doc.transaction();
        let data = transaction
            .get(ROOT, "data")
            .expect("read")
            .map(|(_, id)| id)
            .unwrap_or_else(|| {
                transaction
                    .put_object(ROOT, "data", ObjType::Map)
                    .expect("map")
            });
        transaction
            .put(
                &data,
                format!("late{}", first + i),
                ScalarValue::Int(i as i64),
            )
            .expect("write");
        transaction
            .commit_with(automerge::transaction::CommitOptions::default().with_time(at + i as i64));
    }
}

/// `late` writes made to the old document after `keys` keys, carried into a new one
/// holding those keys (as a snapshot, at time 0) and `newer` writes of its own made
/// after them.
fn replay(keys: usize, late: usize, newer: usize) {
    let old_base = document(keys);
    let base = old_base.get_heads();
    let mut old = old_base.clone();
    writes(&mut old, 0, late, 1_000);
    let after = old.get_heads();

    let mut new = Document::new();
    {
        let mut transaction = new.transaction();
        store::materialize_into(&old_base, &mut transaction, &|_| false).expect("snapshot");
        transaction.commit_with(automerge::transaction::CommitOptions::default().with_time(0));
    }
    writes(&mut new, late, newer, 2_000);

    let took = median(5, || {
        let mut target = new.clone();
        let started = Instant::now();
        std::hint::black_box(replay_newest(
            &old,
            &base,
            &after,
            &mut target,
            &Translator::default(),
            Superseded::Drop,
        ));
        started.elapsed()
    });
    println!(
        "{:<60} {:>10.3} ms",
        format!("replay of {late} writes, {keys} keys, {newer} newer changes"),
        took.as_secs_f64() * 1e3
    );
}

#[test]
#[ignore = "a benchmark: cargo benchmarks"]
fn bench_replay() {
    println!(""); // put reports on new line
    replay(1_000, 100, 100);
    replay(100_000, 100, 100);
    replay(100_000, 1_000, 1_000);
}