1
//! Do not take these benchmarks seriously at the moment.
2
//!
3
//! Proper benchmarks will be coming.
4

            
5
use std::env;
6
use std::fmt::Display;
7

            
8
use chrono::{DateTime, Utc};
9
use criterion::{black_box, criterion_group, criterion_main, Criterion};
10
use fake::faker::filesystem::en::FilePath;
11
use fake::faker::internet::en::Username;
12
use fake::faker::lorem::en::Sentence;
13
use fake::Fake;
14
use rand::rngs::StdRng;
15
use rand::{thread_rng, Rng, SeedableRng};
16
use serde::{Deserialize, Serialize};
17

            
18
470000
#[derive(Serialize, Deserialize, Debug, PartialEq)]
19
pub struct Log {
20
    pub level: Level,
21
    pub user_id: String,
22
    pub timestamp: DateTime<Utc>,
23
    pub request: String,
24
    pub message: Option<String>,
25
    pub code: u16,
26
    pub size: u64,
27
}
28

            
29
170000
#[derive(Serialize, Deserialize, Debug, PartialEq)]
30
pub enum Level {
31
    Trace,
32
    Debug,
33
    Info,
34
    Warn,
35
    Error,
36
}
37

            
38
17
#[derive(Serialize, Deserialize, Debug, PartialEq)]
39
pub struct LogArchive {
40
    entries: Vec<Log>,
41
}
42

            
43
impl Log {
44
10000
    fn generate<R: Rng>(rand: &mut R) -> Self {
45
10000
        Self {
46
10000
            user_id: Username().fake_with_rng(rand),
47
10000
            timestamp: Utc::now(),
48
10000
            code: rand.gen(),
49
10000
            size: rand.gen(),
50
10000
            level: Level::generate(rand),
51
10000
            request: FilePath().fake_with_rng(rand),
52
10000
            message: if rand.gen() {
53
4940
                Some(Sentence(3..100).fake_with_rng(rand))
54
            } else {
55
5060
                None
56
            },
57
        }
58
10000
    }
59
}
60

            
61
impl Level {
62
10000
    fn generate<R: Rng>(rand: &mut R) -> Self {
63
10000
        match rand.gen_range(0_u8..=4u8) {
64
1991
            0 => Level::Trace,
65
1958
            1 => Level::Debug,
66
2103
            2 => Level::Info,
67
2020
            3 => Level::Warn,
68
1928
            4 => Level::Error,
69
            _ => unreachable!(),
70
        }
71
10000
    }
72
}
73

            
74
enum Backend {
75
    Pot,
76
    Cbor,
77
    Bincode,
78
    Msgpack,
79
    MsgpackNamed,
80
}
81

            
82
impl Backend {
83
3
    fn all() -> [Self; 5] {
84
3
        [
85
3
            Self::Pot,
86
3
            Self::Cbor,
87
3
            Self::Bincode,
88
3
            Self::Msgpack,
89
3
            Self::MsgpackNamed,
90
3
        ]
91
3
    }
92
}
93

            
94
impl Display for Backend {
95
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96
15
        f.write_str(match self {
97
3
            Self::Pot => "pot",
98
3
            Self::Cbor => "cbor",
99
3
            Self::Bincode => "bincode",
100
3
            Self::Msgpack => "MessagePack",
101
3
            Self::MsgpackNamed => "MessagePack(named)",
102
        })
103
15
    }
