Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

double the allocator limit #9102

Merged
3 commits merged into from
Jun 17, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions primitives/allocator/src/freeing_bump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! Let's look into an example. Given a heap of 32 MiB. The user makes a 32 MiB allocation that we
//! Let's look into an example. Given a heap of 32 MiB, the user makes a 32 MiB allocation that we

Copy link
Contributor

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

//! 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be "nearly half of the space will be wasted"?

Copy link
Member

@gavofyork gavofyork Jun 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neither. we know allocations of size N+1..=2N to each take up to 2N, thus assuming a uniform distribution of allocation sizes, the average amount in use of a 2N space on the heap will be (3N+1)/2, so the actual average usage is (3N+1)/4N which is around 75%, or just under 25% wastage on average.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pepyakin please fix your comment.

Copy link
Contributor

Choose a reason for hiding this comment

The 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}};
Expand Down Expand Up @@ -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.
Expand All @@ -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)]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down