1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/// How much RAM is the application using?
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MemoryUse {
    /// Bytes allocated by the application according to operating system.
    ///
    /// Resident Set Size (RSS) on Linux, Android, Mac, iOS.
    /// Working Set on Windows.
    ///
    /// `None` if unknown.
    pub resident: Option<i64>,

    /// Bytes used by the application according to our own memory allocator's accounting.
    ///
    /// This can be smaller than [`Self::resident`] because our memory allocator may not
    /// return all the memory we free to the OS.
    ///
    /// `None` if [`crate::AccountingAllocator`] is not used.
    pub counted: Option<i64>,
}

impl MemoryUse {
    /// Read the current memory of the running application.
    #[inline]
    pub fn capture() -> Self {
        Self {
            resident: bytes_resident(),
            counted: crate::accounting_allocator::global_allocs().map(|c| c.size as _),
        }
    }

    /// Bytes used by the application according to our best estimate.
    ///
    /// This is either [`Self::counted`] if it's available, otherwise fallbacks to
    /// [`Self::resident`] if that's available, otherwise `None`.
    #[inline]
    pub fn used(&self) -> Option<i64> {
        self.counted.or(self.resident)
    }
}

impl std::ops::Mul<f32> for MemoryUse {
    type Output = Self;

    fn mul(self, factor: f32) -> Self::Output {
        Self {
            resident: self.resident.map(|v| (v as f32 * factor) as i64),
            counted: self.counted.map(|v| (v as f32 * factor) as i64),
        }
    }
}

impl std::ops::Sub for MemoryUse {
    type Output = Self;

    #[inline]
    fn sub(self, rhs: Self) -> Self::Output {
        fn sub(a: Option<i64>, b: Option<i64>) -> Option<i64> {
            Some(a? - b?)
        }

        MemoryUse {
            resident: sub(self.resident, rhs.resident),
            counted: sub(self.counted, rhs.counted),
        }
    }
}

// ----------------------------------------------------------------------------

/// According to the OS. This is what matters.
///
/// Resident Set Size (RSS) on Linux, Android, Mac, iOS.
/// Working Set on Windows.
#[cfg(not(target_arch = "wasm32"))]
fn bytes_resident() -> Option<i64> {
    memory_stats::memory_stats().map(|usage| usage.physical_mem as i64)
}

#[cfg(target_arch = "wasm32")]
fn bytes_resident() -> Option<i64> {
    // blocked on https://github.com/Arc-blroth/memory-stats/issues/1
    None
}