forest/utils/misc/
adaptive_value_provider.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use parking_lot::RwLock;
5
6pub trait AdaptiveValueProvider<T: num::PrimInt> {
7    fn get(&self) -> T;
8
9    fn adapt_on_success(&self, record: T) -> bool;
10
11    fn adapt_on_failure(&self);
12}
13
14pub struct ExponentialAdaptiveValueProvider<T: num::PrimInt> {
15    value: RwLock<T>,
16    min: T,
17    max: T,
18    increase_on_success: bool,
19}
20
21impl<T: num::PrimInt> ExponentialAdaptiveValueProvider<T> {
22    pub fn new(value: T, min: T, max: T, increase_on_success: bool) -> Self {
23        Self {
24            value: RwLock::new(value),
25            min,
26            max,
27            increase_on_success,
28        }
29    }
30
31    fn increase(&self, record: Option<T>) -> bool {
32        let current = self.get();
33        if current == self.max {
34            return false;
35        }
36        let new_value = current.shl(1).min(self.max);
37        if let Some(record) = record
38            && record < new_value
39        {
40            return false;
41        }
42        *self.value.write() = new_value;
43        true
44    }
45
46    fn decrease(&self, record: Option<T>) -> bool {
47        let current = self.get();
48        if current == self.min {
49            return false;
50        }
51        let new_value = current.shr(1).max(self.min);
52        if let Some(record) = record
53            && record > new_value
54        {
55            return false;
56        }
57        *self.value.write() = new_value;
58        true
59    }
60}
61
62impl<T: num::PrimInt> AdaptiveValueProvider<T> for ExponentialAdaptiveValueProvider<T> {
63    fn get(&self) -> T {
64        *self.value.read()
65    }
66
67    fn adapt_on_success(&self, record: T) -> bool {
68        if self.increase_on_success {
69            self.increase(Some(record))
70        } else {
71            self.decrease(Some(record))
72        }
73    }
74
75    fn adapt_on_failure(&self) {
76        if !self.increase_on_success {
77            self.increase(None);
78        } else {
79            self.decrease(None);
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_exponential_adaptive_value_provider_behaviour() {
90        let p = ExponentialAdaptiveValueProvider::new(8, 2, 60, false);
91        assert_eq!(p.get(), 8);
92        assert!(!p.adapt_on_success(5));
93        assert_eq!(p.get(), 8);
94        assert!(p.adapt_on_success(4));
95        assert_eq!(p.get(), 4);
96        assert!(!p.adapt_on_success(3));
97        assert_eq!(p.get(), 4);
98        assert!(p.adapt_on_success(2));
99        assert_eq!(p.get(), 2);
100        assert!(!p.adapt_on_success(1));
101        assert_eq!(p.get(), 2);
102        assert!(!p.adapt_on_success(1));
103        assert_eq!(p.get(), 2);
104        p.adapt_on_failure();
105        assert_eq!(p.get(), 4);
106        p.adapt_on_failure();
107        assert_eq!(p.get(), 8);
108        p.adapt_on_failure();
109        assert_eq!(p.get(), 16);
110        p.adapt_on_failure();
111        assert_eq!(p.get(), 32);
112        p.adapt_on_failure();
113        assert_eq!(p.get(), 60);
114        p.adapt_on_failure();
115        assert_eq!(p.get(), 60);
116    }
117}