nushell/crates/nu-cli/src/commands/benchmark.rs

155 lines
4.1 KiB
Rust
Raw Normal View History

2020-07-19 19:39:43 +02:00
use crate::commands::classified::block::run_block;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
#[cfg(feature = "rich-benchmark")]
use heim::cpu::time;
2020-07-19 19:39:43 +02:00
use nu_errors::ShellError;
use nu_protocol::{hir::Block, Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
use std::convert::TryInto;
use std::time::{Duration, Instant};
2020-07-19 19:39:43 +02:00
pub struct Benchmark;
#[derive(Deserialize, Debug)]
struct BenchmarkArgs {
block: Block,
}
#[async_trait]
impl WholeStreamCommand for Benchmark {
fn name(&self) -> &str {
"benchmark"
}
fn signature(&self) -> Signature {
Signature::build("benchmark").required(
"block",
SyntaxShape::Block,
"the block to run and benchmark",
)
}
fn usage(&self) -> &str {
2020-09-18 08:13:53 +02:00
"Runs a block and returns the time it took to execute it"
2020-07-19 19:39:43 +02:00
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
benchmark(args, registry).await
}
2020-09-18 08:13:53 +02:00
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Benchmarks a command within a block",
example: "benchmark { sleep 500ms }",
result: None,
}]
}
2020-07-19 19:39:43 +02:00
}
#[cfg(feature = "rich-benchmark")]
2020-07-19 19:39:43 +02:00
async fn benchmark(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let tag = raw_args.call_info.args.span;
let mut context = EvaluationContext::from_raw(&raw_args, &registry);
2020-07-19 19:39:43 +02:00
let scope = raw_args.call_info.scope.clone();
let (BenchmarkArgs { block }, input) = raw_args.process(&registry).await?;
let start_time = Instant::now();
let start = time().await;
2020-07-19 19:39:43 +02:00
let result = run_block(
&block,
&mut context,
input,
&scope.it,
&scope.vars,
&scope.env,
)
.await;
let _ = result?.drain_vec().await;
let end = time().await;
let end_time = Instant::now();
context.clear_errors();
if let (Ok(start), Ok(end)) = (start, end) {
let mut indexmap = IndexMap::with_capacity(4);
let real_time = into_value(end_time - start_time, &tag);
indexmap.insert("real time".to_string(), real_time);
let user_time = into_value(end.user() - start.user(), &tag);
indexmap.insert("user time".to_string(), user_time);
let system_time = into_value(end.system() - start.system(), &tag);
indexmap.insert("system time".to_string(), system_time);
let idle_time = into_value(end.idle() - start.idle(), &tag);
indexmap.insert("idle time".to_string(), idle_time);
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
Ok(OutputStream::one(value))
} else {
Err(ShellError::untagged_runtime_error(
"Could not retreive CPU time",
))
}
}
#[cfg(not(feature = "rich-benchmark"))]
async fn benchmark(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let tag = raw_args.call_info.args.span;
let mut context = EvaluationContext::from_raw(&raw_args, &registry);
let scope = raw_args.call_info.scope.clone();
let (BenchmarkArgs { block }, input) = raw_args.process(&registry).await?;
2020-07-19 19:39:43 +02:00
let start_time = Instant::now();
let result = run_block(
&block,
&mut context,
input,
&scope.it,
&scope.vars,
&scope.env,
)
.await;
2020-08-03 00:34:33 +02:00
let _ = result?.drain_vec().await;
let end_time = Instant::now();
2020-08-03 00:34:33 +02:00
context.clear_errors();
let mut indexmap = IndexMap::with_capacity(4);
2020-08-03 00:34:33 +02:00
let real_time = into_value(end_time - start_time, &tag);
indexmap.insert("real time".to_string(), real_time);
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
Ok(OutputStream::one(value))
}
fn into_value<T: TryInto<Duration>>(time: T, tag: &Span) -> Value {
UntaggedValue::duration(
time.try_into()
.unwrap_or_else(|_| Duration::new(0, 0))
.as_nanos()
.into(),
)
.into_value(tag)
2020-07-19 19:39:43 +02:00
}