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}