xalloc/bitmap.rs
1//
2// Copyright 2017 yvt, all rights reserved.
3//
4// Licensed under the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>. This file may
6// not be copied, modified,or distributed except
7// according to those terms.
8//
9//! Free space bitmap-based external memory allocator.
10//!
11//! [`BitmapAlloc`] uses a simple bitmap to track the allocation state, and
12//! relies on a naïve bit scan for allocation.
13//!
14//! ## Memory Overhead
15//!
16//! Since it uses a bitmap to track free space, it consumes a memory proportional
17//! to the size of the heap. The memory consumption is estimated to be roughly
18//! `size / 8` bytes, where `size` is the size of the heap measured by the
19//! number of allocation units.
20//!
21//! `BitmapAlloc` does not store information about individual allocated regions
22//! by itself. Therefore, `BitmapAlloc` would be preferred when the number of
23//! allocations is quite high and each allocation is very small (preferably,
24//! just one allocation unit).
25//!
26//! The following table shows the memory overhead comparison between `Tlsf` and
27//! `BitmapAlloc` with a varying number of allocations (assuming the heap is
28//! fully occupied).
29//!
30//! | `size` | # of allocations | `Tlsf` (bytes) | `BitmapAloc` (bytes) |
31//! | ------ | ---------------- | -------------- | -------------------- |
32//! | 1,024 | 0 | 1,118 | 128 |
33//! | | 1 | 1,174 | 128 |
34//! | | 256 | 15,454 | 128 |
35//! | | 1,024 | 58,462 | 128 |
36//! | 65,536 | 0 | 1,118 | 8,192 |
37//! | | 1 | 1,174 | 8,192 |
38//! | | 1,024 | 58,462 | 8,192 |
39//! | | 65,536 | 3,671,134 | 8,192 |
40//!
41//! ## Performance
42//!
43//! The allocation throughput is roughly the same as of jemalloc.
44use bitmaputils::*;
45use int::BinaryInteger;
46use std::ops::Range;
47
48/// Free space bitmap-based external memory allocator.
49///
50/// See [the module-level documentation] for more.
51///
52/// [the module-level documentation]: index.html
53#[derive(Debug, Clone)]
54pub struct BitmapAlloc {
55 bitmap: Vec<usize>,
56 size: usize,
57 next: usize,
58}
59
60/// A handle type to a region allocated in a `BitmapAlloc`.
61///
62/// `BitmapAllocRegion` returned by a `BitmapAlloc` only can be used with the
63/// same `BitmapAlloc`.
64#[derive(Debug, PartialEq, Eq, Hash)]
65pub struct BitmapAllocRegion {
66 range: Range<usize>,
67}
68
69impl BitmapAlloc {
70 /// Construct a `BitmapAlloc`.
71 pub fn new(size: usize) -> Self {
72 let width = <usize>::max_digits() as usize;
73 Self {
74 bitmap: vec![0; (size + width - 1) / width],
75 size,
76 next: 0,
77 }
78 }
79
80 /// Alias of [`alloc_next`].
81 ///
82 /// [`alloc_next`]: #method.alloc_next
83 pub fn alloc(&mut self, size: usize) -> Option<(BitmapAllocRegion, usize)> {
84 self.alloc_next(size)
85 }
86
87 /// Allocate a region of the size `size` using a Next-Fit strategy.
88 /// The time complexity is linear to the size of the heap.
89 ///
90 /// Returns a handle of the allocated region and its offset if the
91 /// allocation succeeds. Returns `None` otherwise.
92 ///
93 /// `size` must not be zero.
94 pub fn alloc_next(&mut self, size: usize) -> Option<(BitmapAllocRegion, usize)> {
95 let next = self.next;
96 self.alloc_inner(size, next)
97 .or_else(|| self.alloc_first(size))
98 }
99
100 /// Allocate a region of the size `size` using a First-Fit strategy.
101 /// The time complexity is linear to the size of the heap.
102 ///
103 /// Returns a handle of the allocated region and its offset if the
104 /// allocation succeeds. Returns `None` otherwise.
105 ///
106 /// `size` must not be zero.
107 pub fn alloc_first(&mut self, size: usize) -> Option<(BitmapAllocRegion, usize)> {
108 self.alloc_inner(size, 0)
109 }
110
111 fn alloc_inner(&mut self, size: usize, start: usize) -> Option<(BitmapAllocRegion, usize)> {
112 assert!(size != 0);
113
114 if start + size > self.size {
115 return None;
116 }
117
118 find_zeros(&self.bitmap, start, size).and_then(|offs| {
119 let range = offs..offs + size;
120 if range.end <= self.size {
121 set_bits_ranged(&mut self.bitmap, range.clone());
122 if range.end == self.size {
123 self.next = 0;
124 } else {
125 self.next = range.end;
126 }
127 Some((BitmapAllocRegion { range }, offs))
128 } else {
129 None
130 }
131 })
132 }
133
134 /// Deallocate the specified region.
135 ///
136 /// `r` must originate from the same instance of `BitmapAlloc`. Otherwise,
137 /// `BitmapAlloc` enters an inconsistent state and possibly panics, but does
138 /// not cause an undefined behavior.
139 pub fn dealloc_relaxed(&mut self, r: BitmapAllocRegion) {
140 // Optimize for stack-like usage
141 if self.next == r.range.end {
142 self.next = r.range.start;
143 }
144
145 clear_bits_ranged(&mut self.bitmap, r.range);
146 }
147}