From bb2744da523880311115317d9063ddc0ecdc0a75 Mon Sep 17 00:00:00 2001 From: Babur Makhmudov Date: Tue, 24 Dec 2024 15:44:49 +0530 Subject: [PATCH] docs: update docs to reflect recent optmizations --- README.md | 41 +++++++++++++++------------------- src/header.rs | 16 +++++++++++-- src/lib.rs | 62 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 69 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 30c10c8..45ce95b 100644 --- a/README.md +++ b/README.md @@ -25,30 +25,12 @@ rendering it inefficient. 5. **Recycled Storage**: Upon buffer deallocation, the backing store becomes available for subsequent allocations. -## Design Philosophy - # Design Philosophy -The core functionality of RingAl is dedicated to an advanced and flexible memory management -system. This system is designed to support a wide array of allocation needs by employing guard -sequences. These guard sequences can dynamically adapt to varying allocation scenarios by being -created, changed and destroyed as required. The implementation efficiently utilizes a single -`usize` head pointer to manage these memory guards. - -This architecture is carefully crafted to ensure safe and efficient multithreaded buffer -operations. It allows exclusive write access to one thread while permitting simultaneous read -access by another thread. Although this design may naturally give rise to race conditions, -particularly when the writing thread releases the buffer without immediate notification to the -reading or allocating thread, these issues are mitigated through a strategy of optimistic -availability checks. When the allocating thread encounters an engaged buffer, it simply returns -`None`, signaling to the caller to retry the operation later. This approach effectively avoids -the need for costly atomic synchronization operations by relying on eventual consistency, which -is appropriate for the intended use cases of this allocator. - -It is important to highlight that this allocator is not marked as `Sync`, preventing its -concurrent use across multiple threads. All allocation operations require `&mut self`, -inherently disallowing the allocator from being wrapped within an `Arc`. Using locks around the -allocator is discouraged as it could significantly degrade performance, it is recommended to -utilize thread local storage instead. +RingAl library focuses on a robust and versatile memory management system, through the use of dynamic and self descriptive backing store. This system is engineered to accommodate a wide range of allocation requirements through the use of guard sequences. These guard sequences are capable of adjusting dynamically to different allocation conditions by being created, modified, and removed as necessary. The system effectively manages these memory guards using a single `usize` head pointer. + +The design is structured to ensure both safe and efficient multithreaded buffer operations. It grants exclusive write access to one thread while allowing another thread to read simultaneously. Although this design does inevitably lead to race conditions, particularly when the writing thread releases the buffer, without proper synchronization with reading or allocating thread, these issues are addressed through a method of optimistic availability checks. If the allocating thread finds a buffer in use, it returns `None`, indicating to the caller to retry the operation. This method avoids the need for expensive atomic synchronization by relying on eventual consistency, which is suitable for the use cases of this allocator. + +It is important to note that this allocator is not marked as `Sync`, which restricts its concurrent use across multiple threads. All allocation actions require `&mut self`, inherently preventing the allocator from being enclosed within an `Arc`. Using locks around the allocator is not recommended as it can greatly reduce performance; instead, it is advisable to use thread-local storage. ### Guard Insights: @@ -184,6 +166,19 @@ tx.send(buffer); handle.join(); ``` +### Thread Local Storage +```rust +ringal!(@init, 1024); +// allocate fixed buffer +let mut fixed = ringal!(@fixed, 64).unwrap(); +let _ = fixed.write(b"hello world!").unwrap(); +// allocate extendable buffer and write some data to it +ringal!{@ext, 64, |extendable| { + let _ = extendable.write(b"hello world!").unwrap(); + extendable.finalize() +}}; +``` + # Benchmarks Benchmark comparisons are made between two buffer allocator implementations: diff --git a/src/header.rs b/src/header.rs index d0fd647..c13928a 100644 --- a/src/header.rs +++ b/src/header.rs @@ -66,8 +66,20 @@ impl From
for Guard { impl Drop for Guard { #[inline(always)] fn drop(&mut self) { - // release the lock from memory region, - // making it available to allocator + // releases the lock from the specified memory region, thereby making it available for the allocator. + // + // # safety and ordering guarantees + // + // the operation of releasing the lock is non-atomic and migth cause race conditions in + // multithreaded context. It is ensured that the caller of `drop` is the sole entity + // capable of writing to the region at this point in the execution timeline. Furthermore, + // the compiler enforces strict ordering such that this operation cannot be reordered with + // other operations that might utilize this memory region. + // + // importantly, the allocator is restricted from accessing the region until it is fully released. + // While there may be a delay in the visibility of this operation to the allocator due to CPU caching, + // the change will eventually propagate to the allocator's thread. Consequently, potential race conditions + // are effectively mitigated by these guarantees, ensuring correctness and eventual consistency. *self.0 &= usize::MAX << 1; } } diff --git a/src/lib.rs b/src/lib.rs index 283a54c..615bffc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,32 +21,29 @@ //! 5. **Recycled Storage**: Upon buffer deallocation, the backing store becomes //! available for subsequent allocations. //! - -//! # Design Philosophy -//! -//! The core functionality of RingAl is dedicated to an advanced and flexible memory management -//! system. This system is designed to support a wide array of allocation needs by employing guard -//! sequences. These guard sequences can dynamically adapt to varying allocation scenarios by being -//! created, changed and destroyed as required. The implementation efficiently utilizes a single -//! `usize` head pointer to manage these memory guards. +//! RingAl library focuses on a robust and versatile memory management system, through the use of +//! dynamic and self descriptive backing store. This system is engineered to accommodate a wide +//! range of allocation requirements through the use of guard sequences. These guard sequences are +//! capable of adjusting dynamically to different allocation conditions by being created, modified, +//! and removed as necessary. The system effectively manages these memory guards using a single +//! `usize` head pointer. //! -//! This architecture is carefully crafted to ensure safe and efficient multithreaded buffer -//! operations. It allows exclusive write access to one thread while permitting simultaneous read -//! access by another thread. Although this design may naturally give rise to race conditions, -//! particularly when the writing thread releases the buffer without immediate notification to the -//! reading or allocating thread, these issues are mitigated through a strategy of optimistic -//! availability checks. When the allocating thread encounters an engaged buffer, it simply returns -//! `None`, signaling to the caller to retry the operation later. This approach effectively avoids -//! the need for costly atomic synchronization operations by relying on eventual consistency, which -//! is appropriate for the intended use cases of this allocator. +//! The design is structured to ensure both safe and efficient multithreaded buffer operations. It +//! grants exclusive write access to one thread while allowing another thread to read +//! simultaneously. Although this design does inevitably lead to race conditions, particularly when +//! the writing thread releases the buffer, without proper synchronization with reading or +//! allocating thread, these issues are addressed through a method of optimistic availability +//! checks. If the allocating thread finds a buffer in use, it returns `None`, indicating to the +//! caller to retry the operation. This method avoids the need for expensive atomic synchronization +//! by relying on eventual consistency, which is suitable for the use cases of this allocator. //! -//! It is important to highlight that this allocator is not marked as `Sync`, preventing its -//! concurrent use across multiple threads. All allocation operations require `&mut self`, -//! inherently disallowing the allocator from being wrapped within an `Arc`. Using locks around the -//! allocator is discouraged as it could significantly degrade performance, it is recommended to -//! utilize thread local storage instead. +//! It is important to note that this allocator is not marked as `Sync`, which restricts its +//! concurrent use across multiple threads. All allocation actions require `&mut self`, inherently +//! preventing the allocator from being enclosed within an `Arc`. Using locks around the allocator +//! is not recommended as it can greatly reduce performance; instead, it is advisable to use +//! thread-local storage. //! -//! # Guard Insights: +//! # Guard sequence insights: //! //! Each guard encodes: //! 1. A flag indicating whether the guarded memory region is in use. @@ -103,8 +100,6 @@ //! allocation for a reference counter and should be avoided unless necessary to //! prevent overhead. //! -//! For more details, visit the [RingAl Documentation](https://docs.rs/ringal). -//! //! # Optional Crate Features (Cargo) //! 1. **`tls` (Thread-Local Storage):** This feature enables advanced //! functionalities related to thread-local storage within the allocator. By @@ -188,6 +183,22 @@ //! handle.join(); //! ``` //! +//! ## Thread Local Storage +//! ```rust +//! # use ringal::ringal; +//! # use std::io::Write; +//! ringal!(@init, 1024); +//! // allocate fixed buffer +//! let mut fixed = ringal!(@fixed, 64).unwrap(); +//! let _ = fixed.write(b"hello world!").unwrap(); +//! // allocate extendable buffer and write some data to it +//! ringal!{@ext, 64, |mut extendable| { +//! let _ = extendable.write(b"hello world!").unwrap(); +//! extendable.finalize() +//! }}; +//! ``` +//! +//! //! # Dependencies //! The crate is designed without any external dependencies, and only relies on standard library //! @@ -364,6 +375,7 @@ impl RingAl { macro_rules! ringal { (@init, $capacity: expr) => { use std::cell::RefCell; + use $crate::RingAl; thread_local! { pub static RINGAL: RefCell = RefCell::new(RingAl::new($capacity));