I was checking with callgrind and a considerable amount of energy of the software is concentrated (obviously) on message parsing.
Timestamp is a low-hanging fruit on our side, here is a quick investigation of replacing chrono and dealing with SystemTime ourselves:
Testing approaches with criterion

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
fn generate_timestamp_direct() -> u64 {
let now = SystemTime::now();
let duration = now.duration_since(UNIX_EPOCH).unwrap();
let secs = duration.as_secs();
let nanos = duration.subsec_nanos();
secs * 1_000_000 + (nanos / 1_000) as u64
}
fn generate_timestamp_with_as_micros() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_micros() as u64)
.unwrap()
}
fn generate_timestamp_with_as_micros_alt() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_micros().try_into().unwrap())
.unwrap()
}
fn generate_timestamp_using_chrono() -> u64 {
chrono::Utc::now().timestamp_micros() as u64
}
fn benchmark_timestamp(c: &mut Criterion) {
let mut group = c.benchmark_group("generate_timestamp");
group.bench_function("generate_timestamp_direct", |b| {
b.iter(|| generate_timestamp_direct())
});
group.bench_function("generate_timestamp_with_as_micros", |b| {
b.iter(|| generate_timestamp_with_as_micros())
});
group.bench_function("generate_timestamp_with_as_micros_alt", |b| {
b.iter(|| generate_timestamp_with_as_micros_alt())
});
group.bench_function("generate_timestamp_using_chrono", |b| {
b.iter(|| generate_timestamp_using_chrono())
});
}
criterion_group!(benches, benchmark_timestamp);
criterion_main!(benches);
Results in callgrind:

I was checking with callgrind and a considerable amount of energy of the software is concentrated (obviously) on message parsing.
Timestamp is a low-hanging fruit on our side, here is a quick investigation of replacing chrono and dealing with SystemTime ourselves:
Testing approaches with criterion
Results in callgrind: