netperf/common/
ui.rs

1use crate::common::control::TestResults;
2use crate::common::data::Direction;
3use colored::Colorize;
4
5// Bytes
6const KBYTES: usize = 1024;
7const MBYTES: usize = 1024 * KBYTES;
8const GBYTES: usize = 1024 * MBYTES;
9const TBYTES: usize = 1024 * GBYTES;
10
11// Bitrates
12const KBITS: usize = 1000;
13const MBITS: usize = 1000 * KBITS;
14const GBITS: usize = 1000 * MBITS;
15const TBITS: usize = 1000 * GBITS;
16
17pub fn print_header() {
18    println!("[ ID]   Interval          Transfer      Bitrate");
19}
20pub fn print_server_banner(port: u16) {
21    println!("--------------------------------------");
22    println!("{} {}", "Listening on port".cyan(), port);
23    println!("--------------------------------------");
24}
25
26pub fn humanize_bytes(bytes: usize) -> String {
27    if bytes < KBYTES {
28        format!("{} B", bytes)
29    } else if bytes < MBYTES {
30        format!("{:.2} KiB", bytes as f64 / KBYTES as f64)
31    } else if bytes < GBYTES {
32        format!("{:.2} MiB", bytes as f64 / MBYTES as f64)
33    } else if bytes < TBYTES {
34        format!("{:.2} GiB", bytes as f64 / GBYTES as f64)
35    } else {
36        format!("{:.2} TiB", bytes as f64 / TBYTES as f64)
37    }
38}
39
40pub fn humanize_bitrate(bytes: usize, duration_millis: u64) -> String {
41    // For higher accuracy we are getting the actual millis of the duration rather than the
42    // rounded seconds.
43    let bits = bytes * 8;
44    // rate as fraction in seconds;
45    let rate = (bits as f64 / duration_millis as f64) * 1000f64;
46    if rate < KBITS as f64 {
47        format!("{} Bits/sec", rate)
48    } else if bytes < MBITS {
49        format!("{:.2} Kbits/sec", rate / KBITS as f64)
50    } else if bytes < GBITS {
51        format!("{:.2} Mbits/sec", rate / MBITS as f64)
52    } else if bytes < TBITS {
53        format!("{:.2} Gbits/sec", rate / GBITS as f64)
54    } else {
55        format!("{:.2} Tbits/sec", rate / TBITS as f64)
56    }
57}
58
59pub fn print_stats(
60    id: Option<usize>,
61    offset_from_start_millis: u64,
62    duration_millis: u64,
63    bytes_transferred: usize,
64    sender: bool,
65    _syscalls: usize,
66    _block_size: usize,
67) {
68    let end_point = offset_from_start_millis + duration_millis;
69    // Calculating the percentage of
70    println!(
71        "[{:>3}]   {:.2}..{:.2} sec  {}   {}        {}",
72        id.map(|x| x.to_string())
73            .unwrap_or_else(|| "SUM".to_owned()),
74        offset_from_start_millis as f64 / 1000f64,
75        end_point as f64 / 1000f64,
76        humanize_bytes(bytes_transferred),
77        humanize_bitrate(bytes_transferred, duration_millis),
78        if sender {
79            "sender".yellow()
80        } else {
81            "receiver".magenta()
82        },
83    );
84}
85
86pub fn print_summary(
87    local_results: &TestResults,
88    remote_results: &TestResults,
89    direction: &Direction,
90) {
91    println!("- - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
92    print_summary_header();
93    // Streams IDs match between server and client (they make pairs of sender/receiver)
94    // We print each stream sender then receiver. Then the sum of all senders and receivers.
95    let mut sender_duration_millis = 0;
96    let mut receiver_duration_millis = 0;
97    let mut sender_bytes_transferred = 0;
98    let mut receiver_bytes_transferred = 0;
99    for (id, local_stats) in &local_results.streams {
100        print_stats(
101            Some(*id),
102            0,
103            local_stats.duration_millis,
104            local_stats.bytes_transferred,
105            local_stats.sender,
106            local_stats.syscalls,
107            0,
108        );
109        if local_stats.sender {
110            sender_bytes_transferred += local_stats.bytes_transferred;
111            sender_duration_millis =
112                std::cmp::max(sender_duration_millis, local_stats.duration_millis);
113        } else {
114            receiver_bytes_transferred += local_stats.bytes_transferred;
115            receiver_duration_millis =
116                std::cmp::max(receiver_duration_millis, local_stats.duration_millis);
117        }
118        // find the remote counterpart. This is only valuable if we are not using
119        // bidirectional streams. In bidirectional streams we already have the sender
120        // and receiving data.
121        if *direction != Direction::Bidirectional {
122            if let Some(remote_stats) = remote_results.streams.get(id) {
123                print_stats(
124                    Some(*id),
125                    0,
126                    remote_stats.duration_millis,
127                    remote_stats.bytes_transferred,
128                    remote_stats.sender,
129                    remote_stats.syscalls,
130                    0,
131                );
132                if remote_stats.sender {
133                    sender_bytes_transferred += remote_stats.bytes_transferred;
134                    sender_duration_millis =
135                        std::cmp::max(sender_duration_millis, remote_stats.duration_millis);
136                } else {
137                    receiver_bytes_transferred += remote_stats.bytes_transferred;
138                    receiver_duration_millis =
139                        std::cmp::max(receiver_duration_millis, remote_stats.duration_millis);
140                }
141            }
142        }
143    }
144    // if we have more than one stream, let's print a SUM entry as well.
145    if local_results.streams.len() > 1 {
146        println!();
147        print_stats(
148            None,
149            0,
150            sender_duration_millis,
151            sender_bytes_transferred,
152            true,
153            0,
154            0,
155        );
156        print_stats(
157            None,
158            0,
159            receiver_duration_millis,
160            receiver_bytes_transferred,
161            false,
162            0,
163            0,
164        );
165    }
166}
167
168fn print_summary_header() {
169    println!(
170        "{}   {}          {}      {}",
171        "ID".bold(),
172        "Interval".bold(),
173        "Transfer".bold(),
174        "Bitrate".bold()
175    );
176}