tangram_progress_counter/lib.rs
1use std::sync::{
2 atomic::{AtomicU64, Ordering},
3 Arc,
4};
5
6/**
7A `ProgressCounter` is used to efficiently track the progress of a task occurring across multiple threads.
8
9Imagine you have the following code:
10
11```
12use rayon::prelude::*;
13
14let mut v: Vec<usize> = (0..1_000).collect();
15v.par_iter_mut().for_each(|v| { *v += 1; });
16```
17
18Now you want to track the progress of this loop. You can use `Arc<Mutex<T>>` like so:
19
20```
21use rayon::prelude::*;
22use std::sync::{Arc, Mutex};
23
24let mut v: Vec<usize> = (0..1_000).collect();
25let progress_counter = Arc::new(Mutex::new(0));
26v.par_iter_mut().for_each(|v| {
27 *v += 1;
28 *progress_counter.lock().unwrap() += 1;
29});
30```
31
32However, because the work done in each loop iteration is small, a large portion of time will be spent waiting on the mutex. A better choice in this case is to use [atomics](https://doc.rust-lang.org/stable/std/sync/atomic/index.html). `ProgressCounter` is a convenient wrapper around atomics for use in tracking progress. This example will now run much faster:
33
34```
35use rayon::prelude::*;
36use tangram_progress_counter::ProgressCounter;
37
38let mut v: Vec<usize> = (0..1_000).collect();
39let progress_counter = ProgressCounter::new(v.len() as u64);
40v.par_iter_mut().for_each(|v| {
41 *v += 1;
42 progress_counter.inc(1);
43});
44```
45*/
46#[derive(Clone, Debug)]
47pub struct ProgressCounter {
48 current: Arc<AtomicU64>,
49 total: u64,
50}
51
52impl ProgressCounter {
53 /// Create a new `ProgressCounter` that will count from 0 up to the specified `total`.
54 pub fn new(total: u64) -> ProgressCounter {
55 ProgressCounter {
56 current: Arc::new(AtomicU64::new(0)),
57 total,
58 }
59 }
60
61 /// Retrieve the total value this `ProgressCounter` counts up to.
62 pub fn total(&self) -> u64 {
63 self.total
64 }
65
66 /// Retrieve the current progress value.
67 pub fn get(&self) -> u64 {
68 self.current.load(Ordering::Relaxed)
69 }
70
71 /// Set the current progress value.
72 pub fn set(&self, value: u64) {
73 self.current.store(value, Ordering::Relaxed);
74 }
75
76 /// Increment the progress value by `amount`.
77 pub fn inc(&self, amount: u64) {
78 self.current.fetch_add(amount, Ordering::Relaxed);
79 }
80}