-
Notifications
You must be signed in to change notification settings - Fork 79
Add optimized grow()/shrink() functions to Allocator implementation for pool #191
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
Conversation
423bf5f
to
91d7f67
Compare
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.
Pull Request Overview
This PR implements optimized grow()
and shrink()
functions for the Pool allocator that can avoid real allocations when resizing the last allocation in the pool by simply adjusting the pool's last pointer.
- Adds
grow()
method that can expand the last allocation in-place if there's sufficient space - Adds
shrink()
method that can contract the last allocation in-place by moving the pool's last pointer backward - Falls back to allocate-copy-deallocate pattern when in-place resizing isn't possible
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
91d7f67
to
c6e44e6
Compare
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.
Some minor nitpicks, for the commit log
Making the commit log shorter and adding a new line for the body.
`feat: Add optimized grow()/shrink() functions for Pool
If resizing is requested for the last allocation in the pool, it may be
possible to adjust pool data and avoid any real allocations.`
?
Otherwise looks good.
c6e44e6
to
433e6e0
Compare
|
22992a9
to
5bf391f
Compare
5bf391f
to
15f257a
Compare
15f257a
to
00f3ccb
Compare
BTW, were you able to try it somehow (manual test rust code)? Are you sure it will work in practice? |
It took some time, but here is the text of simple tests: #[cfg(test)]
mod tests {
use nginx_sys::{ngx_create_pool, ngx_destroy_pool};
use super::*;
unsafe extern "C" {
pub unsafe fn libngx_init(prefix: *mut nginx_sys::u_char) -> *mut nginx_sys::ngx_cycle_t;
}
#[test]
fn test_pool_resize() {
let _ = unsafe {
libngx_init("prefix".as_ptr() as *mut nginx_sys::u_char);
};
let mut log: nginx_sys::ngx_log_t = unsafe { core::mem::zeroed() };
let p: *mut nginx_sys::ngx_pool_t = unsafe { ngx_create_pool(1024, &mut log) };
let pool = unsafe { Pool::from_ngx_pool(p) };
let layout = Layout::from_size_align(16, 8).unwrap();
let slice_ptr = pool.allocate(layout).unwrap();
let ptr = slice_ptr.as_ptr().cast::<u8>();
let new_layout = Layout::from_size_align(32, 8).unwrap();
let nonnull_ptr = unsafe { NonNull::new_unchecked(ptr) };
let newptr = unsafe { pool.resize(nonnull_ptr, layout, new_layout) };
assert!(newptr.is_ok());
assert!(std::ptr::addr_eq(newptr.unwrap().as_ptr(), ptr));
unsafe { ngx_destroy_pool(p) };
}
#[test]
fn test_vec() {
let _ = unsafe {
libngx_init("prefix".as_ptr() as *mut nginx_sys::u_char);
};
let mut log: nginx_sys::ngx_log_t = unsafe { core::mem::zeroed() };
let p: *mut nginx_sys::ngx_pool_t = unsafe { ngx_create_pool(1024, &mut log) };
let pool = unsafe { Pool::from_ngx_pool(p) };
let mut v1: allocator_api2::vec::Vec<u8,Pool> = allocator_api2::vec::Vec::new_in(pool);
v1.reserve(4);
assert!(v1.capacity() >= 4);
let v1_ptr1 = v1.as_ptr();
v1.reserve(4);
assert!(v1.capacity() >= 8);
let v1_ptr2 = v1.as_ptr();
assert!(v1_ptr1 == v1_ptr2);
v1.resize(4, 1);
v1.shrink_to_fit();
let v1_ptr3 = v1.as_ptr();
assert!(v1_ptr1 == v1_ptr3);
unsafe { ngx_destroy_pool(p) };
}
#[test]
fn test_two_vecs() {
let _ = unsafe {
libngx_init("prefix".as_ptr() as *mut nginx_sys::u_char);
};
let mut log: nginx_sys::ngx_log_t = unsafe { core::mem::zeroed() };
let p: *mut nginx_sys::ngx_pool_t = unsafe { ngx_create_pool(2048, &mut log) };
let pool = unsafe { Pool::from_ngx_pool(p) };
let mut v1: allocator_api2::vec::Vec<u8,Pool> = allocator_api2::vec::Vec::new_in(pool.clone());
v1.reserve(128);
assert!(v1.capacity() >= 128);
let v1_ptr1 = v1.as_ptr();
v1.resize(128, 1);
let mut v2: allocator_api2::vec::Vec<u8,Pool> = allocator_api2::vec::Vec::new_in(pool);
v2.reserve(128);
assert!(v2.capacity() >= 128);
v1.reserve(128);
assert!(v1.capacity() >= 256, "actual capacity: {}", v1.capacity());
let v1_ptr2 = v1.as_ptr();
assert!(v1_ptr1 != v1_ptr2);
unsafe { ngx_destroy_pool(p) };
}
} They work as expected. I don't add these tests to PR because some tricks are needed to link test executable with nginx. These tricks deserve special PR, but they are not generalized yet to be published. |
8929b39
to
8a0f2ad
Compare
The last changes were done after offline discussions:
|
If resizing is requested for the last allocation in the pool, it may be possible to adjust pool data and avoid any real allocations.
8a0f2ad
to
3c7d1f9
Compare
Proposed changes
If resizing is requested for the last allocation in the pool, it may be possible to adjust pool data and avoid any real allocations.
Checklist
Before creating a PR, run through this checklist and mark each as complete.