mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Enhance PrefetchCache with directory init-file resolution
- Added a new field `dir_init` to `PrefetchCache` to store pre-resolved init-file information for directories, allowing for efficient retrieval during prefetch operations. - Introduced the `prefetch_dir_init` method in the `Vfs` struct to look up init-file info from the prefetch cache. - Updated the `prefetch_project_files` function to populate the `dir_init` map based on the defined `INIT_FILE_PRIORITY`. - Adjusted related tests to ensure proper initialization of the `dir_init` field in various scenarios. These changes improve the performance of directory handling in the VFS by reducing the need for repeated metadata probes.
This commit is contained in:
parent
f1992a61b1
commit
6168597acb
5 changed files with 159 additions and 66 deletions
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
apply_patch_set, compute_patch_set, AppliedPatchSet, InstanceContext, InstanceSnapshot,
|
||||
PatchSet, RojoTree,
|
||||
},
|
||||
snapshot_middleware::{is_script_relevant_path, snapshot_from_vfs},
|
||||
snapshot_middleware::{is_script_relevant_path, snapshot_from_vfs, INIT_FILE_PRIORITY},
|
||||
};
|
||||
|
||||
/// Set to `true` to validate on plugin connect (useful for testing, do not enable on production).
|
||||
|
|
@ -155,31 +155,35 @@ fn prefetch_project_files(project: &Project, sync_scripts_only: bool) -> io::Res
|
|||
files: HashMap::new(),
|
||||
is_file: HashMap::new(),
|
||||
children: HashMap::new(),
|
||||
dir_init: HashMap::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let walk_start = Instant::now();
|
||||
|
||||
let mut entries: Vec<walkdir::DirEntry> = Vec::new();
|
||||
for root in &roots {
|
||||
if !root.exists() {
|
||||
continue;
|
||||
}
|
||||
for e in WalkDir::new(root).follow_links(true).into_iter().flatten() {
|
||||
|
||||
// Walk the entire project folder so that every file under it
|
||||
// (nested project files, etc.) is covered by the is_file map.
|
||||
// P1 returns NotFound for cache misses when prefetch is active.
|
||||
if folder.exists() {
|
||||
for e in WalkDir::new(folder)
|
||||
.follow_links(true)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
entries.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Also include the project file itself (read by project middleware).
|
||||
if let Ok(meta) = std::fs::metadata(&project.file_location) {
|
||||
if meta.is_file() {
|
||||
entries.push(
|
||||
WalkDir::new(&project.file_location)
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
);
|
||||
// Also walk $path roots that may be OUTSIDE the project folder
|
||||
// (e.g. $path: "../module"). These wouldn't be covered above.
|
||||
for root in &roots {
|
||||
if !root.exists() || root.starts_with(folder) {
|
||||
continue;
|
||||
}
|
||||
for e in WalkDir::new(root).follow_links(true).into_iter().flatten() {
|
||||
entries.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,8 +219,9 @@ fn prefetch_project_files(project: &Project, sync_scripts_only: bool) -> io::Res
|
|||
read_elapsed,
|
||||
);
|
||||
|
||||
let mut is_file_map: HashMap<std::path::PathBuf, bool> = HashMap::new();
|
||||
let mut children_map: HashMap<std::path::PathBuf, Vec<std::path::PathBuf>> = HashMap::new();
|
||||
let mut is_file_map: HashMap<std::path::PathBuf, bool> = HashMap::with_capacity(entries.len());
|
||||
let mut children_map: HashMap<std::path::PathBuf, Vec<std::path::PathBuf>> =
|
||||
HashMap::with_capacity(dir_count);
|
||||
|
||||
for entry in &entries {
|
||||
let path = entry.path().to_path_buf();
|
||||
|
|
@ -242,10 +247,28 @@ fn prefetch_project_files(project: &Project, sync_scripts_only: bool) -> io::Res
|
|||
children_map.len(),
|
||||
);
|
||||
|
||||
let mut dir_init_map: HashMap<std::path::PathBuf, Option<(String, std::path::PathBuf)>> =
|
||||
HashMap::with_capacity(children_map.len());
|
||||
for (dir_path, child_paths) in &children_map {
|
||||
let child_names: std::collections::HashSet<&str> = child_paths
|
||||
.iter()
|
||||
.filter_map(|p| p.file_name().and_then(|n| n.to_str()))
|
||||
.collect();
|
||||
let mut found = None;
|
||||
for &(_, init_name) in INIT_FILE_PRIORITY {
|
||||
if child_names.contains(init_name) {
|
||||
found = Some((init_name.to_string(), dir_path.join(init_name)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
dir_init_map.insert(dir_path.clone(), found);
|
||||
}
|
||||
|
||||
Ok(PrefetchCache {
|
||||
files: file_data.into_iter().collect::<HashMap<_, _>>(),
|
||||
is_file: is_file_map,
|
||||
children: children_map,
|
||||
dir_init: dir_init_map,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -273,12 +296,15 @@ impl ServeSession {
|
|||
let prefetch_start = Instant::now();
|
||||
match prefetch_project_files(&root_project, sync_scripts_only) {
|
||||
Ok(cache) => {
|
||||
let count = cache.files.len();
|
||||
log::debug!(
|
||||
"Prefetch total: {} files in {:.1?}",
|
||||
cache.files.len(),
|
||||
count,
|
||||
prefetch_start.elapsed()
|
||||
);
|
||||
vfs.set_prefetch_cache(cache);
|
||||
if !cache.is_file.is_empty() {
|
||||
vfs.set_prefetch_cache(cache);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("Prefetch failed, falling back to sequential reads: {err}");
|
||||
|
|
@ -291,37 +317,45 @@ impl ServeSession {
|
|||
let mut instance_context = InstanceContext::new();
|
||||
instance_context.sync_scripts_only = sync_scripts_only;
|
||||
|
||||
let watch_start = Instant::now();
|
||||
let mut path_roots = Vec::new();
|
||||
collect_path_roots(
|
||||
&root_project.tree,
|
||||
root_project.folder_location(),
|
||||
&mut path_roots,
|
||||
);
|
||||
let _ = vfs.watch(root_project.folder_location());
|
||||
for root in &path_roots {
|
||||
let _ = vfs.watch(root);
|
||||
}
|
||||
log::debug!(
|
||||
"Set up {} recursive watches in {:.1?}",
|
||||
path_roots.len() + 1,
|
||||
watch_start.elapsed(),
|
||||
);
|
||||
|
||||
let snap_start = Instant::now();
|
||||
log::trace!("Generating snapshot of instances from VFS");
|
||||
vfs.set_watch_enabled(false);
|
||||
let snapshot = snapshot_from_vfs(&instance_context, vfs, start_path)?;
|
||||
vfs.set_watch_enabled(true);
|
||||
log::debug!("Snapshot built in {:.1?}", snap_start.elapsed());
|
||||
|
||||
vfs.clear_prefetch_cache();
|
||||
|
||||
let patch_start = Instant::now();
|
||||
log::trace!("Computing initial patch set");
|
||||
let patch_set = compute_patch_set(snapshot, &tree, root_id);
|
||||
let project_folder = root_project.folder_location().to_path_buf();
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
let watch_start = Instant::now();
|
||||
let _ = vfs.watch(&project_folder);
|
||||
for root in &path_roots {
|
||||
let _ = vfs.watch(root);
|
||||
}
|
||||
log::debug!(
|
||||
"Set up {} recursive watches in {:.1?}",
|
||||
path_roots.len() + 1,
|
||||
watch_start.elapsed(),
|
||||
);
|
||||
});
|
||||
|
||||
log::trace!("Applying initial patch set");
|
||||
apply_patch_set(&mut tree, patch_set);
|
||||
log::debug!("Patch computed + applied in {:.1?}", patch_start.elapsed());
|
||||
log::trace!("Computing initial patch set");
|
||||
let patch_set = compute_patch_set(snapshot, &tree, root_id);
|
||||
|
||||
log::trace!("Applying initial patch set");
|
||||
apply_patch_set(&mut tree, patch_set);
|
||||
});
|
||||
log::debug!("Watch + patch in {:.1?}", patch_start.elapsed());
|
||||
|
||||
Ok((root_project, tree))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue