開発

[rust] async-stdで書き直してみました

前回のコードを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で書き直してみたいと思います。

MeanStd.Dev.MinMedianMax
real6.4620.524.7686.5078.897
user5.8630.2555.3215.8196.428
sys12.2591.1228.60812.4217.374
vCPU=4
MeanStd.Dev.MinMedianMax
real24.9561.25222.96524.8729.234
user62.2592.73157.15362.13969.562
sys27.8271.93925.09227.48236.805
vCPU=4, Thread=5000
MeanStd.Dev.MinMedianMax
real11.0512.2697.71611.18421.939
user11.2070.35810.40711.24111.826
sys30.6687.33120.1231.18364.927
vCPU=6
MeanStd.Dev.MinMedianMax
real27.7331.71724.67327.90232.478
user111.996.286100.2112.537126.222
sys30.6962.68326.19530.45741.556
vCPU=6,Thread=5000
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)
}
Tags: rust
管理人

Recent Posts

CanvaがSerif (Affinity) を買収

私は使ったことがないのですが名前はよく聞…

4週間 ago

Serifのスプリングセール – アドオンが50%オフ

Affinity Photoなどレタッチ…

2か月 ago

音声がロボットのようになるときの対処

リモート会議などでたまに相手の音声がおか…

3か月 ago

Serifのブラックフライデー – 全品40%オフ V1ユーザは更にお得!

恒例のブラックフライデーセールが始まりま…

5か月 ago

[rust] rayonで書き直してみました

前回のコードを元にrayonを使った処理…

6か月 ago

[rust] mutexを使わないバージョン tokio版ディレクトリ再帰的取得

前回の続き。mutexを使わないバージョ…

6か月 ago