Avoid calling destructors for individual Vec elements

I've ran into an interesting performance problem.

enum MaybeNeedsDrop {
  Single(f32),
  Collection(Vec<f32>)
}

// variant one: 
let mut v = Vec::new();
for _ in 0..1000000 {
   v.push(MaybeNeedsDrop::Single(1.0));
}

drop(v);   /// aaaargh, drop_in_place killing performance, can I not call individual destructors?


// variant two: 
let mut v = Vec::new();
for _ in 0..1000000 {
   v.push(MaybeNeedsDrop::Collection(vec![1.0, 2.0]));
}
drop(v);  // but in this case I want to drop all the nested vectors

How do I conditionally, at runtime, prevent calling destructors of elements
when I know that all items in the vector are of variant that doesn't need calling destructors?

Would that work:

fn drop_vec_but_no_elements<T>(vec: Vec<T>) {
    unsafe { std::mem::transmute::<Vec<T>, Vec<ManuallyDrop<T>>>(vec); }
}
fn drop_vec_forget_elements<T>(vec: Vec<T>) {
    let orig = ManuallyDrop::new(vec);
    let ptr = orig.as_mut_ptr();
    let len = orig.len();
    let cap = orig.capacity();
    // SAFETY: ManuallyDrop<T> and T have the same layout,
    // and we're only dropping the allocation once
    let reconstructed: Vec<ManuallyDrop<T>> = unsafe {
        Vec::from_raw_parts(ptr, len, cap)
    };
    drop(reconstructed);
}
1 Like

Transmuting the Vec directly isn't guaranteed to work, because even if T and U have the same layout, Vec<T> and Vec<U> are not guaranteed to have the same layout. (e.g., they might have the capacity and length stored in different orders.)

2 Likes
fn drop_vec_forget_elements<T>(mut vec: Vec<T>) {
    // SAFETY:
    // - 0 is always <= capacity
    // - every element in range is initialized (there are none)
    unsafe {
        vec.set_len(0);
    }
}
5 Likes

Here's a safe alternative:

v.into_iter().for_each(std::mem::forget);
4 Likes

Are you sure that iteration is going to be optimized out?
@quinedot I like the set_len(0) solution, just tried it and works like a charm!

In debug mode it might not, in release mode almost surely. Compiler Explorer

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.