Skip to content

Commit

Permalink
perf: various performance tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed May 13, 2024
1 parent 218e964 commit 075fee1
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 57 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ include = [
"/build.rs",
"/zipsign.pub",
]
rust-version = "1.74.0"
rust-version = "1.76.0"
build = "build.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down Expand Up @@ -62,6 +62,7 @@ eyre = "0.6.12"
filetime = "0.2.23"
flate2 = "1.0.30"
fslock = "0.2.1"
git2 = "0.18.3"
globset = "0.4.14"
globwalk = "0.9.1"
home = "0.5.9"
Expand Down Expand Up @@ -127,7 +128,7 @@ zip = { version = "1.1.2", default-features = false, features = ["deflate"] }
exec = "0.3.1"

[build-dependencies]
built = { version = "0.7.2", features = ["chrono"] }
built = { version = "0.7.2", features = ["chrono", "git2"] }

[dev-dependencies]
assert_cmd = "2.0.14"
Expand All @@ -139,7 +140,7 @@ test-case = "3.3.1"

[features]
default = ["native-tls"]
git2 = ["built/git2"]
git2 = []
native-tls = ["reqwest/native-tls"]
rustls = ["reqwest/rustls-tls", "self_update/rustls"]
rustls-native-roots = ["reqwest/rustls-tls-native-roots", "self_update/rustls"]
Expand Down
45 changes: 25 additions & 20 deletions src/cli/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::process::exit;
use console::{pad_str, style, Alignment};
use indenter::indented;
use itertools::Itertools;
use rayon::prelude::*;

