前回のコードをasync-stdで書き直してみました。
tokioバージョンの時点で書き方を意識するとasync-stdへのリプレイスは難しくない印象です。
tokio::task::JoinSetに相当するものがasync-stdには無いのでFuturesUnorderedを代わりに使用しています。
スレッド数は環境変数ASYNC_STD_THREAD_COUNT
でコントロールできます。
計測結果は下記になります。
全体的にtokioバージョンよりは遅いです。tokioはsysの実行時間が大きく増える傾向にありましたが、async-stdはuserの実行時間が増える傾向にありました。何がボトルネックになっているのか分かっていません。
こちらの新しいスケジューラーを試してみようと思ったのですが既にリポジトリから削除されていたのでダメでした。古い記事なので既に取り込まれているのかもしれません。
async-stdは周辺ツールもあまり無い様なので最適化が難しい気がします。
async/awaitとは関係無いのですがスレッドや並行処理をやってきたので次回はrayonで書き直してみたいと思います。
Mean | Std.Dev. | Min | Median | Max | |
real | 6.462 | 0.52 | 4.768 | 6.507 | 8.897 |
user | 5.863 | 0.255 | 5.321 | 5.819 | 6.428 |
sys | 12.259 | 1.122 | 8.608 | 12.42 | 17.374 |
Mean | Std.Dev. | Min | Median | Max | |
real | 24.956 | 1.252 | 22.965 | 24.87 | 29.234 |
user | 62.259 | 2.731 | 57.153 | 62.139 | 69.562 |
sys | 27.827 | 1.939 | 25.092 | 27.482 | 36.805 |
Mean | Std.Dev. | Min | Median | Max | |
real | 11.051 | 2.269 | 7.716 | 11.184 | 21.939 |
user | 11.207 | 0.358 | 10.407 | 11.241 | 11.826 |
sys | 30.668 | 7.331 | 20.12 | 31.183 | 64.927 |
Mean | Std.Dev. | Min | Median | Max | |
real | 27.733 | 1.717 | 24.673 | 27.902 | 32.478 |
user | 111.99 | 6.286 | 100.2 | 112.537 | 126.222 |
sys | 30.696 | 2.683 | 26.195 | 30.457 | 41.556 |
use anyhow::Result; // 1.0.71
use clap::Parser; // 4.3.11
use async_std::fs; // 1.12.0
use async_std::task;
use futures::{stream::FuturesUnordered, StreamExt};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// target directory
#[arg(short, long, default_value_t = String::from(".") )]
dir: String,
}
#[async_std::main]
async fn main() -> Result<()> {
let args = Args::parse();
let dir_string = args.dir.to_string();
println!("{}", dir_string);
let mut list_from_thd: Vec<String> = vec![dir_string];
// Like a do-while
while !list_from_thd.is_empty() {
let mut thds = FuturesUnordered::new();
for item in list_from_thd {
thds.push(task::spawn(get_dirs(item)));
}
let mut new_dir_list = Vec::<String>::new();
while let Some(Ok(mut dir)) = thds.next().await {
new_dir_list.append(&mut dir);
}
list_from_thd = new_dir_list;
}
Ok(())
}
async fn get_dirs(dir: String) -> Result<Vec<String>> {
let mut entries = fs::read_dir(dir).await?;
// Folder list
let mut dirs = Vec::<String>::new();
let mut thds = FuturesUnordered::new();
while let Some(entry) = entries.next().await {
let entry = entry?;
let metadata = entry.metadata().await?;
let path = entry.path();
let path_2 = path.clone();
thds.push(task::spawn(async move {
let path = path.display().to_string();
if metadata.is_dir() {
println!("{}", path);
return Some(path);
}
None
} ));
thds.push(task::spawn(async move {
let path = path_2;
if let Ok(symlink) = fs::read_link(&path).await {
if path.is_dir().await {
println!("{}@ -> {}", path.display(), symlink.display());
}
}
None
}));
}
while let Some(dir) = thds.next().await {
if let Some(dir) = dir {
dirs.push(dir);
}
}
Ok(dirs)
}