1 use criterion::{criterion_group, criterion_main, Criterion};
2 use std::{
3 thread::{self, JoinHandle},
4 time::Instant,
5 };
6 use tracing::{event, Level};
7 use tracing_appender::non_blocking;
8 use tracing_subscriber::fmt::MakeWriter;
9
10 // a no-op writer is used in order to measure the overhead incurred by
11 // tracing-subscriber.
12 #[derive(Clone)]
13 struct NoOpWriter;
14
15 impl NoOpWriter {
new() -> NoOpWriter16 fn new() -> NoOpWriter {
17 NoOpWriter
18 }
19 }
20
21 impl<'a> MakeWriter<'a> for NoOpWriter {
22 type Writer = NoOpWriter;
23
make_writer(&self) -> Self::Writer24 fn make_writer(&self) -> Self::Writer {
25 self.clone()
26 }
27 }
28
29 impl std::io::Write for NoOpWriter {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>30 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
31 Ok(buf.len())
32 }
33
flush(&mut self) -> std::io::Result<()>34 fn flush(&mut self) -> std::io::Result<()> {
35 Ok(())
36 }
37 }
38
synchronous_benchmark(c: &mut Criterion)39 fn synchronous_benchmark(c: &mut Criterion) {
40 let mut group = c.benchmark_group("synchronous");
41 group.bench_function("single_thread", |b| {
42 let subscriber = tracing_subscriber::fmt().with_writer(NoOpWriter::new());
43 tracing::subscriber::with_default(subscriber.finish(), || {
44 b.iter(|| event!(Level::INFO, "event"))
45 });
46 });
47
48 group.bench_function("multiple_writers", |b| {
49 b.iter_custom(|iters| {
50 let mut handles: Vec<JoinHandle<()>> = Vec::new();
51
52 let start = Instant::now();
53
54 let make_writer = NoOpWriter::new();
55 let cloned_make_writer = make_writer.clone();
56
57 handles.push(thread::spawn(move || {
58 let subscriber = tracing_subscriber::fmt().with_writer(make_writer);
59 tracing::subscriber::with_default(subscriber.finish(), || {
60 for _ in 0..iters {
61 event!(Level::INFO, "event");
62 }
63 });
64 }));
65
66 handles.push(thread::spawn(move || {
67 let subscriber = tracing_subscriber::fmt().with_writer(cloned_make_writer);
68 tracing::subscriber::with_default(subscriber.finish(), || {
69 for _ in 0..iters {
70 event!(Level::INFO, "event");
71 }
72 });
73 }));
74
75 for handle in handles {
76 let _ = handle.join();
77 }
78
79 start.elapsed()
80 });
81 });
82 }
83
non_blocking_benchmark(c: &mut Criterion)84 fn non_blocking_benchmark(c: &mut Criterion) {
85 let mut group = c.benchmark_group("non_blocking");
86
87 group.bench_function("single_thread", |b| {
88 let (non_blocking, _guard) = non_blocking(NoOpWriter::new());
89 let subscriber = tracing_subscriber::fmt().with_writer(non_blocking);
90
91 tracing::subscriber::with_default(subscriber.finish(), || {
92 b.iter(|| event!(Level::INFO, "event"))
93 });
94 });
95
96 group.bench_function("multiple_writers", |b| {
97 b.iter_custom(|iters| {
98 let (non_blocking, _guard) = non_blocking(NoOpWriter::new());
99
100 let mut handles: Vec<JoinHandle<()>> = Vec::new();
101
102 let start = Instant::now();
103
104 let cloned_make_writer = non_blocking.clone();
105
106 handles.push(thread::spawn(move || {
107 let subscriber = tracing_subscriber::fmt().with_writer(non_blocking);
108 tracing::subscriber::with_default(subscriber.finish(), || {
109 for _ in 0..iters {
110 event!(Level::INFO, "event");
111 }
112 });
113 }));
114
115 handles.push(thread::spawn(move || {
116 let subscriber = tracing_subscriber::fmt().with_writer(cloned_make_writer);
117 tracing::subscriber::with_default(subscriber.finish(), || {
118 for _ in 0..iters {
119 event!(Level::INFO, "event");
120 }
121 });
122 }));
123
124 for handle in handles {
125 let _ = handle.join();
126 }
127
128 start.elapsed()
129 });
130 });
131 }
132
133 criterion_group!(benches, synchronous_benchmark, non_blocking_benchmark);
134 criterion_main!(benches);
135