Skip to main content

open

Function open 

Source
pub fn open(
    host: &str,
) -> Result<(SshChild, BufWriter<ChildStdin>, BufReader<ChildStdout>)>
Examples found in repository?
examples/measure-roundtrips.rs (line 43)
36async fn main() -> Result<()> {
37    let mut args = std::env::args().skip(1);
38    let host = args
39        .next()
40        .context("usage: measure-roundtrips <ssh-host> [remote-dir]")?;
41    let dir = args.next().unwrap_or_else(|| "/usr/include".to_string());
42
43    let (_child, w, r) = transport::open(&host)?;
44    let mut s = Sftp::handshake(w, r).await?;
45    println!("host {host}  sftp v{}", s.version());
46
47    let tau = measure_tau(&mut s).await?;
48    println!("tau (one round trip) = {:.1} ms", ms(tau));
49
50    let entries = list(&mut s, &dir).await?;
51    let files: Vec<String> = entries
52        .iter()
53        .filter(|(name, a)| {
54            !a.is_dir() && !a.is_symlink() && a.size.unwrap_or(0) > 0 && name != "." && name != ".."
55        })
56        .map(|(name, _)| format!("{}/{}", dir.trim_end_matches('/'), name))
57        .collect();
58    ensure!(
59        !files.is_empty(),
60        "no regular non-empty files in {dir}; pass a different remote-dir"
61    );
62    println!(
63        "{} entries in {dir}, {} usable files",
64        entries.len(),
65        files.len()
66    );
67    println!();
68
69    let mut largest = 0usize;
70    let mut largest_open = 0.0f64;
71    let mut largest_read = 0.0f64;
72    let mut prev_read = 0.0f64;
73    let mut last_read = 0.0f64;
74    for n in BATCH_SIZES {
75        if n > files.len() {
76            continue;
77        }
78        let batch = &files[..n];
79        let (t_open, handles) = batch_open(&mut s, batch).await?;
80        let (t_read, bytes) = batch_read(&mut s, &handles).await?;
81        batch_close(&mut s, &handles).await?;
82
83        let open_tau = t_open.as_secs_f64() / tau.as_secs_f64();
84        let read_tau = t_read.as_secs_f64() / tau.as_secs_f64();
85        println!(
86            "n={n:<3} open {:>7.1} ms ({open_tau:>5.2} tau)   read {:>7.1} ms ({read_tau:>5.2} tau)   {bytes} B",
87            ms(t_open),
88            ms(t_read)
89        );
90        largest = n;
91        largest_open = open_tau;
92        largest_read = read_tau;
93        if n > 1 {
94            prev_read = last_read;
95        }
96        last_read = read_tau;
97    }
98
99    println!();
100    ensure!(
101        largest > 1,
102        "only one usable file; cannot distinguish pipelined from serial"
103    );
104    // The verdict rests on opens alone. An open carries a path and nothing else, so
105    // its cost is round trips and only round trips. A read also carries the file,
106    // and under an injected per-packet delay the transfer dominates: 400 KB reads
107    // as 15 tau however few round trips fetched it. Judging on reads made this
108    // check fail on how much data the chosen directory happens to hold, which is
109    // a property of the machine rather than of the code -- exactly the kind of
110    // flaky gate that teaches people to ignore a red check.
111    //
112    // Reads still say something, just not in absolute terms: if doubling the batch
113    // does not double the time, the round trips did not scale either.
114    if prev_read > 0.0 {
115        let growth = largest_read / prev_read;
116        println!(
117            "reads {largest_read:.2} tau at n={largest}, {growth:.2}x the previous batch (serial would be about 2x)"
118        );
119    }
120
121    let serial = largest as f64;
122    if largest_open < serial / PIPELINE_MARGIN {
123        println!(
124            "VERDICT pipelined: opens cost {largest_open:.2} tau at n={largest} (serial would cost about {serial:.0})"
125        );
126        Ok(())
127    } else {
128        bail!(
129            "VERDICT serial: opens cost {largest_open:.2} tau at n={largest}, near the serial cost {serial:.0}. Invariant 1 (O(1) round trips per page) is not reachable over this transport."
130        )
131    }
132}