Skip to content

Commit 235c4c8

Browse files
committed
perf(buffer): store aligned mutable pointer
Signed-off-by: Nicholas Gates <nick@nickgates.com>
1 parent f200f3a commit 235c4c8

2 files changed

Lines changed: 28 additions & 17 deletions

File tree

vortex-buffer/src/buffer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ impl<T> Buffer<T> {
604604
.align_offset(physical_alignment.as_usize());
605605
Ok(BufferMut {
606606
allocation,
607-
offset,
607+
ptr,
608608
length,
609609
alignment,
610610
physical_alignment,

vortex-buffer/src/buffer_mut.rs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::trusted_len::TrustedLen;
2929
/// A mutable buffer that maintains a runtime-defined alignment through resizing operations.
3030
pub struct BufferMut<T> {
3131
pub(crate) allocation: Allocation,
32-
pub(crate) offset: usize,
32+
pub(crate) ptr: std::ptr::NonNull<T>,
3333
pub(crate) length: usize,
3434
pub(crate) alignment: Alignment,
3535
pub(crate) physical_alignment: Alignment,
@@ -38,6 +38,11 @@ pub struct BufferMut<T> {
3838
pub(crate) _marker: std::marker::PhantomData<T>,
3939
}
4040

41+
// SAFETY: BufferMut uniquely owns its allocation and only exposes T across threads.
42+
unsafe impl<T: Send> Send for BufferMut<T> {}
43+
// SAFETY: shared access to BufferMut only exposes shared access to T.
44+
unsafe impl<T: Sync> Sync for BufferMut<T> {}
45+
4146
impl<T> BufferMut<T> {
4247
/// Create a new `BufferMut` with the requested alignment and capacity.
4348
pub fn with_capacity(capacity: usize) -> Self {
@@ -130,9 +135,11 @@ impl<T> BufferMut<T> {
130135
};
131136
let allocation = Allocation::allocate(layout, allocator);
132137
let offset = allocation.ptr().as_ptr().align_offset(actual.as_usize());
138+
// SAFETY: the allocation includes enough padding to reach this aligned pointer.
139+
let ptr = unsafe { allocation.ptr().add(offset).cast() };
133140
Self {
134141
allocation,
135-
offset,
142+
ptr,
136143
length: 0,
137144
alignment,
138145
physical_alignment: actual,
@@ -219,9 +226,11 @@ impl<T> BufferMut<T> {
219226
.ptr()
220227
.as_ptr()
221228
.align_offset(actual_alignment.as_usize());
229+
// SAFETY: the allocation includes enough padding to reach this aligned pointer.
230+
let ptr = unsafe { allocation.ptr().add(offset).cast() };
222231
Self {
223232
allocation,
224-
offset,
233+
ptr,
225234
length: len,
226235
alignment,
227236
physical_alignment: actual_alignment,
@@ -396,7 +405,8 @@ impl<T> BufferMut<T> {
396405
}
397406

398407
if !self.overallocated {
399-
return (self.allocation.size() - self.offset) / size_of::<T>();
408+
let offset = self.ptr.cast::<u8>().addr().get() - self.allocation.ptr().addr().get();
409+
return (self.allocation.size() - offset) / size_of::<T>();
400410
}
401411

402412
(self.allocation.size() - self.physical_alignment.as_usize()) / size_of::<T>()
@@ -405,21 +415,19 @@ impl<T> BufferMut<T> {
405415
/// Returns a raw pointer to the buffer's data.
406416
#[inline(always)]
407417
pub fn as_ptr(&self) -> *const T {
408-
// SAFETY: offset always remains within the allocation.
409-
unsafe { self.allocation.ptr().as_ptr().add(self.offset).cast() }
418+
self.ptr.as_ptr()
410419
}
411420

412421
/// Returns a mutable raw pointer to the buffer's data.
413422
#[inline(always)]
414423
pub fn as_mut_ptr(&mut self) -> *mut T {
415-
// SAFETY: BufferMut uniquely owns the allocation and offset is in bounds.
416-
unsafe { self.allocation.ptr().as_ptr().add(self.offset).cast() }
424+
self.ptr.as_ptr()
417425
}
418426

419427
/// Returns a slice over the buffer of elements of type T.
420428
#[inline]
421429
pub fn as_slice(&self) -> &[T] {
422-
// SAFETY: the allocation is live, offset is in bounds, and construction checks alignment.
430+
// SAFETY: ptr is in the live allocation and construction checks its alignment.
423431
unsafe { std::slice::from_raw_parts(self.as_ptr(), self.length) }
424432
}
425433

@@ -491,7 +499,7 @@ impl<T> BufferMut<T> {
491499
let layout = Layout::from_size_align(allocation_size, allocation_alignment)
492500
.unwrap_or_else(|_| vortex_panic!("buffer capacity exceeds maximum allocation size"));
493501

494-
let old_offset = self.offset;
502+
let old_offset = self.ptr.cast::<u8>().addr().get() - self.allocation.ptr().addr().get();
495503
let new_offset = if self.allocation.allocator().is_statically_allocated() {
496504
let allocation =
497505
Allocation::allocate(layout, BufferAllocatorRef::statically_allocated());
@@ -502,7 +510,7 @@ impl<T> BufferMut<T> {
502510
// SAFETY: both allocations have room for the initialized elements and do not overlap.
503511
unsafe {
504512
std::ptr::copy_nonoverlapping(
505-
self.allocation.ptr().as_ptr().add(old_offset),
513+
self.ptr.cast::<u8>().as_ptr(),
506514
allocation.ptr().as_ptr().add(new_offset),
507515
self.length * size_of::<T>(),
508516
);
@@ -530,7 +538,8 @@ impl<T> BufferMut<T> {
530538
}
531539
new_offset
532540
};
533-
self.offset = new_offset;
541+
// SAFETY: new_offset was computed within the allocation for physical_alignment.
542+
self.ptr = unsafe { self.allocation.ptr().add(new_offset).cast() };
534543
self.physical_alignment = physical_alignment;
535544
self.overallocated = true;
536545
}
@@ -678,7 +687,7 @@ impl<T> BufferMut<T> {
678687
pub fn into_byte_buffer(self) -> ByteBufferMut {
679688
ByteBufferMut {
680689
allocation: self.allocation,
681-
offset: self.offset,
690+
ptr: self.ptr.cast(),
682691
length: self.length * size_of::<T>(),
683692
alignment: self.alignment,
684693
physical_alignment: self.physical_alignment,
@@ -689,9 +698,10 @@ impl<T> BufferMut<T> {
689698

690699
/// Freeze the `BufferMut` into a `Buffer`.
691700
pub fn freeze(self) -> Buffer<T> {
701+
let offset = self.ptr.cast::<u8>().addr().get() - self.allocation.ptr().addr().get();
692702
Buffer::from_allocation(
693703
self.allocation,
694-
self.offset,
704+
offset,
695705
self.length,
696706
self.alignment,
697707
self.physical_alignment,
@@ -756,7 +766,7 @@ impl<T> BufferMut<T> {
756766

757767
BufferMut {
758768
allocation: self.allocation,
759-
offset: self.offset,
769+
ptr: self.ptr.cast(),
760770
length: self.length,
761771
alignment: self.alignment,
762772
physical_alignment: self.physical_alignment,
@@ -1000,7 +1010,8 @@ impl Buf for ByteBufferMut {
10001010
);
10011011
}
10021012
assert!(cnt <= self.length, "advance out of bounds");
1003-
self.offset += cnt;
1013+
// SAFETY: cnt is checked against the initialized length above.
1014+
self.ptr = unsafe { self.ptr.add(cnt) };
10041015
self.length -= cnt;
10051016
}
10061017
}

0 commit comments

Comments
 (0)