From b0e780f6ecba7838ea576a68d537a45850d5b5c5 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 14 Jun 2021 10:43:41 +0200 Subject: [PATCH 1/3] double the allocator limit --- primitives/allocator/src/freeing_bump.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/primitives/allocator/src/freeing_bump.rs b/primitives/allocator/src/freeing_bump.rs index 14746c8784f8d..f418a1a747081 100644 --- a/primitives/allocator/src/freeing_bump.rs +++ b/primitives/allocator/src/freeing_bump.rs @@ -86,7 +86,7 @@ macro_rules! trace { // 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 +const MAX_POSSIBLE_ALLOCATION: u32 = 33554432; // 2^25 bytes, 32 MiB 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 +100,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)] From 942ae4deb20497b0afc8a70127b833629a421263 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Mon, 14 Jun 2021 10:08:13 +0000 Subject: [PATCH 2/3] 32 MiB should be enough for everybody. --- primitives/allocator/src/freeing_bump.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/primitives/allocator/src/freeing_bump.rs b/primitives/allocator/src/freeing_bump.rs index f418a1a747081..3a4dab653c0ea 100644 --- a/primitives/allocator/src/freeing_bump.rs +++ b/primitives/allocator/src/freeing_bump.rs @@ -36,7 +36,7 @@ //! //! 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. @@ -78,14 +78,14 @@ 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; +// 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 const MIN_POSSIBLE_ALLOCATION: u32 = 8; // 2^3 bytes, 8 bytes @@ -330,7 +330,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. @@ -814,7 +814,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(); From 91e2d6059da1bb3bc3b8021e57e90128a9d1d85e Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Mon, 14 Jun 2021 11:32:28 +0000 Subject: [PATCH 3/3] Update doc --- primitives/allocator/src/freeing_bump.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/primitives/allocator/src/freeing_bump.rs b/primitives/allocator/src/freeing_bump.rs index 3a4dab653c0ea..64ba136f9a354 100644 --- a/primitives/allocator/src/freeing_bump.rs +++ b/primitives/allocator/src/freeing_bump.rs @@ -44,6 +44,24 @@ //! //! 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. use crate::Error; use sp_std::{mem, convert::{TryFrom, TryInto}, ops::{Range, Index, IndexMut}};