use crate::build_time::built_info;
use crate::cli::version;
Expand Down Expand Up @@ -115,7 +116,7 @@ impl Doctor {
if !env::is_activated() && !shims_on_path() {
let cmd = style::nyellow("mise help activate");
let url = style::nunderline("https://mise.jdx.dev");
let shims = style::ncyan(dirs::SHIMS.display());
let shims = style::ncyan(display_path(*dirs::SHIMS));
self.errors.push(formatdoc!(
r#"mise is not activated, run {cmd} or
read documentation at {url} for activation instructions.
Expand Down Expand Up @@ -161,6 +162,7 @@ impl Doctor {
}

fn analyze_shims(&mut self, toolset: &Toolset) {
let start_ms = std::time::Instant::now();
let mise_bin = file::which("mise").unwrap_or(env::MISE_BIN.clone());

if let Ok((missing, extra)) = shims::get_shim_diffs(mise_bin, toolset) {
Expand All @@ -182,6 +184,7 @@ impl Doctor {
));
}
}
trace!("Shim analysis took {:?}", start_ms.elapsed());
}

fn analyze_plugins(&mut self) {
Expand Down Expand Up @@ -253,7 +256,6 @@ fn render_backends() -> String {
}

fn render_plugins() -> String {
let mut s = vec![];
let plugins = forge::list()
.into_iter()
.filter(|p| p.is_installed() && p.get_type() == ForgeType::Asdf)
Expand All @@ -264,26 +266,29 @@ fn render_plugins() -> String {
.max()
.unwrap_or(0)
.min(40);
for p in plugins {
let padded_name = pad_str(p.id(), max_plugin_name_len, Alignment::Left, None);
let extra = match p.get_plugin_type() {
PluginType::External => {
let git = Git::new(dirs::PLUGINS.join(p.id()));
match git.get_remote_url() {
Some(url) => {
let sha = git
.current_sha_short()
.unwrap_or_else(|_| "(unknown)".to_string());
format!("{url}#{sha}")
plugins
.into_par_iter()
.map(|p| {
let padded_name = pad_str(p.id(), max_plugin_name_len, Alignment::Left, None);
let extra = match p.get_plugin_type() {
PluginType::External => {
let git = Git::new(dirs::PLUGINS.join(p.id()));
match git.get_remote_url() {
Some(url) => {
let sha = git
.current_sha_short()
.unwrap_or_else(|_| "(unknown)".to_string());
format!("{url}#{sha}")
}
None => "".to_string(),
}
None => "".to_string(),
}
}
PluginType::Core => "(core)".to_string(),
};
s.push(format!("{padded_name} {}", style::ndim(extra)));
}
s.join("\n")
PluginType::Core => "(core)".to_string(),
};
format!("{padded_name} {}", style::ndim(extra))
})
.collect::<Vec<_>>()
.join("\n")
}

fn build_info() -> String {
Expand Down
2 changes: 1 addition & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ impl Config {

fn load_tasks_includes(&self, root: &Path) -> Result<Vec<Task>> {
file::recursive_ls(root)?
.into_iter()
.into_par_iter()
.filter(|p| file::is_executable(p))
.map(|path| Task::from_path(&path))
.collect()
Expand Down
15 changes: 9 additions & 6 deletions src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use color_eyre::eyre::{Context, Result};
use filetime::{set_file_times, FileTime};
use flate2::read::GzDecoder;
use itertools::Itertools;
use rayon::prelude::*;
use tar::Archive;
use walkdir::WalkDir;
use zip::ZipArchive;
Expand Down Expand Up @@ -335,14 +336,16 @@ pub fn which_non_pristine<P: AsRef<Path>>(name: P) -> Option<PathBuf> {
_which(name, &env::PATH_NON_PRISTINE)
}

fn _which<P: AsRef<Path>>(name: P, paths: &Vec<PathBuf>) -> Option<PathBuf> {
for path in paths {
let bin = path.join(name.as_ref());
fn _which<P: AsRef<Path>>(name: P, paths: &[PathBuf]) -> Option<PathBuf> {
let name = name.as_ref();
paths.par_iter().find_map_first(|path| {
let bin = path.join(name);
if is_executable(&bin) {
return Some(bin);
Some(bin)
} else {
None
}
}
None
})
}

pub fn untar(archive: &Path, dest: &Path) -> Result<()> {
Expand Down
63 changes: 54 additions & 9 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ use std::path::PathBuf;

use duct::Expression;
use eyre::{eyre, Result, WrapErr};
use once_cell::sync::OnceCell;

use crate::cmd;
use crate::file::touch_dir;

pub struct Git {
pub dir: PathBuf,
pub repo: OnceCell<git2::Repository>,
}

macro_rules! git_cmd {
Expand All @@ -33,7 +35,18 @@ macro_rules! git_cmd_read {

impl Git {
pub fn new(dir: PathBuf) -> Self {
Self { dir }
Self {
dir,
repo: OnceCell::new(),
}
}

pub fn repo(&self) -> Result<&git2::Repository> {
self.repo.get_or_try_init(|| {
git2::Repository::open(&self.dir)
.wrap_err_with(|| format!("failed to open git repository at {:?}", self.dir))
.inspect_err(|err| warn!("{err:#}"))
})
}

pub fn is_repo(&self) -> bool {
Expand Down Expand Up @@ -99,44 +112,76 @@ impl Git {
}

pub fn current_branch(&self) -> Result<String> {
let dir = &self.dir;
if let Ok(repo) = self.repo() {
let branch = repo.head()?.shorthand().unwrap().to_string();
debug!("current branch for {dir:?}: {branch}");
return Ok(branch);
}
let branch = git_cmd_read!(&self.dir, "branch", "--show-current")?;
debug!("current branch for {}: {}", self.dir.display(), &branch);
Ok(branch)
}
pub fn current_sha(&self) -> Result<String> {
let dir = &self.dir;
if let Ok(repo) = self.repo() {
let head = repo.head()?;
let head = head.peel_to_commit()?;
let sha = head.id().to_string();
debug!("current sha for {dir:?}: {sha}");
return Ok(sha);
}
let sha = git_cmd_read!(&self.dir, "rev-parse", "HEAD")?;
debug!("current sha for {}: {}", self.dir.display(), &sha);
Ok(sha)
}

pub fn current_sha_short(&self) -> Result<String> {
let dir = &self.dir;
if let Ok(repo) = self.repo() {
let head = repo.head()?;
let head = head.peel_to_commit()?;
let sha = head.as_object().short_id()?.as_str().unwrap().to_string();
debug!("current sha for {dir:?}: {sha}");
return Ok(sha);
}
let sha = git_cmd_read!(&self.dir, "rev-parse", "--short", "HEAD")?;
debug!("current sha for {}: {}", self.dir.display(), &sha);
debug!("current sha for {dir:?}: {sha}");
Ok(sha)
}

pub fn current_abbrev_ref(&self) -> Result<String> {
let dir = &self.dir;
if let Ok(repo) = self.repo() {
let head = repo.head()?;
let head = head.shorthand().unwrap().to_string();
debug!("current abbrev ref for {dir:?}: {head}");
return Ok(head);
}
let aref = git_cmd_read!(&self.dir, "rev-parse", "--abbrev-ref", "HEAD")?;
debug!("current abbrev ref for {}: {}", self.dir.display(), &aref);
Ok(aref)
}

pub fn get_remote_url(&self) -> Option<String> {
if !self.dir.exists() {
let dir = &self.dir;
if !dir.exists() {
return None;
}
if let Ok(repo) = self.repo() {
let remote = repo.find_remote("origin").ok()?;
let url = remote.url()?;
trace!("remote url for {dir:?}: {url}");
return Some(url.to_string());
}
let res = git_cmd_read!(&self.dir, "config", "--get", "remote.origin.url");
match res {
Ok(url) => {
debug!("remote url for {}: {}", self.dir.display(), &url);
debug!("remote url for {dir:?}: {url}");
Some(url)
}
Err(err) => {
warn!(
"failed to get remote url for {}: {:#}",
self.dir.display(),
err
);
warn!("failed to get remote url for {dir:?}: {err:#}");
None
}
}
Expand Down
49 changes: 32 additions & 17 deletions src/shims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,22 @@ pub fn get_shim_diffs(
mise_bin: impl AsRef<Path>,
toolset: &Toolset,
) -> Result<(BTreeSet<String>, BTreeSet<String>)> {
let actual_shims = get_actual_shims(&mise_bin)?;
let desired_shims = get_desired_shims(toolset)?;

Ok((
let start_ms = std::time::Instant::now();
let mise_bin = mise_bin.as_ref();
let (actual_shims, desired_shims) =
rayon::join(|| get_actual_shims(mise_bin), || get_desired_shims(toolset));
let (actual_shims, desired_shims) = (actual_shims?, desired_shims?);
let out: (BTreeSet<String>, BTreeSet<String>) = (
desired_shims.difference(&actual_shims).cloned().collect(),
actual_shims.difference(&desired_shims).cloned().collect(),
))
);
trace!(
"get_shim_diffs({:?}): sizes: ({},{})",
start_ms.elapsed(),
out.0.len(),
out.1.len()
);
Ok(out)
}

fn get_actual_shims(mise_bin: impl AsRef<Path>) -> Result<HashSet<String>> {
Expand All @@ -161,18 +170,24 @@ fn get_actual_shims(mise_bin: impl AsRef<Path>) -> Result<HashSet<String>> {
}

fn list_executables_in_dir(dir: &Path) -> Result<HashSet<String>> {
let mut out = HashSet::new();
for bin in dir.read_dir()? {
let bin = bin?;
// skip non-files and non-symlinks or non-executable files
if (!bin.file_type()?.is_file() && !bin.file_type()?.is_symlink())
|| !file::is_executable(&bin.path())
{
continue;
}
out.insert(bin.file_name().into_string().unwrap());
}
Ok(out)
Ok(dir
.read_dir()?
.par_bridge()
.map(|bin| {
let bin = bin?;
// files and symlinks which are executable
if file::is_executable(&bin.path())
&& (bin.file_type()?.is_file() || bin.file_type()?.is_symlink())
{
Ok(Some(bin.file_name().into_string().unwrap()))
} else {
Ok(None)
}
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect())
}

fn get_desired_shims(toolset: &Toolset) -> Result<HashSet<String>> {
Expand Down
Loading

0 comments on commit 075fee1

Please sign in to comment.