-
Notifications
You must be signed in to change notification settings - Fork 2.6k
double the allocator limit #9102
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,14 +36,32 @@ | |
//! | ||
//! For implementing freeing we maintain a linked lists for each order. The maximum supported | ||
//! allocation size is capped, therefore the number of orders and thus the linked lists is as well | ||
//! limited. Currently, the maximum size of an allocation is 16 MiB. | ||
//! limited. Currently, the maximum size of an allocation is 32 MiB. | ||
//! | ||
//! When the allocator serves an allocation request it first checks the linked list for the respective | ||
//! order. If it doesn't have any free chunks, the allocator requests memory from the bump allocator. | ||
//! In any case the order is stored in the header of the allocation. | ||
//! | ||
//! Upon deallocation we get the order of the allocation from its header and then add that | ||
//! allocation to the linked list for the respective order. | ||
//! | ||
//! # Caveats | ||
//! | ||
//! This is a fast allocator but it is also dumb. There are specifically two main shortcomings | ||
//! that the user should keep in mind: | ||
//! | ||
//! - Once the bump allocator space is exhausted, there is no way to reclaim the memory. This means | ||
//! that it's possible to end up in a situation where there are no live allocations yet a new | ||
//! allocation will fail. | ||
//! | ||
//! Let's look into an example. Given a heap of 32 MiB. The user makes a 32 MiB allocation that we | ||
//! call `X` . Now the heap is full. Then user deallocates `X`. Since all the space in the bump | ||
//! allocator was consumed by the 32 MiB allocation, allocations of all sizes except 32 MiB will | ||
//! fail. | ||
//! | ||
//! - Sizes of allocations are rounded up to the nearest order. That is, an allocation of 2,00001 MiB | ||
//! will be put into the bucket of 4 MiB. Therefore, typically more than half of the space in allocation | ||
//! will be wasted. This is more pronounced with larger allocation sizes. | ||
Comment on lines
+62
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't it be "nearly half of the space will be wasted"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. neither. we know allocations of size There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pepyakin please fix your comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. Sorry for being math retarded. Fixed in #9167. |
||
|
||
use crate::Error; | ||
use sp_std::{mem, convert::{TryFrom, TryInto}, ops::{Range, Index, IndexMut}}; | ||
|
@@ -78,15 +96,15 @@ macro_rules! trace { | |
// The minimum possible allocation size is chosen to be 8 bytes because in that case we would have | ||
// easier time to provide the guaranteed alignment of 8. | ||
// | ||
// The maximum possible allocation size was chosen rather arbitrary. 16 MiB should be enough for | ||
// The maximum possible allocation size was chosen rather arbitrary. 32 MiB should be enough for | ||
// everybody. | ||
// | ||
// N_ORDERS - represents the number of orders supported. | ||
// | ||
// This number corresponds to the number of powers between the minimum possible allocation and | ||
// maximum possible allocation, or: 2^3...2^24 (both ends inclusive, hence 22). | ||
const N_ORDERS: usize = 22; | ||
const MAX_POSSIBLE_ALLOCATION: u32 = 16777216; // 2^24 bytes, 16 MiB | ||
// maximum possible allocation, or: 2^3...2^25 (both ends inclusive, hence 23). | ||
const N_ORDERS: usize = 23; | ||
const MAX_POSSIBLE_ALLOCATION: u32 = 33554432; // 2^25 bytes, 32 MiB | ||
kianenigma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const MIN_POSSIBLE_ALLOCATION: u32 = 8; // 2^3 bytes, 8 bytes | ||
|
||
/// The exponent for the power of two sized block adjusted to the minimum size. | ||
|
@@ -100,6 +118,7 @@ const MIN_POSSIBLE_ALLOCATION: u32 = 8; // 2^3 bytes, 8 bytes | |
/// 64 | 3 | ||
/// ... | ||
/// 16777216 | 21 | ||
/// 33554432 | 22 | ||
/// | ||
/// and so on. | ||
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | ||
|
@@ -329,7 +348,7 @@ impl FreeingBumpHeapAllocator { | |
} | ||
|
||
/// Gets requested number of bytes to allocate and returns a pointer. | ||
/// The maximum size which can be allocated at once is 16 MiB. | ||
/// The maximum size which can be allocated at once is 32 MiB. | ||
/// There is no minimum size, but whatever size is passed into | ||
/// this function is rounded to the next power of two. If the requested | ||
/// size is below 8 bytes it will be rounded up to 8 bytes. | ||
|
@@ -813,7 +832,7 @@ mod tests { | |
#[test] | ||
fn should_get_max_item_size_from_index() { | ||
// given | ||
let raw_order = 21; | ||
let raw_order = 22; | ||
|
||
// when | ||
let item_size = Order::from_raw(raw_order).unwrap().size(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually that is meant to be like more formal of "We have a heap of 32 MiB". I don't mind the change though