104
}
105

            
106
const LOG_ENTRIES: usize = 10_000;
107

            
108
1
fn bench_logs(c: &mut Criterion) {
109
1
    let mut logs = LogArchive {
110
1
        entries: Vec::with_capacity(LOG_ENTRIES),
111
1
    };
112
2
    let random_seed = env::args().find(|arg| arg.starts_with("-s")).map_or_else(
113
1
        || thread_rng().gen(),
114
1
        |seed| {
115
            let (_, seed) = seed.split_at(2);
116
            let (upper, lower) = if seed.len() > 32 {
117
                let (upper, lower) = seed.split_at(seed.len() - 32);
118
                (
119
                    u128::from_str_radix(upper, 16).expect("invalid hexadecimal seed"),
120
                    u128::from_str_radix(lower, 16).expect("invalid hexadecimal seed"),
121
                )
122
            } else {
123
                (
124
                    0,
125
                    u128::from_str_radix(seed, 16).expect("invalid hexadecimal seed"),
126
                )
127
            };
128
            let mut seed = [0; 32];
129
            seed[..16].copy_from_slice(&upper.to_be_bytes());
130
            seed[16..].copy_from_slice(&lower.to_be_bytes());
131
            seed
132
1
        },
133
1
    );
134
1
    print!("Using random seed -s");
135
33
    for b in random_seed {
136
32
        print!("{b:02x}");
137
32
    }
138
1
    println!();
139
1
    let mut rng = StdRng::from_seed(random_seed);
140
10001
    for _ in 0..LOG_ENTRIES {
141
10000
        logs.entries.push(Log::generate(&mut rng));
142
10000
    }
143

            
144
1
    let mut serialize_group = c.benchmark_group("logs/serialize");
145
6
    for backend in Backend::all() {
146
5
        let serialize = match backend {
147
1
            Backend::Pot => |logs| pot::to_vec(logs).unwrap(),
148
1
            Backend::Cbor => |logs| {
149
1
                let mut cbor_bytes = Vec::new();
150
1
                ciborium::ser::into_writer(&logs, &mut cbor_bytes).unwrap();
151
1
                cbor_bytes
152
1
            },
153
1
            Backend::Bincode => |logs| bincode::serialize(logs).unwrap(),
154
1
            Backend::Msgpack => |logs| rmp_serde::to_vec(logs).unwrap(),
155
1
            Backend::MsgpackNamed => |logs| rmp_serde::to_vec_named(logs).unwrap(),
156
        };
157
5
        serialize_group.bench_function(backend.to_string(), |b| {
158
5
            b.iter(|| {
159
5
                serialize(black_box(&logs));
160
5
            });
161
5
        });
162
    }
163
1
    drop(serialize_group);
164
1

            
165
1
    let mut buffer = Vec::with_capacity(LOG_ENTRIES * 1024);
166
1
    let mut serialize_reuse_group = c.benchmark_group("logs/serialize-reuse");
167
6
    for backend in Backend::all() {
168
5
        let serialize = match backend {
169
1
            Backend::Pot => pot_serialize_into,
170
1
            Backend::Cbor => cbor_serialize_into,
171
1
            Backend::Bincode => bincode_serialize_into,
172
1
            Backend::Msgpack => msgpack_serialize_into,
173
1
            Backend::MsgpackNamed => msgpack_serialize_into_named,
174
        };
175

            
176
5
        serialize_reuse_group.bench_function(backend.to_string(), |b| {
177
5
            b.iter(|| {
178
5
                buffer.clear();
179
5
                serialize(black_box(&logs), black_box(&mut buffer));
180
5
            });
181
5
        });
182
    }
183
1
    drop(serialize_reuse_group);
184
1

            
185
1
    let mut deserialize_group = c.benchmark_group("logs/deserialize");
186

            
187
6
    for backend in Backend::all() {
188
5
        let deserialize = match backend {
189
1
            Backend::Pot => |logs| pot::from_slice::<LogArchive>(logs).unwrap(),
190
1
            Backend::Cbor => |logs| ciborium::de::from_reader(logs).unwrap(),
191
1
            Backend::Bincode => |logs| bincode::deserialize(logs).unwrap(),
192
2
            Backend::Msgpack | Backend::MsgpackNamed => |logs| rmp_serde::from_slice(logs).unwrap(),
193
        };
194
5
        let bytes = match backend {
195
1
            Backend::Pot => pot::to_vec(&logs).unwrap(),
196
            Backend::Cbor => {
197
1
                let mut cbor_bytes = Vec::new();
198
1
                ciborium::ser::into_writer(&logs, &mut cbor_bytes).unwrap();
199
1
                cbor_bytes
200
            }
201
1
            Backend::Bincode => bincode::serialize(&logs).unwrap(),
202
1
            Backend::Msgpack => rmp_serde::to_vec(&logs).unwrap(),
203
1
            Backend::MsgpackNamed => rmp_serde::to_vec_named(&logs).unwrap(),
204
        };
205
5
        deserialize_group.bench_function(backend.to_string(), |b| {
206
5
            b.iter(|| {
207
5
                deserialize(black_box(&bytes));
208
5
            });
209
5
        });
210
    }
211
1
}
212

            
213
1
fn pot_serialize_into(logs: &LogArchive, buffer: &mut Vec<u8>) {
214
1
    pot::to_writer(logs, buffer).unwrap();
215
1
}
216

            
217
1
fn cbor_serialize_into(logs: &LogArchive, buffer: &mut Vec<u8>) {
218
1
    ciborium::ser::into_writer(logs, buffer).unwrap();
219
1
}
220

            
221
1
fn bincode_serialize_into(logs: &LogArchive, buffer: &mut Vec<u8>) {
222
1
    bincode::serialize_into(buffer, logs).unwrap();
223
1
}
224

            
225
1
fn msgpack_serialize_into(logs: &LogArchive, buffer: &mut Vec<u8>) {
226
1
    rmp_serde::encode::write(buffer, logs).unwrap()
227
1
}
228

            
229
1
fn msgpack_serialize_into_named(logs: &LogArchive, buffer: &mut Vec<u8>) {
230
1
    rmp_serde::encode::write_named(buffer, logs).unwrap()
231
1
}
232

            
233
1
fn criterion_benchmark(c: &mut Criterion) {
234
1
    bench_logs(c)
235
1
}
236

            
237
criterion_group!(benches, criterion_benchmark);
238
criterion_main!(benches);