add some startup performance metrics (#7851)

# Description

This PR changes the old performance logging with `Instant` timers. I'm
not sure if this is the best way to do it but it does help reveal where
time is being spent on startup. This is what it looks like when you
launch nushell with `cargo run -- --log-level info`. I'm using the
`info` log level exclusively for performance monitoring at this point.

![image](https://user-images.githubusercontent.com/343840/214372903-fdfa9c99-b846-47f3-8faf-bd6ed98df3a9.png)
## After Startup

Since you're in the repl, you can continue running commands. Here's the
output of `ls`, for instance.

![image](https://user-images.githubusercontent.com/343840/214373035-4d2f6e2d-5c1d-43d3-b997-51d79d496ba3.png)
Note that the above screenshots are in debug mode, so they're much
slower than release.

# User-Facing Changes



# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
Darren Schroeder 2023-01-24 14:28:59 -06:00 committed by GitHub
parent 41306aa7e0
commit d64e381085
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 202 additions and 94 deletions

1
Cargo.lock generated
View File

@ -2984,6 +2984,7 @@ name = "nu-utils"
version = "0.74.1" version = "0.74.1"
dependencies = [ dependencies = [
"crossterm_winapi", "crossterm_winapi",
"log",
"lscolors", "lscolors",
"num-format", "num-format",
"strip-ansi-escapes", "strip-ansi-escapes",

View File

@ -1,7 +1,5 @@
use crate::util::{eval_source, report_error}; use crate::util::{eval_source, report_error};
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use log::info;
#[cfg(feature = "plugin")]
use nu_parser::ParseError; use nu_parser::ParseError;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use nu_path::canonicalize_with; use nu_path::canonicalize_with;
@ -9,6 +7,8 @@ use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use nu_protocol::Spanned; use nu_protocol::Spanned;
use nu_protocol::{HistoryFileFormat, PipelineData}; use nu_protocol::{HistoryFileFormat, PipelineData};
#[cfg(feature = "plugin")]
use nu_utils::utils::perf;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
@ -24,6 +24,8 @@ pub fn read_plugin_file(
plugin_file: Option<Spanned<String>>, plugin_file: Option<Spanned<String>>,
storage_path: &str, storage_path: &str,
) { ) {
let start_time = std::time::Instant::now();
let mut plug_path = String::new();
// Reading signatures from signature file // Reading signatures from signature file
// The plugin.nu file stores the parsed signature collected from each registered plugin // The plugin.nu file stores the parsed signature collected from each registered plugin
add_plugin_file(engine_state, plugin_file, storage_path); add_plugin_file(engine_state, plugin_file, storage_path);
@ -31,7 +33,7 @@ pub fn read_plugin_file(
let plugin_path = engine_state.plugin_signatures.clone(); let plugin_path = engine_state.plugin_signatures.clone();
if let Some(plugin_path) = plugin_path { if let Some(plugin_path) = plugin_path {
let plugin_filename = plugin_path.to_string_lossy(); let plugin_filename = plugin_path.to_string_lossy();
plug_path = plugin_filename.to_string();
if let Ok(contents) = std::fs::read(&plugin_path) { if let Ok(contents) = std::fs::read(&plugin_path) {
eval_source( eval_source(
engine_state, engine_state,
@ -43,7 +45,13 @@ pub fn read_plugin_file(
} }
} }
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!()); perf(
&format!("read_plugin_file {}", &plug_path),
start_time,
file!(),
line!(),
column!(),
);
} }
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]

View File

@ -6,7 +6,7 @@ use crate::{
NuHighlighter, NuValidator, NushellPrompt, NuHighlighter, NuValidator, NushellPrompt,
}; };
use crossterm::cursor::CursorShape; use crossterm::cursor::CursorShape;
use log::{info, trace, warn}; use log::{trace, warn};
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use nu_color_config::StyleComputer; use nu_color_config::StyleComputer;
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return}; use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
@ -18,6 +18,7 @@ use nu_protocol::{
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span, format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
Spanned, Type, Value, VarId, Spanned, Type, Value, VarId,
}; };
use nu_utils::utils::perf;
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi}; use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
use std::{ use std::{
io::{self, Write}, io::{self, Write},
@ -41,7 +42,7 @@ pub fn evaluate_repl(
stack: &mut Stack, stack: &mut Stack,
nushell_path: &str, nushell_path: &str,
prerun_command: Option<Spanned<String>>, prerun_command: Option<Spanned<String>>,
start_time: Instant, entire_start_time: Instant,
) -> Result<()> { ) -> Result<()> {
use reedline::{FileBackedHistory, Reedline, Signal}; use reedline::{FileBackedHistory, Reedline, Signal};
@ -59,18 +60,19 @@ pub fn evaluate_repl(
let mut nu_prompt = NushellPrompt::new(); let mut nu_prompt = NushellPrompt::new();
info!( let start_time = std::time::Instant::now();
"translate environment vars {}:{}:{}",
file!(),
line!(),
column!()
);
// Translate environment variables from Strings to Values // Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) { if let Some(e) = convert_env_values(engine_state, stack) {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e); report_error(&working_set, &e);
} }
perf(
"translate env vars",
start_time,
file!(),
line!(),
column!(),
);
// seed env vars // seed env vars
stack.add_env_var( stack.add_env_var(
@ -80,15 +82,7 @@ pub fn evaluate_repl(
stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(0, Span::unknown())); stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(0, Span::unknown()));
info!( let mut start_time = std::time::Instant::now();
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
let mut line_editor = Reedline::create(); let mut line_editor = Reedline::create();
// Now that reedline is created, get the history session id and store it in engine_state // Now that reedline is created, get the history session id and store it in engine_state
@ -97,16 +91,16 @@ pub fn evaluate_repl(
.map(i64::from) .map(i64::from)
.unwrap_or(0); .unwrap_or(0);
engine_state.history_session_id = hist_sesh; engine_state.history_session_id = hist_sesh;
perf("setup reedline", start_time, file!(), line!(), column!());
let config = engine_state.get_config(); let config = engine_state.get_config();
start_time = std::time::Instant::now();
let history_path = crate::config_files::get_history_path( let history_path = crate::config_files::get_history_path(
nushell_path, nushell_path,
engine_state.config.history_file_format, engine_state.config.history_file_format,
); );
if let Some(history_path) = history_path.as_deref() { if let Some(history_path) = history_path.as_deref() {
info!("setup history {}:{}:{}", file!(), line!(), column!());
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format { let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
HistoryFileFormat::PlainText => Box::new( HistoryFileFormat::PlainText => Box::new(
FileBackedHistory::with_file( FileBackedHistory::with_file(
@ -121,7 +115,9 @@ pub fn evaluate_repl(
}; };
line_editor = line_editor.with_history(history); line_editor = line_editor.with_history(history);
}; };
perf("setup history", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let sys = sysinfo::System::new(); let sys = sysinfo::System::new();
let show_banner = config.show_banner; let show_banner = config.show_banner;
@ -134,6 +130,13 @@ pub fn evaluate_repl(
println!("{}", nu_utils::strip_ansi_string_likely(banner)); println!("{}", nu_utils::strip_ansi_string_likely(banner));
} }
} }
perf(
"get sysinfo/show banner",
start_time,
file!(),
line!(),
column!(),
);
if let Some(s) = prerun_command { if let Some(s) = prerun_command {
eval_source( eval_source(
@ -147,35 +150,35 @@ pub fn evaluate_repl(
} }
loop { loop {
info!( let loop_start_time = std::time::Instant::now();
"load config each loop {}:{}:{}",
file!(),
line!(),
column!()
);
let cwd = get_guaranteed_cwd(engine_state, stack); let cwd = get_guaranteed_cwd(engine_state, stack);
start_time = std::time::Instant::now();
// Before doing anything, merge the environment from the previous REPL iteration into the // Before doing anything, merge the environment from the previous REPL iteration into the
// permanent state. // permanent state.
if let Err(err) = engine_state.merge_env(stack, cwd) { if let Err(err) = engine_state.merge_env(stack, cwd) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
perf("merge env", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
//Reset the ctrl-c handler //Reset the ctrl-c handler
if let Some(ctrlc) = &mut engine_state.ctrlc { if let Some(ctrlc) = &mut engine_state.ctrlc {
ctrlc.store(false, Ordering::SeqCst); ctrlc.store(false, Ordering::SeqCst);
} }
perf("reset ctrlc", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
// Reset the SIGQUIT handler // Reset the SIGQUIT handler
if let Some(sig_quit) = engine_state.get_sig_quit() { if let Some(sig_quit) = engine_state.get_sig_quit() {
sig_quit.store(false, Ordering::SeqCst); sig_quit.store(false, Ordering::SeqCst);
} }
perf("reset sig_quit", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let config = engine_state.get_config(); let config = engine_state.get_config();
info!("setup colors {}:{}:{}", file!(), line!(), column!());
info!("update reedline {}:{}:{}", file!(), line!(), column!());
let engine_reference = std::sync::Arc::new(engine_state.clone()); let engine_reference = std::sync::Arc::new(engine_state.clone());
// Find the configured cursor shapes for each mode // Find the configured cursor shapes for each mode
@ -188,6 +191,15 @@ pub fn evaluate_repl(
)), )),
emacs: Some(map_nucursorshape_to_cursorshape(config.cursor_shape_emacs)), emacs: Some(map_nucursorshape_to_cursorshape(config.cursor_shape_emacs)),
}; };
perf(
"get config/cursor config",
start_time,
file!(),
line!(),
column!(),
);
start_time = std::time::Instant::now();
line_editor = line_editor line_editor = line_editor
.with_highlighter(Box::new(NuHighlighter { .with_highlighter(Box::new(NuHighlighter {
@ -205,9 +217,11 @@ pub fn evaluate_repl(
.with_partial_completions(config.partial_completions) .with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring) .with_ansi_colors(config.use_ansi_coloring)
.with_cursor_config(cursor_config); .with_cursor_config(cursor_config);
perf("reedline builder", start_time, file!(), line!(), column!());
let style_computer = StyleComputer::from_config(engine_state, stack); let style_computer = StyleComputer::from_config(engine_state, stack);
start_time = std::time::Instant::now();
line_editor = if config.use_ansi_coloring { line_editor = if config.use_ansi_coloring {
line_editor.with_hinter(Box::new({ line_editor.with_hinter(Box::new({
// As of Nov 2022, "hints" color_config closures only get `null` passed in. // As of Nov 2022, "hints" color_config closures only get `null` passed in.
@ -217,13 +231,23 @@ pub fn evaluate_repl(
} else { } else {
line_editor.disable_hints() line_editor.disable_hints()
}; };
perf(
"reedline coloring/style_computer",
start_time,
file!(),
line!(),
column!(),
);
start_time = std::time::Instant::now();
line_editor = add_menus(line_editor, engine_reference, stack, config).unwrap_or_else(|e| { line_editor = add_menus(line_editor, engine_reference, stack, config).unwrap_or_else(|e| {
let working_set = StateWorkingSet::new(engine_state); let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e); report_error(&working_set, &e);
Reedline::create() Reedline::create()
}); });
perf("reedline menus", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let buffer_editor = if !config.buffer_editor.is_empty() { let buffer_editor = if !config.buffer_editor.is_empty() {
Some(config.buffer_editor.clone()) Some(config.buffer_editor.clone())
} else { } else {
@ -244,17 +268,23 @@ pub fn evaluate_repl(
} else { } else {
line_editor line_editor
}; };
perf(
"reedline buffer_editor",
start_time,
file!(),
line!(),
column!(),
);
start_time = std::time::Instant::now();
if config.sync_history_on_enter { if config.sync_history_on_enter {
info!("sync history {}:{}:{}", file!(), line!(), column!());
if let Err(e) = line_editor.sync_history() { if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {}", e); warn!("Failed to sync history: {}", e);
} }
} }
perf("sync_history", start_time, file!(), line!(), column!());
info!("setup keybindings {}:{}:{}", file!(), line!(), column!()); start_time = std::time::Instant::now();
// Changing the line editor based on the found keybindings // Changing the line editor based on the found keybindings
line_editor = match create_keybindings(config) { line_editor = match create_keybindings(config) {
Ok(keybindings) => match keybindings { Ok(keybindings) => match keybindings {
@ -276,9 +306,9 @@ pub fn evaluate_repl(
line_editor line_editor
} }
}; };
perf("keybindings", start_time, file!(), line!(), column!());
info!("prompt_update {}:{}:{}", file!(), line!(), column!()); start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user, // Right before we start our prompt and take input from the user,
// fire the "pre_prompt" hook // fire the "pre_prompt" hook
if let Some(hook) = config.hooks.pre_prompt.clone() { if let Some(hook) = config.hooks.pre_prompt.clone() {
@ -286,7 +316,9 @@ pub fn evaluate_repl(
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
} }
perf("pre-prompt hook", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for // Next, check all the environment variables they ask for
// fire the "env_change" hook // fire the "env_change" hook
let config = engine_state.get_config(); let config = engine_state.get_config();
@ -295,26 +327,23 @@ pub fn evaluate_repl(
{ {
report_error_new(engine_state, &error) report_error_new(engine_state, &error)
} }
perf("env-change hook", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let config = engine_state.get_config(); let config = engine_state.get_config();
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt); let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
perf("update_prompt", start_time, file!(), line!(), column!());
entry_num += 1; entry_num += 1;
info!(
"finished setup, starting repl {}:{}:{}",
file!(),
line!(),
column!()
);
if entry_num == 1 && show_banner { if entry_num == 1 && show_banner {
println!( println!(
"Startup Time: {}", "Startup Time: {}",
format_duration(start_time.elapsed().as_nanos() as i64) format_duration(entire_start_time.elapsed().as_nanos() as i64)
); );
} }
start_time = std::time::Instant::now();
let input = line_editor.read_line(prompt); let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration; let shell_integration = config.shell_integration;
@ -550,6 +579,21 @@ pub fn evaluate_repl(
} }
} }
} }
perf(
"processing line editor input",
start_time,
file!(),
line!(),
column!(),
);
perf(
"finished repl loop",
loop_start_time,
file!(),
line!(),
column!(),
);
} }
Ok(()) Ok(())

View File

@ -9,6 +9,7 @@ use nu_protocol::{
}; };
#[cfg(windows)] #[cfg(windows)]
use nu_utils::enable_vt_processing; use nu_utils::enable_vt_processing;
use nu_utils::utils::perf;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
// This will collect environment variables from std::env and adds them to a stack. // This will collect environment variables from std::env and adds them to a stack.
@ -204,6 +205,8 @@ pub fn eval_source(
fname: &str, fname: &str,
input: PipelineData, input: PipelineData,
) -> bool { ) -> bool {
let start_time = std::time::Instant::now();
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse( let (output, err) = parse(
@ -282,6 +285,13 @@ pub fn eval_source(
return false; return false;
} }
} }
perf(
&format!("eval_source {}", &fname),
start_time,
file!(),
line!(),
column!(),
);
true true
} }

View File

@ -1,10 +1,10 @@
[package] [package]
authors = ["The Nushell Project Developers"] authors = ["The Nushell Project Developers"]
description = "Nushell utility functions" description = "Nushell utility functions"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-utils"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-utils" name = "nu-utils"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-utils"
version = "0.74.1" version = "0.74.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,10 +13,11 @@ name = "utils"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
log = "0.4"
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false } lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
num-format = { version = "0.4.3" } num-format = { version = "0.4.3" }
sys-locale = "0.2.1"
strip-ansi-escapes = "0.1.1" strip-ansi-escapes = "0.1.1"
sys-locale = "0.2.1"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
crossterm_winapi = "0.9.0" crossterm_winapi = "0.9.0"

File diff suppressed because one or more lines are too long

View File

@ -110,8 +110,6 @@ pub(crate) fn read_config_file(
eval_config_contents(config_path, engine_state, stack); eval_config_contents(config_path, engine_state, stack);
} }
info!("read_config_file {}:{}:{}", file!(), line!(), column!());
} }
pub(crate) fn read_loginshell_file(engine_state: &mut EngineState, stack: &mut Stack) { pub(crate) fn read_loginshell_file(engine_state: &mut EngineState, stack: &mut Stack) {
@ -200,8 +198,6 @@ pub(crate) fn setup_config(
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
read_plugin_file(engine_state, stack, plugin_file, NUSHELL_FOLDER); read_plugin_file(engine_state, stack, plugin_file, NUSHELL_FOLDER);
info!("read_config_file {}:{}:{}", file!(), line!(), column!());
read_config_file(engine_state, stack, env_file, true); read_config_file(engine_state, stack, env_file, true);
read_config_file(engine_state, stack, config_file, false); read_config_file(engine_state, stack, config_file, false);

View File

@ -15,7 +15,7 @@ use crate::{
logger::{configure, logger}, logger::{configure, logger},
terminal::acquire_terminal, terminal::acquire_terminal,
}; };
use log::{info, Level}; use log::Level;
use miette::Result; use miette::Result;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use nu_cli::read_plugin_file; use nu_cli::read_plugin_file;
@ -26,16 +26,17 @@ use nu_cli::{
use nu_command::create_default_context; use nu_command::create_default_context;
use nu_parser::{escape_for_script_arg, escape_quote_string}; use nu_parser::{escape_for_script_arg, escape_quote_string};
use nu_protocol::{util::BufferedReader, PipelineData, RawStream}; use nu_protocol::{util::BufferedReader, PipelineData, RawStream};
use nu_utils::utils::perf;
use signals::{ctrlc_protection, sigquit_protection}; use signals::{ctrlc_protection, sigquit_protection};
use std::{ use std::{
io::BufReader, io::BufReader,
str::FromStr, str::FromStr,
sync::{atomic::AtomicBool, Arc}, sync::{atomic::AtomicBool, Arc},
time::Instant,
}; };
fn main() -> Result<()> { fn main() -> Result<()> {
let start_time = Instant::now(); let entire_start_time = std::time::Instant::now();
let mut start_time = std::time::Instant::now();
let miette_hook = std::panic::take_hook(); let miette_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |x| { std::panic::set_hook(Box::new(move |x| {
crossterm::terminal::disable_raw_mode().expect("unable to disable raw mode"); crossterm::terminal::disable_raw_mode().expect("unable to disable raw mode");
@ -113,37 +114,6 @@ fn main() -> Result<()> {
let parsed_nu_cli_args = parse_commandline_args(&nushell_commandline_args, &mut engine_state) let parsed_nu_cli_args = parse_commandline_args(&nushell_commandline_args, &mut engine_state)
.unwrap_or_else(|_| std::process::exit(1)); .unwrap_or_else(|_| std::process::exit(1));
set_config_path(
&mut engine_state,
&init_cwd,
"config.nu",
"config-path",
&parsed_nu_cli_args.config_file,
);
set_config_path(
&mut engine_state,
&init_cwd,
"env.nu",
"env-path",
&parsed_nu_cli_args.env_file,
);
// keep this condition in sync with the branches below
acquire_terminal(
parsed_nu_cli_args.commands.is_none()
&& (script_name.is_empty() || parsed_nu_cli_args.interactive_shell.is_some()),
);
if let Some(t) = parsed_nu_cli_args.threads {
// 0 means to let rayon decide how many threads to use
let threads = t.as_i64().unwrap_or(0);
rayon::ThreadPoolBuilder::new()
.num_threads(threads as usize)
.build_global()
.expect("error setting number of threads");
}
if let Some(level) = parsed_nu_cli_args.log_level.map(|level| level.item) { if let Some(level) = parsed_nu_cli_args.log_level.map(|level| level.item) {
let level = if Level::from_str(&level).is_ok() { let level = if Level::from_str(&level).is_ok() {
level level
@ -159,9 +129,48 @@ fn main() -> Result<()> {
.unwrap_or_else(|| "stderr".to_string()); .unwrap_or_else(|| "stderr".to_string());
logger(|builder| configure(&level, &target, builder))?; logger(|builder| configure(&level, &target, builder))?;
info!("start logging {}:{}:{}", file!(), line!(), column!()); // info!("start logging {}:{}:{}", file!(), line!(), column!());
perf("start logging", start_time, file!(), line!(), column!());
} }
start_time = std::time::Instant::now();
set_config_path(
&mut engine_state,
&init_cwd,
"config.nu",
"config-path",
&parsed_nu_cli_args.config_file,
);
set_config_path(
&mut engine_state,
&init_cwd,
"env.nu",
"env-path",
&parsed_nu_cli_args.env_file,
);
perf("set_config_path", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
// keep this condition in sync with the branches below
acquire_terminal(
parsed_nu_cli_args.commands.is_none()
&& (script_name.is_empty() || parsed_nu_cli_args.interactive_shell.is_some()),
);
perf("acquire_terminal", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
if let Some(t) = parsed_nu_cli_args.threads {
// 0 means to let rayon decide how many threads to use
let threads = t.as_i64().unwrap_or(0);
rayon::ThreadPoolBuilder::new()
.num_threads(threads as usize)
.build_global()
.expect("error setting number of threads");
}
perf("set rayon threads", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
if let Some(testbin) = &parsed_nu_cli_args.testbin { if let Some(testbin) = &parsed_nu_cli_args.testbin {
// Call out to the correct testbin // Call out to the correct testbin
match testbin.item.as_str() { match testbin.item.as_str() {
@ -181,6 +190,9 @@ fn main() -> Result<()> {
} }
std::process::exit(0) std::process::exit(0)
} }
perf("run test_bins", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let input = if let Some(redirect_stdin) = &parsed_nu_cli_args.redirect_stdin { let input = if let Some(redirect_stdin) = &parsed_nu_cli_args.redirect_stdin {
let stdin = std::io::stdin(); let stdin = std::io::stdin();
let buf_reader = BufReader::new(stdin); let buf_reader = BufReader::new(stdin);
@ -201,15 +213,17 @@ fn main() -> Result<()> {
} else { } else {
PipelineData::empty() PipelineData::empty()
}; };
perf("redirect stdin", start_time, file!(), line!(), column!());
info!("redirect_stdin {}:{}:{}", file!(), line!(), column!()); start_time = std::time::Instant::now();
// First, set up env vars as strings only // First, set up env vars as strings only
gather_parent_env_vars(&mut engine_state, &init_cwd); gather_parent_env_vars(&mut engine_state, &init_cwd);
perf("gather env vars", start_time, file!(), line!(), column!());
let mut stack = nu_protocol::engine::Stack::new(); let mut stack = nu_protocol::engine::Stack::new();
if let Some(commands) = &parsed_nu_cli_args.commands { if let Some(commands) = &parsed_nu_cli_args.commands {
start_time = std::time::Instant::now();
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
read_plugin_file( read_plugin_file(
&mut engine_state, &mut engine_state,
@ -217,7 +231,9 @@ fn main() -> Result<()> {
parsed_nu_cli_args.plugin_file, parsed_nu_cli_args.plugin_file,
NUSHELL_FOLDER, NUSHELL_FOLDER,
); );
perf("read plugins", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
// only want to load config and env if relative argument is provided. // only want to load config and env if relative argument is provided.
if parsed_nu_cli_args.env_file.is_some() { if parsed_nu_cli_args.env_file.is_some() {
config_files::read_config_file( config_files::read_config_file(
@ -229,7 +245,9 @@ fn main() -> Result<()> {
} else { } else {
config_files::read_default_env_file(&mut engine_state, &mut stack) config_files::read_default_env_file(&mut engine_state, &mut stack)
} }
perf("read env.nu", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
if parsed_nu_cli_args.config_file.is_some() { if parsed_nu_cli_args.config_file.is_some() {
config_files::read_config_file( config_files::read_config_file(
&mut engine_state, &mut engine_state,
@ -238,7 +256,9 @@ fn main() -> Result<()> {
false, false,
); );
} }
perf("read config.nu", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let ret_val = evaluate_commands( let ret_val = evaluate_commands(
commands, commands,
&mut engine_state, &mut engine_state,
@ -246,13 +266,16 @@ fn main() -> Result<()> {
input, input,
parsed_nu_cli_args.table_mode, parsed_nu_cli_args.table_mode,
); );
info!("-c command execution {}:{}:{}", file!(), line!(), column!()); perf("evaluate_commands", start_time, file!(), line!(), column!());
match ret_val { match ret_val {
Ok(Some(exit_code)) => std::process::exit(exit_code as i32), Ok(Some(exit_code)) => std::process::exit(exit_code as i32),
Ok(None) => Ok(()), Ok(None) => Ok(()),
Err(e) => Err(e), Err(e) => Err(e),
} }
} else if !script_name.is_empty() && parsed_nu_cli_args.interactive_shell.is_none() { } else if !script_name.is_empty() && parsed_nu_cli_args.interactive_shell.is_none() {
start_time = std::time::Instant::now();
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
read_plugin_file( read_plugin_file(
&mut engine_state, &mut engine_state,
@ -260,7 +283,9 @@ fn main() -> Result<()> {
parsed_nu_cli_args.plugin_file, parsed_nu_cli_args.plugin_file,
NUSHELL_FOLDER, NUSHELL_FOLDER,
); );
perf("read plugins", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
// only want to load config and env if relative argument is provided. // only want to load config and env if relative argument is provided.
if parsed_nu_cli_args.env_file.is_some() { if parsed_nu_cli_args.env_file.is_some() {
config_files::read_config_file( config_files::read_config_file(
@ -272,7 +297,9 @@ fn main() -> Result<()> {
} else { } else {
config_files::read_default_env_file(&mut engine_state, &mut stack) config_files::read_default_env_file(&mut engine_state, &mut stack)
} }
perf("read env.nu", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
if parsed_nu_cli_args.config_file.is_some() { if parsed_nu_cli_args.config_file.is_some() {
config_files::read_config_file( config_files::read_config_file(
&mut engine_state, &mut engine_state,
@ -281,7 +308,9 @@ fn main() -> Result<()> {
false, false,
); );
} }
perf("read config.nu", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let ret_val = evaluate_file( let ret_val = evaluate_file(
script_name, script_name,
&args_to_script, &args_to_script,
@ -289,7 +318,9 @@ fn main() -> Result<()> {
&mut stack, &mut stack,
input, input,
); );
perf("evaluate_file", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let last_exit_code = stack.get_env_var(&engine_state, "LAST_EXIT_CODE"); let last_exit_code = stack.get_env_var(&engine_state, "LAST_EXIT_CODE");
if let Some(last_exit_code) = last_exit_code { if let Some(last_exit_code) = last_exit_code {
let value = last_exit_code.as_integer(); let value = last_exit_code.as_integer();
@ -299,10 +330,12 @@ fn main() -> Result<()> {
} }
} }
} }
info!("eval_file execution {}:{}:{}", file!(), line!(), column!()); perf("get exit code", start_time, file!(), line!(), column!());
ret_val ret_val
} else { } else {
start_time = std::time::Instant::now();
setup_config( setup_config(
&mut engine_state, &mut engine_state,
&mut stack, &mut stack,
@ -312,15 +345,17 @@ fn main() -> Result<()> {
parsed_nu_cli_args.env_file, parsed_nu_cli_args.env_file,
parsed_nu_cli_args.login_shell.is_some(), parsed_nu_cli_args.login_shell.is_some(),
); );
perf("setup_config", start_time, file!(), line!(), column!());
start_time = std::time::Instant::now();
let ret_val = evaluate_repl( let ret_val = evaluate_repl(
&mut engine_state, &mut engine_state,
&mut stack, &mut stack,
config_files::NUSHELL_FOLDER, config_files::NUSHELL_FOLDER,
parsed_nu_cli_args.execute, parsed_nu_cli_args.execute,
start_time, entire_start_time,
); );
info!("repl eval {}:{}:{}", file!(), line!(), column!()); perf("evaluate_repl", start_time, file!(), line!(), column!());
ret_val ret_val
} }