-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
thread-local arena: grow buf if there is no enough space #116
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
b7298e2
skiplist: adding ut for arena
zhangjinpeng87 bce2a28
arena: align with 8 bytes
zhangjinpeng87 a7be258
add detailed comment for the reserved bytes at the beginning of arena
zhangjinpeng87 f0e0049
Merge branch 'master' into bench
zhangjinpeng87 682afe5
Merge branch 'master' into bench
zhangjinpeng87 c172882
Node::alloc, return None if arena has no enough space
zhangjinpeng87 25586ad
Merge branch 'master' into bench
zhangjinpeng87 6cbe734
thread-local arena: grow buf if there is no enough space
zhangjinpeng87 6b15cc3
Merge branch 'master' into bench
zhangjinpeng87 d7e16f8
clearify the comments in Arena::alloc
zhangjinpeng87 cc3a0f2
skiplist: Arc<Arena> -> Arena
zhangjinpeng87 7a1f694
Merge branch 'master' into bench
zhangjinpeng87 5f4b668
address comments: remove ArenaCore
zhangjinpeng87 File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,121 @@ | ||
use std::{ | ||
cell::Cell, | ||
mem, ptr, | ||
sync::{ | ||
atomic::{AtomicU32, Ordering}, | ||
Arc, | ||
}, | ||
sync::atomic::{AtomicUsize, Ordering}, | ||
}; | ||
|
||
struct ArenaCore { | ||
len: AtomicU32, | ||
cap: usize, | ||
ptr: *mut u8, | ||
const ADDR_ALIGN_MASK: usize = 7; | ||
|
||
// Thread-local Arena | ||
pub struct Arena { | ||
len: AtomicUsize, | ||
cap: Cell<usize>, | ||
ptr: Cell<*mut u8>, | ||
} | ||
|
||
impl Drop for ArenaCore { | ||
impl Drop for Arena { | ||
fn drop(&mut self) { | ||
let ptr = self.ptr as *mut u64; | ||
let cap = self.cap / 8; | ||
let ptr = self.ptr.get() as *mut u64; | ||
let cap = self.cap.get() / 8; | ||
unsafe { | ||
Vec::from_raw_parts(ptr, 0, cap); | ||
} | ||
} | ||
} | ||
|
||
pub struct Arena { | ||
core: Arc<ArenaCore>, | ||
} | ||
|
||
impl Arena { | ||
pub fn with_capacity(cap: u32) -> Arena { | ||
let mut buf: Vec<u64> = Vec::with_capacity(cap as usize / 8); | ||
pub fn with_capacity(cap: usize) -> Arena { | ||
let mut buf: Vec<u64> = Vec::with_capacity(cap / 8); | ||
let ptr = buf.as_mut_ptr() as *mut u8; | ||
let cap = buf.capacity() * 8; | ||
mem::forget(buf); | ||
Arena { | ||
core: Arc::new(ArenaCore { | ||
len: AtomicU32::new(1), | ||
cap, | ||
ptr, | ||
}), | ||
// Offset 0 is invalid value for func `offset` and `get_mut`, initialize the | ||
// len 8 to guarantee the allocated memory addr is always align with 8 bytes. | ||
len: AtomicUsize::new(8), | ||
cap: Cell::new(cap), | ||
ptr: Cell::new(ptr), | ||
} | ||
} | ||
|
||
pub fn len(&self) -> u32 { | ||
self.core.len.load(Ordering::SeqCst) | ||
pub fn len(&self) -> usize { | ||
self.len.load(Ordering::SeqCst) | ||
} | ||
|
||
pub fn alloc(&self, align: usize, size: usize) -> u32 { | ||
let align_mask = align - 1; | ||
// Leave enough padding for align. | ||
let size = size + align_mask; | ||
let offset = self.core.len.fetch_add(size as u32, Ordering::SeqCst); | ||
// Calculate the correct align point, it equals to | ||
// (offset + align_mask) / align * align. | ||
let ptr_offset = (offset as usize + align_mask) & !align_mask; | ||
assert!(offset as usize + size <= self.core.cap); | ||
ptr_offset as u32 | ||
/// Alloc 8-byte aligned memory. | ||
pub fn alloc(&self, size: usize) -> usize { | ||
// Leave enough padding for alignment. | ||
let size = (size + ADDR_ALIGN_MASK) & !ADDR_ALIGN_MASK; | ||
let offset = self.len.fetch_add(size, Ordering::SeqCst); | ||
|
||
// Grow the arena if there is no enough space | ||
if offset + size > self.cap.get() { | ||
// Alloc new buf and copy data to new buf | ||
let mut grow_by = self.cap.get(); | ||
if grow_by > 1 << 30 { | ||
grow_by = 1 << 30; | ||
} | ||
if grow_by < size { | ||
grow_by = size; | ||
} | ||
let mut new_buf: Vec<u64> = Vec::with_capacity((self.cap.get() + grow_by) as usize / 8); | ||
let new_ptr = new_buf.as_mut_ptr() as *mut u8; | ||
unsafe { | ||
ptr::copy_nonoverlapping(new_ptr, self.ptr.get(), self.cap.get()); | ||
} | ||
|
||
// Release old buf | ||
let old_ptr = self.ptr.get() as *mut u64; | ||
unsafe { | ||
Vec::from_raw_parts(old_ptr, 0, self.cap.get() / 8); | ||
} | ||
|
||
// Use new buf | ||
self.ptr.set(new_ptr); | ||
self.cap.set(new_buf.capacity() * 8); | ||
mem::forget(new_buf); | ||
} | ||
offset | ||
} | ||
|
||
pub unsafe fn get_mut<N>(&self, offset: u32) -> *mut N { | ||
pub unsafe fn get_mut<N>(&self, offset: usize) -> *mut N { | ||
if offset == 0 { | ||
return ptr::null_mut(); | ||
} | ||
self.core.ptr.add(offset as usize) as _ | ||
|
||
self.ptr.get().add(offset) as _ | ||
} | ||
|
||
pub fn offset<N>(&self, ptr: *const N) -> u32 { | ||
pub fn offset<N>(&self, ptr: *const N) -> usize { | ||
let ptr_addr = ptr as usize; | ||
let self_addr = self.core.ptr as usize; | ||
if ptr_addr > self_addr && ptr_addr < self_addr + self.core.cap { | ||
(ptr_addr - self_addr) as u32 | ||
let self_addr = self.ptr.get() as usize; | ||
if ptr_addr > self_addr && ptr_addr < self_addr + self.cap.get() { | ||
ptr_addr - self_addr | ||
} else { | ||
0 | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_arena() { | ||
// There is enough space | ||
let arena = Arena::with_capacity(128); | ||
let offset = arena.alloc(8); | ||
assert_eq!(offset, 8); | ||
assert_eq!(arena.len(), 16); | ||
unsafe { | ||
let ptr = arena.get_mut::<u64>(offset); | ||
let offset = arena.offset::<u64>(ptr); | ||
assert_eq!(offset, 8); | ||
} | ||
|
||
// There is not enough space, grow buf and then return the offset | ||
let offset = arena.alloc(256); | ||
assert_eq!(offset, 16); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Allocate a more aligned ptr on the same arena:
alloc(16,16)
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.
good catch
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.
Currently we simply alloc align to 8
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 some libraries and compilers expects to align with 16. Check jemalloc/jemalloc#1533. Perhaps we should add static assert on all types we want to store can be aligned with 8.