mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
perf: use rayon to parallelize directory operations
This commit is contained in:
parent
5bd71bf225
commit
4c9ecfef5f
8 changed files with 54 additions and 33 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -2107,6 +2107,16 @@ dependencies = [
|
||||||
"rgb",
|
"rgb",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon-core"
|
name = "rayon-core"
|
||||||
version = "1.12.1"
|
version = "1.12.1"
|
||||||
|
|
@ -3625,6 +3635,7 @@ dependencies = [
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
"shell-words",
|
"shell-words",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ futures = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
rayon = "1.10.0"
|
||||||
|
|
||||||
[target."cfg(unix)".dependencies]
|
[target."cfg(unix)".dependencies]
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use std::{borrow::Cow, collections::{HashMap, HashSet, VecDeque}, ffi::{OsStr, OsString}, path::{Path, PathBuf}};
|
use std::{borrow::Cow, collections::{HashMap, HashSet}, ffi::{OsStr, OsString}, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use tokio::{fs, io, select, sync::{mpsc, oneshot}, time};
|
use tokio::{fs, io, select, sync::{mpsc, oneshot}, time};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use super::Cha;
|
use super::Cha;
|
||||||
|
|
||||||
|
|
@ -155,28 +156,28 @@ pub async fn realname_unchecked<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn calculate_size(path: &Path) -> u64 {
|
pub async fn calculate_size(path: &Path) -> io::Result<u64> {
|
||||||
let mut total = 0;
|
let path = path.to_path_buf();
|
||||||
let mut stack = VecDeque::from([path.to_path_buf()]);
|
tokio::task::spawn_blocking(move || _calculate_size(&path)).await?
|
||||||
while let Some(path) = stack.pop_front() {
|
}
|
||||||
let Ok(meta) = fs::symlink_metadata(&path).await else { continue };
|
|
||||||
if !meta.is_dir() {
|
|
||||||
total += meta.len();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Ok(mut it) = fs::read_dir(path).await else { continue };
|
fn _calculate_size(path: &Path) -> io::Result<u64> {
|
||||||
while let Ok(Some(entry)) = it.next_entry().await {
|
let entries: Vec<_> = std::fs::read_dir(&path)?.collect();
|
||||||
let Ok(meta) = entry.metadata().await else { continue };
|
|
||||||
|
|
||||||
if meta.is_dir() {
|
let total = entries.par_iter().filter_map(|entry| {
|
||||||
stack.push_back(entry.path());
|
match entry {
|
||||||
} else {
|
Ok(entry) => {
|
||||||
total += meta.len();
|
match entry.metadata() {
|
||||||
}
|
Ok(meta) if meta.is_file() => Some(meta.len()),
|
||||||
|
Ok(meta) if meta.is_dir() => _calculate_size(&entry.path()).ok(),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}).sum();
|
||||||
total
|
|
||||||
|
Ok(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_with_progress(
|
pub fn copy_with_progress(
|
||||||
|
|
@ -284,18 +285,25 @@ async fn _copy_with_progress(from: PathBuf, to: PathBuf, cha: Cha) -> io::Result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_dir_clean(dir: &Path) {
|
pub async fn remove_dir_clean(dir: &Path) -> io::Result<()> {
|
||||||
let Ok(mut it) = fs::read_dir(dir).await else { return };
|
let dir = dir.to_path_buf();
|
||||||
|
tokio::task::spawn_blocking(move || _remove_dir_clean(&dir)).await?
|
||||||
|
}
|
||||||
|
|
||||||
while let Ok(Some(entry)) = it.next_entry().await {
|
fn _remove_dir_clean(dir: &Path) -> io::Result<()> {
|
||||||
if entry.file_type().await.is_ok_and(|t| t.is_dir()) {
|
let entries: Vec<_> = std::fs::read_dir(&dir)?.collect();
|
||||||
let path = entry.path();
|
|
||||||
Box::pin(remove_dir_clean(&path)).await;
|
entries.par_iter().for_each(|entry| {
|
||||||
fs::remove_dir(path).await.ok();
|
match entry {
|
||||||
|
Ok(entry) if entry.file_type().is_ok_and(|t| t.is_dir()) => {
|
||||||
|
let _ = _remove_dir_clean(&entry.path());
|
||||||
|
let _ = std::fs::remove_dir(&entry.path());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
fs::remove_dir(dir).await.ok();
|
std::fs::remove_dir(&dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a file mode to a string representation
|
// Convert a file mode to a string representation
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ fn remove(lua: &Lua) -> mlua::Result<Function> {
|
||||||
b"file" => fs::remove_file(&*url).await,
|
b"file" => fs::remove_file(&*url).await,
|
||||||
b"dir" => fs::remove_dir(&*url).await,
|
b"dir" => fs::remove_dir(&*url).await,
|
||||||
b"dir_all" => fs::remove_dir_all(&*url).await,
|
b"dir_all" => fs::remove_dir_all(&*url).await,
|
||||||
b"dir_clean" => Ok(remove_dir_clean(&url).await),
|
b"dir_clean" => Ok(remove_dir_clean(&url).await?),
|
||||||
_ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?,
|
_ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,7 @@ impl File {
|
||||||
|
|
||||||
pub async fn trash(&self, mut task: FileOpTrash) -> Result<()> {
|
pub async fn trash(&self, mut task: FileOpTrash) -> Result<()> {
|
||||||
let id = task.id;
|
let id = task.id;
|
||||||
task.length = calculate_size(&task.target).await;
|
task.length = calculate_size(&task.target).await?;
|
||||||
|
|
||||||
self.prog.send(TaskProg::New(id, task.length))?;
|
self.prog.send(TaskProg::New(id, task.length))?;
|
||||||
self.queue(FileOp::Trash(task), LOW).await?;
|
self.queue(FileOp::Trash(task), LOW).await?;
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ impl Prework {
|
||||||
self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
|
self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
|
||||||
}
|
}
|
||||||
PreworkOp::Size(task) => {
|
PreworkOp::Size(task) => {
|
||||||
let length = calculate_size(&task.target).await;
|
let length = calculate_size(&task.target).await?;
|
||||||
task.throttle.done((task.target, length), |buf| {
|
task.throttle.done((task.target, length), |buf| {
|
||||||
{
|
{
|
||||||
let mut loading = self.size_loading.write();
|
let mut loading = self.size_loading.write();
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ impl Scheduler {
|
||||||
Box::new(move |canceled: bool| {
|
Box::new(move |canceled: bool| {
|
||||||
async move {
|
async move {
|
||||||
if !canceled {
|
if !canceled {
|
||||||
remove_dir_clean(&from).await;
|
let _ = remove_dir_clean(&from).await;
|
||||||
Pump::push_move(from, to);
|
Pump::push_move(from, to);
|
||||||
}
|
}
|
||||||
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
ongoing.lock().try_remove(id, TaskStage::Hooked);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ ratatui = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
shell-words = { workspace = true }
|
shell-words = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
rayon = "1.10.0"
|
||||||
|
|
||||||
[target."cfg(unix)".dependencies]
|
[target."cfg(unix)".dependencies]
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue