Skip to content

Commit

Permalink
[eclipse-iceoryx#497] Provide max chunk layout hint
Browse files Browse the repository at this point in the history
  • Loading branch information
elfenpiff committed Nov 19, 2024
1 parent d29a8bd commit 2ad50ee
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 48 deletions.
8 changes: 5 additions & 3 deletions iceoryx2-bb/container/src/slotmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,11 @@ pub mod details {
}

pub(crate) unsafe fn next_free_key_impl(&self) -> Option<SlotMapKey> {
self.idx_to_data_next_free_index
.peek_impl()
.map(|v| SlotMapKey::new(*v))
if self.idx_to_data_free_list_head == INVALID {
return None;
}

Some(SlotMapKey::new(self.idx_to_data_free_list_head))
}

pub(crate) fn len_impl(&self) -> usize {
Expand Down
33 changes: 24 additions & 9 deletions iceoryx2-cal/src/resizable_shared_memory/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct BuilderConfig<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> {
has_ownership: bool,
shm_builder_timeout: Duration,
max_number_of_chunks_hint: usize,
max_chunk_layout_hint: Layout,
}

#[derive(Debug)]
Expand Down Expand Up @@ -114,6 +115,7 @@ impl<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>>
allocator_config_hint: Allocator::Configuration::default(),
shm: Shm::Configuration::default(),
max_number_of_chunks_hint: 1,
max_chunk_layout_hint: Layout::new::<u8>(),
},
}
}
Expand All @@ -140,8 +142,8 @@ where
self
}

fn allocator_config_hint(mut self, value: Allocator::Configuration) -> Self {
self.config.allocator_config_hint = value;
fn max_chunk_layout_hint(mut self, value: Layout) -> Self {
self.config.max_chunk_layout_hint = value;
self
}

Expand All @@ -160,14 +162,15 @@ where
self
}

fn create(self) -> Result<DynamicMemory<Allocator, Shm>, SharedMemoryCreateError> {
let initial_size = Allocator::payload_size_hint(
&self.config.allocator_config_hint,
fn create(mut self) -> Result<DynamicMemory<Allocator, Shm>, SharedMemoryCreateError> {
let hint = Allocator::initial_setup_hint(
self.config.max_chunk_layout_hint,
self.config.max_number_of_chunks_hint,
);
self.config.allocator_config_hint = hint.config;

let origin = format!("{:?}", self);
let shm = fail!(from origin, when DynamicMemory::create_segment(&self.config, 0, initial_size),
let shm = fail!(from origin, when DynamicMemory::create_segment(&self.config, 0, hint.payload_size),
"Unable to create ResizableSharedMemory since the underlying shared memory could not be created.");

let mut shared_memory_map = SlotMap::new(MAX_DATASEGMENTS);
Expand Down Expand Up @@ -373,6 +376,17 @@ impl<Allocator: ShmAllocator, Shm: SharedMemory<Allocator>> DynamicMemory<Alloca
adjusted_segment_setup.payload_size,
)?;

match state.shared_memory_map.get(state.current_idx) {
Some(ref segment) => {
if segment.chunk_count.load(Ordering::Relaxed) == 0 {
state.shared_memory_map.remove(state.current_idx);
}
}
None => {
fatal_panic!(from self, "This should never happen! Current segment id is unavailable.")
}
}

state
.shared_memory_map
.insert_at(segment_id, ShmEntry::new(shm));
Expand Down Expand Up @@ -405,13 +419,14 @@ where
ptr.offset.set_segment_id(state.current_idx.value() as u8);
return Ok(ptr);
}
Err(ShmAllocationError::AllocationError(AllocationError::OutOfMemory)) => {
Err(ShmAllocationError::AllocationError(AllocationError::OutOfMemory))
| Err(ShmAllocationError::ExceedsMaxSupportedAlignment) => {
self.create_resized_segment(&entry.shm, layout)?;
}
Err(e) => return Err(e.into()),
},
None => fatal_panic!(from self,
"This should never happen! Current shared memory segment is not available!"),
"This should never happen! Unable to allocate memory since the current shared memory segment is not available!"),
}
}
}
Expand All @@ -429,7 +444,7 @@ where
}
}
None => fatal_panic!(from self,
"This should never happen! Current shared memory segment is not available!"),
"This should never happen! Unable to deallocate {:?} since the corresponding shared memory segment is not available!", offset),
}
}

Expand Down
3 changes: 2 additions & 1 deletion iceoryx2-cal/src/resizable_shared_memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

pub mod dynamic;

use std::alloc::Layout;
use std::fmt::Debug;
use std::time::Duration;

Expand Down Expand Up @@ -41,7 +42,7 @@ pub trait ResizableSharedMemoryBuilder<
/// Defines if a newly created [`SharedMemory`] owns the underlying resources
fn has_ownership(self, value: bool) -> Self;

fn allocator_config_hint(self, value: Allocator::Configuration) -> Self;
fn max_chunk_layout_hint(self, value: Layout) -> Self;

fn max_number_of_chunks_hint(self, value: usize) -> Self;

Expand Down
14 changes: 10 additions & 4 deletions iceoryx2-cal/src/shm_allocator/bump_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl ShmAllocator for BumpAllocator {
&self,
layout: Layout,
strategy: AllocationStrategy,
) -> ResizeHint<Self::Configuration> {
) -> SharedMemorySetupHint<Self::Configuration> {
let current_payload_size = self.allocator.total_space();

let payload_size = match strategy {
Expand All @@ -42,14 +42,20 @@ impl ShmAllocator for BumpAllocator {
}
};

ResizeHint {
SharedMemorySetupHint {
payload_size,
config: Self::Configuration::default(),
}
}

fn payload_size_hint(_config: &Self::Configuration, max_number_of_chunks: usize) -> usize {
max_number_of_chunks
fn initial_setup_hint(
max_chunk_layout: Layout,
max_number_of_chunks: usize,
) -> SharedMemorySetupHint<Self::Configuration> {
SharedMemorySetupHint {
config: Self::Configuration::default(),
payload_size: max_chunk_layout.size() * max_number_of_chunks,
}
}

fn management_size(_memory_size: usize, _config: &Self::Configuration) -> usize {
Expand Down
9 changes: 6 additions & 3 deletions iceoryx2-cal/src/shm_allocator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub enum ShmAllocatorInitError {
AllocationFailed,
}

pub struct ResizeHint<Config: ShmAllocatorConfig> {
pub struct SharedMemorySetupHint<Config: ShmAllocatorConfig> {
pub(crate) payload_size: usize,
pub(crate) config: Config,
}
Expand All @@ -89,11 +89,14 @@ pub trait ShmAllocator: Debug + Send + Sync + 'static {
&self,
layout: Layout,
strategy: AllocationStrategy,
) -> ResizeHint<Self::Configuration>;
) -> SharedMemorySetupHint<Self::Configuration>;

/// Suggest a managed payload size under a provided configuration assuming that at most
/// `max_number_of_chunks` of memory are in use in parallel.
fn payload_size_hint(config: &Self::Configuration, max_number_of_chunks: usize) -> usize;
fn initial_setup_hint(
max_chunk_layout: Layout,
max_number_of_chunks: usize,
) -> SharedMemorySetupHint<Self::Configuration>;

/// Returns the required memory size of the additional dynamic part of the allocator that is
/// allocated in [`ShmAllocator::init()`].
Expand Down
66 changes: 40 additions & 26 deletions iceoryx2-cal/src/shm_allocator/pool_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

use std::{alloc::Layout, ptr::NonNull};
use std::{alloc::Layout, ptr::NonNull, sync::atomic::Ordering};

use crate::shm_allocator::{ShmAllocator, ShmAllocatorConfig};
use iceoryx2_bb_elementary::allocator::BaseAllocator;
use iceoryx2_bb_log::fail;
use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicUsize;

use super::{
AllocationStrategy, PointerOffset, ResizeHint, ShmAllocationError, ShmAllocatorInitError,
AllocationStrategy, PointerOffset, SharedMemorySetupHint, ShmAllocationError,
ShmAllocatorInitError,
};

#[derive(Clone, Copy, Debug)]
Expand All @@ -43,6 +45,7 @@ pub struct PoolAllocator {
// the allocator only manages a range of numbers
base_address: usize,
max_supported_alignment_by_memory: usize,
number_of_used_buckets: IoxAtomicUsize,
}

impl PoolAllocator {
Expand All @@ -62,43 +65,51 @@ impl ShmAllocator for PoolAllocator {
&self,
layout: Layout,
strategy: AllocationStrategy,
) -> ResizeHint<Self::Configuration> {
) -> SharedMemorySetupHint<Self::Configuration> {
let current_layout = unsafe {
Layout::from_size_align_unchecked(
self.allocator.bucket_size(),
self.allocator.max_alignment(),
)
};
let adjusted_alignment = current_layout.align().max(layout.align());
let adjusted_size = current_layout
.size()
.max(layout.size())
.next_multiple_of(adjusted_alignment);

let adjusted_layout =
unsafe { Layout::from_size_align_unchecked(adjusted_size, adjusted_alignment) };

let config = Self::Configuration {
bucket_layout: adjusted_layout,
};
let payload_size = match strategy {
AllocationStrategy::BestFit => {
Self::payload_size_hint(&config, (self.allocator.number_of_buckets() + 1) as usize)
let adjusted_number_of_buckets = if self.number_of_used_buckets.load(Ordering::Relaxed)
== self.number_of_buckets() as usize
{
match strategy {
AllocationStrategy::BestFit => self.allocator.number_of_buckets() + 1,
AllocationStrategy::PowerOfTwo => {
(self.allocator.number_of_buckets() + 1).next_power_of_two()
}
}
AllocationStrategy::PowerOfTwo => {
Self::payload_size_hint(&config, (self.allocator.number_of_buckets() + 1) as usize)
.next_power_of_two()
} else {
self.number_of_buckets()
};

let adjusted_layout = if current_layout != layout {
unsafe {
Layout::from_size_align_unchecked(
layout.size().next_power_of_two(),
layout.align().next_power_of_two(),
)
}
} else {
layout
};

ResizeHint {
payload_size,
config,
}
Self::initial_setup_hint(adjusted_layout, adjusted_number_of_buckets as usize)
}

fn payload_size_hint(config: &Self::Configuration, max_number_of_chunks: usize) -> usize {
config.bucket_layout.size() * max_number_of_chunks
fn initial_setup_hint(
max_chunk_layout: Layout,
max_number_of_chunks: usize,
) -> SharedMemorySetupHint<Self::Configuration> {
SharedMemorySetupHint {
payload_size: max_chunk_layout.size() * max_number_of_chunks,
config: Self::Configuration {
bucket_layout: max_chunk_layout,
},
}
}

fn management_size(memory_size: usize, config: &Self::Configuration) -> usize {
Expand All @@ -125,6 +136,7 @@ impl ShmAllocator for PoolAllocator {
),
base_address: (managed_memory.as_ptr() as *mut u8) as usize,
max_supported_alignment_by_memory,
number_of_used_buckets: IoxAtomicUsize::new(0),
}
}

Expand Down Expand Up @@ -162,12 +174,14 @@ impl ShmAllocator for PoolAllocator {
}

let chunk = fail!(from self, when self.allocator.allocate(layout), "{}.", msg);
self.number_of_used_buckets.fetch_add(1, Ordering::Relaxed);
Ok(PointerOffset::new(
(chunk.as_ptr() as *const u8) as usize - self.allocator.start_address(),
))
}

unsafe fn deallocate(&self, offset: PointerOffset, layout: Layout) {
self.number_of_used_buckets.fetch_sub(1, Ordering::Relaxed);
self.allocator.deallocate(
NonNull::new_unchecked((offset.offset() + self.allocator.start_address()) as *mut u8),
layout,
Expand Down
24 changes: 22 additions & 2 deletions iceoryx2-cal/tests/resizable_shared_memory_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ mod resizable_shared_memory {
.unwrap();
let mut sut_viewer = Sut::Builder::new(&storage_name)
.config(&config)
.max_number_of_chunks_hint(1)
.open()
.unwrap();

Expand Down Expand Up @@ -75,12 +74,13 @@ mod resizable_shared_memory {
.unwrap();
let mut sut_viewer = Sut::Builder::new(&storage_name)
.config(&config)
.max_number_of_chunks_hint(1)
.open()
.unwrap();

let ptr_creator_1 = sut_creator.allocate(Layout::new::<u64>()).unwrap();
assert_that!(sut_creator.number_of_active_segments(), eq 1);
let ptr_creator_2 = sut_creator.allocate(Layout::new::<u64>()).unwrap();
assert_that!(sut_creator.number_of_active_segments(), eq 2);

let test_value_1 = 109875896345234897;
let test_value_2 = 412384034975234569;
Expand Down Expand Up @@ -147,6 +147,26 @@ mod resizable_shared_memory {
assert_that!(sut_creator.number_of_active_segments(), eq 1);
}

// TODO:
// * no more key suggestions, increment key
// * make segment id u16
// * increasing chunk size
// * allocate/deallocate in random order
// * allocate until new segment, old segment id is never used
// * open with no more __0 segment
// * open with many segments
// * AllocationStrategy::PowerOfTwo -> doubling in size
// * list/does_exist/remove
// * has_ownership, acquire/release ownership
// * timeout
// * best fit, let reallocate until 256 exceeded, see if id is recycled
// * exceed max alignment
// * separate builder for view, without hints
// * start with layout.size == 1 and max_number_of_chunks == 1
// * allocate 1 byte
// * allocate N byte, may lead to 2 allocations, one for chunk resize, one for bucket number
// resize

#[instantiate_tests(<iceoryx2_cal::shared_memory::posix::Memory<DefaultAllocator>, resizable_shared_memory::dynamic::DynamicMemory<DefaultAllocator, iceoryx2_cal::shared_memory::posix::Memory<DefaultAllocator>>>)]
mod posix {}

Expand Down

0 comments on commit 2ad50ee

Please sign in to comment.