减少Rust中indicatif ProgressBar的开销

yacmzcpb  于 2023-10-20  发布在  其他
关注(0)|答案(2)|浏览(100)

假设我有以下Rust代码:

use indicatif::ProgressBar;

fn main() {
    let limit = 100_000_000;

    let pb = ProgressBar::new(limit);
    for i in 0..limit {
        pb.inc(1);
    }

    pb.finish();
}

执行此代码需要数十秒。
类似地,当我想显示大量循环的进度条,每个循环花费的时间很短时,似乎indicatif更新了每个循环终端上的进度条,开销变得很大。
有没有办法减少这种开销?例如,每50毫秒更新一次进度条?
(我知道我可以通过使用std::time::{Duration, Instant}来实现,但我想知道更简单和可读的解决方案,如果它存在的话。

7gcisfzg

7gcisfzg1#

indicatif实现了自己的过滤机制。但是你的问题不是写,即使我们删除了所有的写,你的代码仍然很慢。问题在于过滤器本身。
为了过滤(每毫秒10次),它在每次调用inc()时构造一个Instant。建造它是缓慢的。
有比Instant::now()更快的方法来获取当前时间,但它们也更复杂。也许,最好的方法是每N次迭代才调用inc()

ngynwnxp

ngynwnxp2#

每隔50 ms更新一次进度条,而不在每次迭代中调用Instant::now,这是可能的,但要比你想象的困难得多:

use indicatif::ProgressBar;
use std::{
    sync::atomic::{AtomicU64, Ordering::SeqCst},
    thread,
    time::Duration,
};

fn main() {
    let limit = 100_000_000;
    let current = AtomicU64::new(0);
    thread::scope(|s| {
        s.spawn(|| {
            let pb = ProgressBar::new(limit);
            loop {
                let current = current.load(SeqCst);
                match current {
                    u64::MAX => break,
                    current => pb.set_position(current),
                }
                thread::sleep(Duration::from_millis(50));
            }
        });
        for i in 0..limit {
            current.store(i, SeqCst);
        }
        current.store(u64::MAX, SeqCst);
    });
}

请注意,这比直接更新更快,但不是数量级更快。至少在Linux上,Instant::now相当便宜。原子商店更便宜,但不是免费的。

相关问题