mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Promote memo levels by provenance
This commit is contained in:
@@ -1073,9 +1073,11 @@ impl FrameArena {
|
||||
|
||||
/// The whole buffer as free space, for one evaluation.
|
||||
pub fn frames(&mut self) -> Frames<'_> {
|
||||
let base = self.buf.as_mut_ptr().cast::<u8>();
|
||||
Frames {
|
||||
base: std::cell::Cell::new(self.buf.as_mut_ptr().cast::<u8>()),
|
||||
base: std::cell::Cell::new(base),
|
||||
words: std::cell::Cell::new(self.buf.len()),
|
||||
bounds: (base as usize, self.buf.len() * 8),
|
||||
_lifetime: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -1092,6 +1094,9 @@ impl FrameArena {
|
||||
pub struct Frames<'e> {
|
||||
base: std::cell::Cell<*mut u8>,
|
||||
words: std::cell::Cell<usize>,
|
||||
/// The whole buffer as (address, bytes), which stays fixed while claims
|
||||
/// advance the cursor, so a promote can range-check a reference against it.
|
||||
bounds: (usize, usize),
|
||||
_lifetime: std::marker::PhantomData<&'e ()>,
|
||||
}
|
||||
|
||||
@@ -1110,10 +1115,17 @@ impl<'e> Frames<'e> {
|
||||
Frames {
|
||||
base: std::cell::Cell::new(self.base.get()),
|
||||
words: std::cell::Cell::new(self.words.get()),
|
||||
bounds: self.bounds,
|
||||
_lifetime: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// The whole frame buffer as (address, bytes), the range a promote treats
|
||||
/// as evaluation-lived.
|
||||
pub fn bounds(&self) -> (usize, usize) {
|
||||
self.bounds
|
||||
}
|
||||
|
||||
/// Runs claims against this space and gives it back on the guard's drop, so
|
||||
/// a loop that evaluates a subtree per iteration reuses the same region.
|
||||
pub fn scope(&self) -> FrameScope<'_, 'e> {
|
||||
@@ -1437,8 +1449,9 @@ where
|
||||
if let Some(deep) = deep_element_glue(std::any::TypeId::of::<T::Static>()) {
|
||||
return unsafe { (deep.repark)(value, dst, arena) };
|
||||
}
|
||||
let retained = retained_measure(std::any::TypeId::of::<T::Static>()).map_or(0, |measure| measure(value));
|
||||
let value = value.downcast_ref::<T::Static>().expect("an element replays at its own type");
|
||||
unsafe { write_element(dst, value.clone(), arena) }
|
||||
unsafe { write_element_sized(dst, value.clone(), arena, retained) }
|
||||
}
|
||||
let (size, align) = element_dims::<T>();
|
||||
ElementWrite {
|
||||
@@ -1525,9 +1538,17 @@ pub unsafe fn read_element<T: Clone>(rec: Rec<'_>) -> T {
|
||||
/// `dst` must be fresh element storage of a record whose element is `T`.
|
||||
/// `None` reports arena exhaustion for a parked element.
|
||||
pub unsafe fn write_element<T: Send + Sync>(dst: *mut u8, value: T, arena: &crate::arena::Arena) -> Option<()> {
|
||||
unsafe { write_element_sized(dst, value, arena, 0) }
|
||||
}
|
||||
|
||||
/// [`write_element`] with the park glue's estimate of the heap `value` owns.
|
||||
///
|
||||
/// # Safety
|
||||
/// As [`write_element`].
|
||||
pub unsafe fn write_element_sized<T: Send + Sync>(dst: *mut u8, value: T, arena: &crate::arena::Arena, retained: usize) -> Option<()> {
|
||||
match element_parked::<T>() {
|
||||
true => {
|
||||
let (parked, _) = arena.alloc(value)?;
|
||||
let (parked, _) = arena.alloc_sized(value, retained)?;
|
||||
unsafe { dst.cast::<&T>().write(parked) };
|
||||
Some(())
|
||||
}
|
||||
@@ -1769,6 +1790,154 @@ impl<'e, 'l> FrameClaim<'e, 'l> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The regions a promote dispatches on. A payload already living in the
|
||||
/// persistent region outlives every entry promoted into it and is shared; a
|
||||
/// payload in the transient arena or the frame buffer dies at the next reset
|
||||
/// and is cloned.
|
||||
///
|
||||
/// The dispatch is decidable only where the reference addresses the payload
|
||||
/// itself, which holds for parked elements and for a group interior's frames.
|
||||
/// A reference-valued attribute instead names heap its arena-parked owner
|
||||
/// holds, at an address in no arena's range, so those always clone.
|
||||
///
|
||||
/// THE SHARING LAW: sharing a payload between persistent entries is sound
|
||||
/// because persistent invalidation is epochal, so every entry dies at one
|
||||
/// flush and no entry can outlive a payload another still names. Per-entry
|
||||
/// eviction would have to refcount the shared payloads before it could
|
||||
/// reclaim one entry's storage.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Promotion<'a> {
|
||||
transient: &'a crate::arena::Arena,
|
||||
frames: (usize, usize),
|
||||
persistent: &'a crate::arena::Arena,
|
||||
}
|
||||
|
||||
impl<'a> Promotion<'a> {
|
||||
/// `frames` is the whole frame buffer as (address, bytes), from
|
||||
/// [`Frames::bounds`].
|
||||
pub fn new(transient: &'a crate::arena::Arena, frames: (usize, usize), persistent: &'a crate::arena::Arena) -> Self {
|
||||
Promotion { transient, frames, persistent }
|
||||
}
|
||||
|
||||
pub fn persistent(&self) -> &'a crate::arena::Arena {
|
||||
self.persistent
|
||||
}
|
||||
|
||||
/// Whether the reference dies with the evaluation, which is the promote's
|
||||
/// clone-or-share question. A null or byte-carried slot reads as neither
|
||||
/// region's and shares.
|
||||
pub fn evaluation_lived(&self, ptr: *const u8) -> bool {
|
||||
self.transient.contains(ptr) || (ptr as usize).wrapping_sub(self.frames.0) < self.frames.1
|
||||
}
|
||||
}
|
||||
|
||||
/// Rewrites one promoted record's parked references in place: the lane bytes
|
||||
/// are already the persistent region's, and each reference whose payload dies
|
||||
/// with the evaluation is replaced by a clone parked there.
|
||||
///
|
||||
/// A parked element rides the arena slot its payload was written into, so the
|
||||
/// region holding it decides share against clone. A parked field's reference
|
||||
/// instead points into heap its arena-parked owner holds, which lies in no
|
||||
/// arena range, so provenance is undecidable there and the field always
|
||||
/// clones.
|
||||
///
|
||||
/// # Safety
|
||||
/// `dst` must be a persistent image of a live record of `layout`.
|
||||
unsafe fn promote_record(layout: &Layout, dst: *mut u8, promotion: &Promotion<'_>) -> Option<()> {
|
||||
if layout.element.parked {
|
||||
// SAFETY: a parked element slot holds one reference at offset 0.
|
||||
let parked = unsafe { dst.cast::<*const u8>().read() };
|
||||
if !promotion.persistent.contains(parked) {
|
||||
match element_promote_glue(layout.element.type_id) {
|
||||
// SAFETY: the slot images a parked element of this type.
|
||||
Some(promote) => unsafe { promote(dst.cast_const(), dst, promotion) }?,
|
||||
None => {
|
||||
// SAFETY: as above, and the clone owns its content.
|
||||
let owned = unsafe { (layout.element.clone_out)(dst.cast_const()) };
|
||||
unsafe { (layout.element.repark)(&*owned, dst, promotion.persistent) }?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for field in &layout.fields {
|
||||
let Some(repark) = field.repark else { continue };
|
||||
// SAFETY: a parked field slot holds one reference at its offset.
|
||||
let slot = unsafe { dst.add(field.offset) };
|
||||
// SAFETY: the slot images a parked field of this descriptor.
|
||||
let value = deepen_field_value(unsafe { (field.read_erased)(slot.cast_const()) });
|
||||
match deep_field_glue(value.as_any().type_id()) {
|
||||
Some(glue) => match (glue.replay)(&*value, promotion.persistent)? {
|
||||
// SAFETY: the replay produced this field's own value type.
|
||||
Some(resident) => unsafe { repark(&*resident, slot, promotion.persistent) }?,
|
||||
None => unsafe { repark(&*value, slot, promotion.persistent) }?,
|
||||
},
|
||||
None => unsafe { repark(&*value, slot, promotion.persistent) }?,
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Re-walks a promoted record and asserts no reference into the evaluation's
|
||||
/// storage survived, which is the postcondition every later hit and every
|
||||
/// shared interior relies on. The element's payload must sit in the persistent
|
||||
/// region itself; a field's payload is heap its owner holds, so the weaker
|
||||
/// range check is all that is decidable there.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ptr` must be a live record of `layout`.
|
||||
pub unsafe fn assert_promoted(layout: &Layout, ptr: *const u8, promotion: &Promotion<'_>) {
|
||||
if layout.element.parked {
|
||||
// SAFETY: a parked element slot holds one reference at offset 0.
|
||||
let parked = unsafe { ptr.cast::<*const u8>().read() };
|
||||
assert!(promotion.persistent.contains(parked), "a promoted element kept a reference outside the persistent region");
|
||||
}
|
||||
for field in &layout.fields {
|
||||
if field.repark.is_none() {
|
||||
continue;
|
||||
}
|
||||
// SAFETY: a parked field slot holds one reference at its offset.
|
||||
let parked = unsafe { ptr.add(field.offset).cast::<*const u8>().read() };
|
||||
assert!(!promotion.evaluation_lived(parked), "a promoted field kept a reference into the evaluation");
|
||||
}
|
||||
}
|
||||
|
||||
/// The promote override for element types whose payload holds arena-resident
|
||||
/// interiors: the generic path clones through an owned intermediate, while a
|
||||
/// registered promote shares the interiors already living in the persistent
|
||||
/// region and copies only the rest.
|
||||
type ElementPromote = unsafe fn(*const u8, *mut u8, &Promotion<'_>) -> Option<()>;
|
||||
|
||||
static ELEMENT_PROMOTES: std::sync::LazyLock<std::sync::Mutex<std::collections::HashMap<std::any::TypeId, ElementPromote>>> = std::sync::LazyLock::new(Default::default);
|
||||
|
||||
/// Registers the promote for elements of `T`. Called at startup from the crate
|
||||
/// that owns the type. The promote must leave no reference the promotion calls
|
||||
/// evaluation-lived.
|
||||
pub fn register_element_promote<T: dyn_any::StaticTypeSized>(promote: ElementPromote) {
|
||||
ELEMENT_PROMOTES.lock().unwrap().insert(std::any::TypeId::of::<T::Static>(), promote);
|
||||
}
|
||||
|
||||
fn element_promote_glue(type_id: std::any::TypeId) -> Option<ElementPromote> {
|
||||
ELEMENT_PROMOTES.lock().unwrap().get(&type_id).copied()
|
||||
}
|
||||
|
||||
/// The park glue's heap estimate for values of a type, keyed as the deep glue
|
||||
/// is. Consulted where a payload parks, so a region's retained heap is known
|
||||
/// without walking it.
|
||||
type RetainedMeasure = fn(&(dyn std::any::Any + Send + Sync)) -> usize;
|
||||
|
||||
static RETAINED_MEASURES: std::sync::LazyLock<std::sync::Mutex<std::collections::HashMap<std::any::TypeId, RetainedMeasure>>> = std::sync::LazyLock::new(Default::default);
|
||||
|
||||
/// Registers the retained-heap estimate for values of `T`. Called at startup
|
||||
/// from the crate that owns the type. The estimate is a hint: an unregistered
|
||||
/// type contributes 0, so a region's counter is a lower bound.
|
||||
pub fn register_retained_heap<T: dyn_any::StaticTypeSized>(measure: RetainedMeasure) {
|
||||
RETAINED_MEASURES.lock().unwrap().insert(std::any::TypeId::of::<T::Static>(), measure);
|
||||
}
|
||||
|
||||
fn retained_measure(type_id: std::any::TypeId) -> Option<RetainedMeasure> {
|
||||
RETAINED_MEASURES.lock().unwrap().get(&type_id).copied()
|
||||
}
|
||||
|
||||
/// A materialized run of lanes as the arena region its frames live in: the
|
||||
/// handle keeps the provenance the region was allocated with and carries the
|
||||
/// generation, so resolving it re-checks liveness where an address would have
|
||||
@@ -1801,14 +1970,16 @@ impl MaterializedSpan {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the batch into `arena`, re-parking every payload its records
|
||||
/// reference so the span's bytes reference nothing outside that region.
|
||||
/// `None` where the region could not hold the copy, which leaves the
|
||||
/// caller with nothing to cache.
|
||||
/// Copies the batch into the persistent region as a copy-on-write over
|
||||
/// provenance: the frame bytes memcpy, and each parked reference is cloned
|
||||
/// only where it dies with the evaluation, so a layout carrying no parked
|
||||
/// slot reduces to the memcpy and a level whose payloads an upstream memo
|
||||
/// already published costs nothing beyond it. `None` where the region
|
||||
/// could not hold the copy, which leaves the caller with nothing to cache.
|
||||
///
|
||||
/// # Safety
|
||||
/// The batch's lanes must be live records of its layout.
|
||||
pub unsafe fn promote(batch: &crate::node::RecordBatch<'_>, arena: &crate::arena::Arena) -> Option<MaterializedSpan> {
|
||||
pub unsafe fn to_persistent(batch: &crate::node::RecordBatch<'_>, promotion: &Promotion<'_>) -> Option<MaterializedSpan> {
|
||||
let layout = batch.layout();
|
||||
let stride = layout.lane_stride();
|
||||
let len = batch.len();
|
||||
@@ -1818,17 +1989,24 @@ impl MaterializedSpan {
|
||||
len: 0,
|
||||
});
|
||||
}
|
||||
let slab = arena.alloc_scratch::<u64>((len * stride).div_ceil(8))?;
|
||||
let persistent = promotion.persistent();
|
||||
let slab = persistent.alloc_scratch::<u64>((len * stride).div_ceil(8))?;
|
||||
let base: *mut u8 = slab.as_mut_ptr().cast();
|
||||
for lane in 0..len {
|
||||
// SAFETY: the caller's contract on the batch's lanes.
|
||||
let owned = unsafe { OwnedRecord::copy_out(layout, batch.get(lane).rec()) };
|
||||
// SAFETY: the lane's own region of the freshly reserved slab.
|
||||
// SAFETY: the caller's contract on the lane, into the lane's own
|
||||
// region of the freshly reserved slab.
|
||||
let dst = unsafe { base.add(lane * stride) };
|
||||
owned.write_into(layout, dst, arena)?;
|
||||
unsafe { std::ptr::copy_nonoverlapping(batch.get(lane).rec().ptr(), dst, layout.size) };
|
||||
// SAFETY: the copy images a record of this layout.
|
||||
unsafe { promote_record(layout, dst, promotion) }?;
|
||||
}
|
||||
#[cfg(debug_assertions)]
|
||||
for lane in 0..len {
|
||||
// SAFETY: every lane was imaged and promoted above.
|
||||
unsafe { assert_promoted(layout, base.add(lane * stride).cast_const(), promotion) };
|
||||
}
|
||||
Some(MaterializedSpan {
|
||||
base: arena.handle_at(base.cast_const())?,
|
||||
base: persistent.handle_at(base.cast_const())?,
|
||||
len,
|
||||
})
|
||||
}
|
||||
@@ -2315,6 +2493,17 @@ impl<'e> GroupItem<'e> {
|
||||
assert!(field.repark.is_none() || field.content_hash.is_some(), "a parked field adopts only with content glue");
|
||||
}
|
||||
let stride = layout.lane_stride();
|
||||
// A run already living in the target region needs no copy: the arena is
|
||||
// insert-only within a generation, so the lanes cannot move or change
|
||||
// while the adopting item borrows them.
|
||||
if batch.len() > 0 && arena.contains(batch.frames_ptr()) {
|
||||
return Some(Self {
|
||||
layout,
|
||||
storage: ItemStorage::Resident(batch.frames_ptr()),
|
||||
len: batch.len(),
|
||||
_arena: std::marker::PhantomData,
|
||||
});
|
||||
}
|
||||
let scratch = arena.alloc_scratch::<u64>((batch.len() * stride).div_ceil(8))?;
|
||||
let frames = scratch.as_mut_ptr().cast::<u8>();
|
||||
for lane in 0..batch.len() {
|
||||
@@ -2489,6 +2678,42 @@ impl<'e> GroupItem<'e> {
|
||||
})
|
||||
}
|
||||
|
||||
/// The run promoted into the persistent region: a run already living there
|
||||
/// is shared, since its own promote left it free of evaluation-lived
|
||||
/// references, and one that dies with the evaluation is copied lane by lane
|
||||
/// with the same dispatch applied to its parked references. This is what
|
||||
/// keeps a promote proportional to newly produced data.
|
||||
pub fn to_persistent<'p>(&self, promotion: &Promotion<'p>) -> Option<GroupItem<'p>> {
|
||||
let ItemStorage::Resident(frames) = self.storage else {
|
||||
return self.replay(promotion.persistent());
|
||||
};
|
||||
let persistent = promotion.persistent();
|
||||
if self.len == 0 || persistent.contains(frames) {
|
||||
return Some(GroupItem {
|
||||
layout: self.layout.clone(),
|
||||
storage: ItemStorage::Resident(frames),
|
||||
len: self.len,
|
||||
_arena: std::marker::PhantomData,
|
||||
});
|
||||
}
|
||||
let stride = self.layout.lane_stride();
|
||||
let scratch = persistent.alloc_scratch::<u64>((self.len * stride).div_ceil(8))?;
|
||||
let dst = scratch.as_mut_ptr().cast::<u8>();
|
||||
// SAFETY: the source holds `len` lanes of `layout` at its stride and the
|
||||
// scratch was reserved for exactly that.
|
||||
unsafe { std::ptr::copy_nonoverlapping(frames, dst, self.len * stride) };
|
||||
for lane in 0..self.len {
|
||||
// SAFETY: each lane images a record of this layout.
|
||||
unsafe { promote_record(&self.layout, dst.add(lane * stride), promotion) }?;
|
||||
}
|
||||
Some(GroupItem {
|
||||
layout: self.layout.clone(),
|
||||
storage: ItemStorage::Resident(dst.cast_const()),
|
||||
len: self.len,
|
||||
_arena: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// A typed view over the stored records, checked against the layout's
|
||||
/// element type.
|
||||
pub fn typed_lanes<T: dyn_any::StaticTypeSized>(&self) -> Option<crate::node::List<'_, T>> {
|
||||
@@ -2595,6 +2820,19 @@ impl<'e> Group<'e> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The group promoted into the persistent region, each run shared or copied
|
||||
/// by [`GroupItem::to_persistent`].
|
||||
pub fn to_persistent<'p>(&self, promotion: &Promotion<'p>) -> Option<Group<'p>> {
|
||||
let row = match &self.row {
|
||||
Some(row) => Some(row.to_persistent(promotion)?),
|
||||
None => None,
|
||||
};
|
||||
Some(Group {
|
||||
row,
|
||||
content: self.content.to_persistent(promotion)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Re-parks an owned group's runs into `arena`; `None` reports arena
|
||||
/// exhaustion.
|
||||
pub fn replay<'a>(&self, arena: &'a crate::arena::Arena) -> Option<Group<'a>> {
|
||||
@@ -2803,6 +3041,141 @@ mod tests {
|
||||
assert!(Layout::union(&[&a, &b]).offset_of("length", 0).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_pod_level_promotes_as_a_bare_memcpy() {
|
||||
let transient = crate::arena::Arena::new(4096).unwrap();
|
||||
let persistent = crate::arena::Arena::new(4096).unwrap();
|
||||
let layout = Layout::default().with_writes(1, element_write::<f64>(), &[f64_field("opacity")]);
|
||||
let stride = layout.lane_stride();
|
||||
|
||||
let mut buffer = vec![0u64; (4 * stride).div_ceil(8)];
|
||||
let base = buffer.as_mut_ptr().cast::<u8>();
|
||||
let bounds = (base as usize, buffer.len() * 8);
|
||||
for lane in 0..4usize {
|
||||
unsafe { base.add(lane * stride).cast::<f64>().write(lane as f64) };
|
||||
}
|
||||
let batch = unsafe { crate::node::RecordBatch::new(base.cast_const(), 4, &layout) };
|
||||
|
||||
let promotion = Promotion::new(&transient, bounds, &persistent);
|
||||
let span = unsafe { MaterializedSpan::to_persistent(&batch, &promotion) }.unwrap();
|
||||
|
||||
let slab = (4 * stride).div_ceil(8) * 8;
|
||||
assert_eq!(persistent.occupancy(), slab, "a layout with no parked slot allocates the slab and nothing else");
|
||||
assert_eq!(persistent.retained_heap(), 0, "no payload parked, so nothing is retained");
|
||||
let published = span.batch(&persistent, &layout).unwrap();
|
||||
for lane in 0..4usize {
|
||||
assert_eq!(unsafe { published.get(lane).rec().element::<f64>() }, lane as f64, "lane {lane}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_promoted_element_lands_in_the_persistent_region() {
|
||||
let transient = crate::arena::Arena::new(4096).unwrap();
|
||||
let persistent = crate::arena::Arena::new(4096).unwrap();
|
||||
let layout = Layout::default().with_writes(0, element_write::<String>(), &[]);
|
||||
|
||||
let mut buffer = vec![0u64; layout.frame_bytes().div_ceil(8)];
|
||||
let base = buffer.as_mut_ptr().cast::<u8>();
|
||||
let bounds = (base as usize, buffer.len() * 8);
|
||||
unsafe { write_element(base, String::from("parked in the evaluation"), &transient) }.unwrap();
|
||||
let source = unsafe { base.cast::<*const u8>().read() };
|
||||
assert!(transient.contains(source));
|
||||
|
||||
let batch = unsafe { crate::node::RecordBatch::new(base.cast_const(), 1, &layout) };
|
||||
let promotion = Promotion::new(&transient, bounds, &persistent);
|
||||
let span = unsafe { MaterializedSpan::to_persistent(&batch, &promotion) }.unwrap();
|
||||
|
||||
let published = span.batch(&persistent, &layout).unwrap();
|
||||
let promoted = unsafe { published.get(0).rec().ptr().cast::<*const u8>().read() };
|
||||
assert!(persistent.contains(promoted), "the promote re-parked the payload into the persistent region");
|
||||
assert_ne!(source, promoted, "an evaluation-lived payload is cloned, not shared");
|
||||
assert_eq!(unsafe { borrow_element::<String>(published.get(0).rec()) }, "parked in the evaluation");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_persistent_element_is_shared_by_the_promote() {
|
||||
let transient = crate::arena::Arena::new(4096).unwrap();
|
||||
let persistent = crate::arena::Arena::new(4096).unwrap();
|
||||
let layout = Layout::default().with_writes(0, element_write::<String>(), &[]);
|
||||
|
||||
let mut buffer = vec![0u64; layout.frame_bytes().div_ceil(8)];
|
||||
let base = buffer.as_mut_ptr().cast::<u8>();
|
||||
let bounds = (base as usize, buffer.len() * 8);
|
||||
// The payload an upstream memo already published: the promote must
|
||||
// name it rather than copy it.
|
||||
unsafe { write_element(base, String::from("published upstream"), &persistent) }.unwrap();
|
||||
let upstream = unsafe { base.cast::<*const u8>().read() };
|
||||
let occupied = persistent.occupancy();
|
||||
|
||||
let batch = unsafe { crate::node::RecordBatch::new(base.cast_const(), 1, &layout) };
|
||||
let promotion = Promotion::new(&transient, bounds, &persistent);
|
||||
let span = unsafe { MaterializedSpan::to_persistent(&batch, &promotion) }.unwrap();
|
||||
|
||||
let published = span.batch(&persistent, &layout).unwrap();
|
||||
let promoted = unsafe { published.get(0).rec().ptr().cast::<*const u8>().read() };
|
||||
assert_eq!(upstream, promoted, "an already persistent payload is shared, pointer for pointer");
|
||||
assert_eq!(persistent.occupancy() - occupied, layout.frame_bytes(), "only the lane slab was allocated");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "a promoted element kept a reference outside the persistent region")]
|
||||
fn the_rewalk_catches_a_reference_the_promote_left_behind() {
|
||||
let transient = crate::arena::Arena::new(4096).unwrap();
|
||||
let persistent = crate::arena::Arena::new(4096).unwrap();
|
||||
let layout = Layout::default().with_writes(0, element_write::<String>(), &[]);
|
||||
|
||||
let mut buffer = vec![0u64; layout.frame_bytes().div_ceil(8)];
|
||||
let base = buffer.as_mut_ptr().cast::<u8>();
|
||||
let bounds = (base as usize, buffer.len() * 8);
|
||||
unsafe { write_element(base, String::from("never promoted"), &transient) }.unwrap();
|
||||
|
||||
let promotion = Promotion::new(&transient, bounds, &persistent);
|
||||
unsafe { assert_promoted(&layout, base.cast_const(), &promotion) };
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_interior_run_already_persistent_is_shared() {
|
||||
let transient = crate::arena::Arena::new(4096).unwrap();
|
||||
let persistent = crate::arena::Arena::new(4096).unwrap();
|
||||
let layout = Layout::default().with_writes(1, element_write::<f64>(), &[f64_field("opacity")]);
|
||||
let stride = layout.lane_stride();
|
||||
|
||||
let mut buffer = vec![0u64; (2 * stride).div_ceil(8)];
|
||||
let base = buffer.as_mut_ptr().cast::<u8>();
|
||||
for lane in 0..2usize {
|
||||
unsafe { base.add(lane * stride).cast::<f64>().write(lane as f64) };
|
||||
}
|
||||
let batch = unsafe { crate::node::RecordBatch::new(base.cast_const(), 2, &layout) };
|
||||
|
||||
let published = GroupItem::adopt(batch, &persistent).unwrap();
|
||||
let occupied = persistent.occupancy();
|
||||
let promotion = Promotion::new(&transient, (base as usize, buffer.len() * 8), &persistent);
|
||||
let shared = published.to_persistent(&promotion).unwrap();
|
||||
|
||||
assert_eq!(persistent.occupancy(), occupied, "a run the region already holds costs no allocation");
|
||||
assert!(std::ptr::eq(published.lanes().frames_ptr(), shared.lanes().frames_ptr()), "the interior is shared, not copied");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adopting_within_one_region_shares_the_run() {
|
||||
let arena = crate::arena::Arena::new(4096).unwrap();
|
||||
let layout = Layout::default().with_writes(1, element_write::<f64>(), &[f64_field("opacity")]);
|
||||
let stride = layout.lane_stride();
|
||||
|
||||
let mut buffer = vec![0u64; (2 * stride).div_ceil(8)];
|
||||
let base = buffer.as_mut_ptr().cast::<u8>();
|
||||
for lane in 0..2usize {
|
||||
unsafe { base.add(lane * stride).cast::<f64>().write(lane as f64) };
|
||||
}
|
||||
let batch = unsafe { crate::node::RecordBatch::new(base.cast_const(), 2, &layout) };
|
||||
|
||||
let resident = GroupItem::adopt(batch, &arena).unwrap();
|
||||
let occupied = arena.occupancy();
|
||||
let again = GroupItem::adopt(resident.lanes(), &arena).unwrap();
|
||||
assert_eq!(arena.occupancy(), occupied, "re-adopting a run the arena already holds copies nothing");
|
||||
assert!(std::ptr::eq(resident.lanes().frames_ptr(), again.lanes().frames_ptr()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adopted_lanes_round_trip_through_the_group_item() {
|
||||
let arena = crate::arena::Arena::new(1024).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user