Skip to content

Commit 09ed470

Browse files
committed
Proof of Concept: eliminate the stored ctrl ptr
1 parent 3353a68 commit 09ed470

File tree

1 file changed

+42
-28
lines changed

1 file changed

+42
-28
lines changed

src/raw/mod.rs

+42-28
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ fn bucket_mask_to_capacity(bucket_mask: usize) -> usize {
213213
/// Returns `None` if an overflow occurs.
214214
#[inline]
215215
#[cfg(feature = "nightly")]
216-
fn calculate_layout<T>(buckets: usize) -> Option<(Layout, usize, usize)> {
216+
fn calculate_layout<T>(buckets: usize) -> Option<(Layout, usize)> {
217217
debug_assert!(buckets.is_power_of_two());
218218

219219
// Misc fields of the map
@@ -232,23 +232,26 @@ fn calculate_layout<T>(buckets: usize) -> Option<(Layout, usize, usize)> {
232232
// Array of buckets
233233
let data = Layout::array::<T>(buckets).ok()?;
234234

235-
let (temp_layout, ctrl_offset) = header.extend(ctrl).ok()?;
235+
let (temp_layout, _ctrl_offset) = header.extend(ctrl).ok()?;
236236
let (final_layout, data_offset) = temp_layout.extend(data).ok()?;
237237

238-
Some((final_layout, ctrl_offset, data_offset))
238+
Some((final_layout, data_offset))
239239
}
240240

241241
// Returns a Layout which describes the allocation required for a hash table,
242242
// and the offset of the buckets in the allocation.
243243
#[inline]
244244
#[cfg(not(feature = "nightly"))]
245-
fn calculate_layout<T>(buckets: usize) -> Option<(Layout, usize, usize)> {
245+
fn calculate_layout<T>(buckets: usize) -> Option<(Layout, usize)> {
246246
debug_assert!(buckets.is_power_of_two());
247247

248248
// Manual layout calculation since Layout methods are not yet stable.
249249
let data_align = mem::align_of::<T>();
250-
let ctrl_align = usize::max(Group::WIDTH, data_align);
251-
let header_align = usize::max(mem::align_of::<Header<T>>(), ctrl_align);
250+
let ctrl_align = Group::WIDTH;
251+
let header_align = usize::max(
252+
usize::max(mem::align_of::<Header<T>>(), ctrl_align),
253+
data_align,
254+
);
252255

253256
let data_size = mem::size_of::<T>().checked_mul(buckets)?;
254257
let ctrl_size = buckets + Group::WIDTH;
@@ -260,11 +263,18 @@ fn calculate_layout<T>(buckets: usize) -> Option<(Layout, usize, usize)> {
260263

261264
Some((
262265
unsafe { Layout::from_size_align_unchecked(len, header_align) },
263-
ctrl_offset,
264266
data_offset,
265267
))
266268
}
267269

270+
#[inline]
271+
fn ctrl_offset<T>() -> usize {
272+
let header_size = mem::size_of::<Header<T>>();
273+
let ctrl_align = Group::WIDTH;
274+
let mask = ctrl_align - 1;
275+
(header_size + mask) & !mask
276+
}
277+
268278
#[inline]
269279
#[cfg(not(feature = "nightly"))]
270280
fn round_size_to_align(size: usize, align: usize) -> Option<usize> {
@@ -365,9 +375,6 @@ pub struct Header<T> {
365375
// number of buckets in the table.
366376
bucket_mask: usize,
367377

368-
// Pointer to the array of control bytes
369-
ctrl: *const u8,
370-
371378
// Pointer to the array of buckets
372379
data: *const T,
373380

@@ -378,22 +385,31 @@ pub struct Header<T> {
378385
items: usize,
379386
}
380387

381-
static EMPTY_SINGLETON: Header<()> = {
388+
static EMPTY_SINGLETON: &'static Header<()> = {
382389
union AlignedBytes {
383390
_align: Group,
384391
bytes: [u8; Group::WIDTH],
385392
};
386-
static ALIGNED_BYTES: AlignedBytes = AlignedBytes {
387-
bytes: [EMPTY; Group::WIDTH],
388-
};
389393

390-
Header {
391-
data: core::ptr::null(),
392-
ctrl: unsafe { &ALIGNED_BYTES.bytes as *const u8 },
393-
bucket_mask: 0,
394-
items: 0,
395-
growth_left: 0,
394+
#[repr(C)]
395+
struct HeaderWithCtrl {
396+
header: Header<()>,
397+
bytes: AlignedBytes,
396398
}
399+
400+
static ALIGNED_HEADER_WITH_CTRL: HeaderWithCtrl = HeaderWithCtrl {
401+
header: Header {
402+
data: core::ptr::null(),
403+
bucket_mask: 0,
404+
items: 0,
405+
growth_left: 0,
406+
},
407+
bytes: AlignedBytes {
408+
bytes: [EMPTY; Group::WIDTH],
409+
},
410+
};
411+
412+
&ALIGNED_HEADER_WITH_CTRL.header
397413
};
398414

399415
unsafe impl<T: Sync> Sync for Header<T> {}
@@ -407,7 +423,7 @@ impl<T> RawTable<T> {
407423
#[inline]
408424
pub fn new() -> Self {
409425
Self {
410-
header: NonNull::from(&EMPTY_SINGLETON).cast(),
426+
header: NonNull::from(EMPTY_SINGLETON).cast(),
411427
marker: PhantomData,
412428
}
413429
}
@@ -420,16 +436,14 @@ impl<T> RawTable<T> {
420436
buckets: usize,
421437
fallability: Fallibility,
422438
) -> Result<Self, CollectionAllocErr> {
423-
let (layout, ctrl_offset, data_offset) =
439+
let (layout, data_offset) =
424440
calculate_layout::<T>(buckets).ok_or_else(|| fallability.capacity_overflow())?;
425441

426442
let header = NonNull::new(alloc(layout)).ok_or_else(|| fallability.alloc_err(layout))?;
427-
let ctrl = header.as_ptr().add(ctrl_offset) as *mut u8;
428443
let data = header.as_ptr().add(data_offset) as *mut T;
429444

430445
let header = header.cast();
431446
*header.as_ptr() = Header {
432-
ctrl,
433447
data,
434448
bucket_mask: buckets - 1,
435449
items: 0,
@@ -472,7 +486,7 @@ impl<T> RawTable<T> {
472486
/// Deallocates the table without dropping any entries.
473487
#[inline]
474488
unsafe fn free_buckets(&mut self) {
475-
let (layout, _, _) =
489+
let (layout, _) =
476490
calculate_layout::<T>(self.buckets()).unwrap_or_else(|| hint::unreachable_unchecked());
477491
dealloc(self.header.as_ptr() as *mut u8, layout);
478492
}
@@ -506,7 +520,7 @@ impl<T> RawTable<T> {
506520
/// Returns a pointer to the ctrl array
507521
#[inline]
508522
fn ctrl_ptr(&self) -> *mut u8 {
509-
self.header().ctrl as *mut u8
523+
unsafe { (self.header.as_ptr() as *mut u8).add(ctrl_offset::<T>()) }
510524
}
511525

512526
#[inline]
@@ -1000,7 +1014,7 @@ impl<T> RawTable<T> {
10001014
/// of 0.
10011015
#[inline]
10021016
fn is_empty_singleton(&self) -> bool {
1003-
self.header() as *const Header<T> == &EMPTY_SINGLETON as *const _ as *const Header<T>
1017+
self.header() as *const Header<T> == EMPTY_SINGLETON as *const _ as *const Header<T>
10041018
}
10051019

10061020
/// Returns an iterator over every element in the table. It is up to
@@ -1037,7 +1051,7 @@ impl<T> RawTable<T> {
10371051
let alloc = if self.is_empty_singleton() {
10381052
None
10391053
} else {
1040-
let (layout, _, _) = calculate_layout::<T>(self.buckets())
1054+
let (layout, _) = calculate_layout::<T>(self.buckets())
10411055
.unwrap_or_else(|| unsafe { hint::unreachable_unchecked() });
10421056
Some((self.header.cast(), layout))
10431057
};

0 commit comments

Comments
 (0)