From 45987706c52943b691e87c012932a03712d03e7e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Jul 2023 14:38:55 -0700 Subject: [PATCH] Optimize two slow code paths (#2728) Linear: https://linear.app/zed-industries/issue/Z-2578/zed-launches-very-slow-for-user I was searching for the cause of a slow startup time reported in the above issue, and I don't think I found it, but I did find two very noticeable slow code paths while profiling, and fixed them. ### Notes 1. When starting the JSON language server, we provide it with a JSON schema for our settings. For the `theme` setting, the JSON schema needs to read all of the themes in the registry, to generate a list of valid theme names. Previously, as part of this, we were deserializing each theme from JSON, which took a lot of CPU. Now, we don't do that. 2. When an FS event occurs within a git repository, we reload the git status for all entries in that git repository. Previously, we did that via a separate `libgit2` call per FS entry (including ignored entries, so many thousands in the case of the `zed` repo). Now we do one `libgit2` call, asking for all of the statuses. Git carries an index of all of the files with statuses, so this is fast. Release Notes: - Improved the the performance of starting up a JSON language server. - Improved the performance of handling changes to git repositories, such as changing branches or committing. --- crates/fs/src/repository.rs | 1 + crates/project/src/worktree.rs | 9 +++++---- crates/theme/src/theme_registry.rs | 22 +++++++++++++++++----- crates/theme/src/theme_settings.rs | 4 ++-- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/crates/fs/src/repository.rs b/crates/fs/src/repository.rs index ed9aa85a89bf991bf1d03d5f8952dd8fb70a9780..3826dae2aa73eeb3f5c263d0291e932939a19fff 100644 --- a/crates/fs/src/repository.rs +++ b/crates/fs/src/repository.rs @@ -33,6 +33,7 @@ pub trait GitRepository: Send { fn statuses(&self) -> Option>; fn status(&self, path: &RepoPath) -> Result>; + fn branches(&self) -> Result> { Ok(vec![]) } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 2c3c9d53047e48b55f556038504bf3546a4ad284..31b1133d658f0cfc1d310d04cb6592b5eb61b32f 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -2022,6 +2022,9 @@ impl LocalSnapshot { ) -> Vec> { let mut changes = vec![]; let mut edits = vec![]; + + let statuses = repo_ptr.statuses(); + for mut entry in self .descendent_entries(false, false, &work_directory.0) .cloned() @@ -2029,10 +2032,8 @@ impl LocalSnapshot { let Ok(repo_path) = entry.path.strip_prefix(&work_directory.0) else { continue; }; - let git_file_status = repo_ptr - .status(&RepoPath(repo_path.into())) - .log_err() - .flatten(); + let repo_path = RepoPath(repo_path.to_path_buf()); + let git_file_status = statuses.as_ref().and_then(|s| s.get(&repo_path).copied()); if entry.git_status != git_file_status { entry.git_status = git_file_status; changes.push(entry.path.clone()); diff --git a/crates/theme/src/theme_registry.rs b/crates/theme/src/theme_registry.rs index 8565bc3b567ab04f0d68ae6637da5c498d595084..617667bc9feb05b8f5ed4e02da7cb14d3781546e 100644 --- a/crates/theme/src/theme_registry.rs +++ b/crates/theme/src/theme_registry.rs @@ -5,6 +5,7 @@ use parking_lot::Mutex; use serde::Deserialize; use serde_json::Value; use std::{ + borrow::Cow, collections::HashMap, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, @@ -43,7 +44,7 @@ impl ThemeRegistry { this } - pub fn list(&self, staff: bool) -> impl Iterator + '_ { + pub fn list_names(&self, staff: bool) -> impl Iterator> + '_ { let mut dirs = self.assets.list("themes/"); if !staff { @@ -53,10 +54,21 @@ impl ThemeRegistry { .collect() } - dirs.into_iter().filter_map(|path| { - let filename = path.strip_prefix("themes/")?; - let theme_name = filename.strip_suffix(".json")?; - self.get(theme_name).ok().map(|theme| theme.meta.clone()) + fn get_name(path: &str) -> Option<&str> { + path.strip_prefix("themes/")?.strip_suffix(".json") + } + + dirs.into_iter().filter_map(|path| match path { + Cow::Borrowed(path) => Some(Cow::Borrowed(get_name(path)?)), + Cow::Owned(path) => Some(Cow::Owned(get_name(&path)?.to_string())), + }) + } + + pub fn list(&self, staff: bool) -> impl Iterator + '_ { + self.list_names(staff).filter_map(|theme_name| { + self.get(theme_name.as_ref()) + .ok() + .map(|theme| theme.meta.clone()) }) } diff --git a/crates/theme/src/theme_settings.rs b/crates/theme/src/theme_settings.rs index b9e6f7a133a42d05d99aec6ab76af395554c160b..1b5351197823312e9a90c8ab07f5a56496048ffc 100644 --- a/crates/theme/src/theme_settings.rs +++ b/crates/theme/src/theme_settings.rs @@ -149,8 +149,8 @@ impl settings::Setting for ThemeSettings { let mut root_schema = generator.root_schema_for::(); let theme_names = cx .global::>() - .list(params.staff_mode) - .map(|theme| Value::String(theme.name.clone())) + .list_names(params.staff_mode) + .map(|theme_name| Value::String(theme_name.to_string())) .collect(); let theme_name_schema = SchemaObject {