use crate::output;
use crate::output::{
fallback_to_anonymous_on_invalid_label, ComparedStdout, LabeledOutput, Output, SimpleStdout,
};
use std::time::{Duration, Instant};
pub fn run_timed<T, F: FnMut() -> T>(mut closure: F) -> Duration {
let start = Instant::now();
(closure)();
Instant::now().duration_since(start)
}
pub fn run_timed_times<T, F: FnMut() -> T>(iterations: usize, mut closure: F) -> TimingData {
let mut elapsed = Duration::ZERO;
let mut min_nanos = u128::MAX;
let mut max_nanos = 0;
for _ in 0..iterations {
let start = Instant::now();
closure();
let run_elapsed = Instant::now().duration_since(start);
let run_elapsed_nanos = run_elapsed.as_nanos();
if run_elapsed_nanos < min_nanos {
min_nanos = run_elapsed_nanos;
}
if run_elapsed_nanos > max_nanos {
max_nanos = run_elapsed_nanos;
}
elapsed += run_elapsed;
}
TimingData {
iterations: iterations as u128,
min_nanos,
max_nanos,
elapsed: elapsed.as_nanos(),
}
}
pub fn run_timed_from_iterator<T, R, F: FnMut(R) -> T, It>(
iterator: It,
mut closure: F,
) -> TimingData
where
It: Iterator<Item = R>,
{
let mut elapsed = Duration::ZERO;
let mut min_nanos = u128::MAX;
let mut max_nanos = 0;
let mut iterations = 0;
for v in iterator {
let start = Instant::now();
closure(v);
let run_elapsed = Instant::now().duration_since(start);
let run_elapsed_nanos = run_elapsed.as_nanos();
if run_elapsed_nanos < min_nanos {
min_nanos = run_elapsed_nanos;
}
if run_elapsed_nanos > max_nanos {
max_nanos = run_elapsed_nanos;
}
elapsed += run_elapsed;
iterations += 1;
}
TimingData {
iterations,
min_nanos,
max_nanos,
elapsed: elapsed.as_nanos(),
}
}
#[derive(Copy, Clone, Debug)]
#[cfg(feature = "timer")]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct TimingData {
pub min_nanos: u128,
pub max_nanos: u128,
pub elapsed: u128,
pub iterations: u128,
}
#[cfg(feature = "timer")]
impl TimingData {
pub fn pretty_print(&self) {
output::print_timer_header("anonymous", self);
output::timer_print_elapsed(
self.min_nanos as f64,
self.elapsed as f64 / self.iterations as f64,
self.max_nanos as f64,
);
}
}
pub trait Timeable<It, T>: Sized
where
It: Iterator<Item = T>,
{
fn timed(self) -> TimedIterator<It, T, SimpleStdout> {
self.timed_labeled("anonymous")
}
fn timed_labeled(self, label: &'static str) -> TimedIterator<It, T, SimpleStdout>;
fn timed_persisted(self) -> TimedIterator<It, T, ComparedStdout> {
self.timed_persisted_labeled("anonymous")
}
fn timed_persisted_labeled(self, label: &'static str) -> TimedIterator<It, T, ComparedStdout>;
}
impl<It, T> Timeable<It, T> for It
where
It: Iterator<Item = T>,
{
fn timed_labeled(self, label: &'static str) -> TimedIterator<It, T, SimpleStdout> {
TimedIterator::new(
self,
LabeledOutput::new(fallback_to_anonymous_on_invalid_label(label), SimpleStdout),
)
}
fn timed_persisted_labeled(self, label: &'static str) -> TimedIterator<It, T, ComparedStdout> {
TimedIterator::new(
self,
LabeledOutput::new(
fallback_to_anonymous_on_invalid_label(label),
ComparedStdout,
),
)
}
}
pub struct TimedIterator<It, T, O>
where
It: Iterator<Item = T>,
{
inner: It,
iterations: u128,
min_nanos: u128,
max_nanos: u128,
elapsed: Duration,
out: LabeledOutput<O>,
}
impl<It, T, O> TimedIterator<It, T, O>
where
It: Iterator<Item = T>,
{
fn new(inner: It, out: LabeledOutput<O>) -> Self {
TimedIterator {
inner,
iterations: 0,
min_nanos: u128::MAX,
max_nanos: 0,
elapsed: Duration::ZERO,
out,
}
}
}
impl<It, T, O> Iterator for TimedIterator<It, T, O>
where
It: Iterator<Item = T>,
O: Output,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let start = Instant::now();
let maybe_item = self.inner.next();
let run_elapsed = Instant::now().duration_since(start);
if let Some(item) = maybe_item {
let run_elapsed_nanos = run_elapsed.as_nanos();
if run_elapsed_nanos < self.min_nanos {
self.min_nanos = run_elapsed_nanos;
}
if run_elapsed_nanos > self.max_nanos {
self.max_nanos = run_elapsed_nanos;
}
self.elapsed += run_elapsed;
self.iterations += 1;
Some(item)
} else {
self.out.dump(TimingData {
min_nanos: self.min_nanos,
max_nanos: self.max_nanos,
elapsed: self.elapsed.as_nanos(),
iterations: self.iterations,
});
None
}
}
}
#[cfg(test)]
#[cfg(feature = "timer")]
mod tests {
use crate::timing::Timeable;
#[test]
fn time_iterator() {
let _v: Vec<i32> = (0..100).timed().chain(0..10_000).timed().collect();
}
#[test]
fn time_persisted_iterator() {
for _ in 0..2 {
let _v: Vec<i32> = (0..1_000_000).timed_persisted().collect();
}
}
#[test]
fn time_persisted_labled() {
for _ in 0..2 {
let _v: Vec<i32> = (0..1_000_000).timed_persisted_labeled("my_test").collect();
}
}
}