typst / ecow

Compact, clone-on-write vector and string.
Apache License 2.0
208 stars 16 forks source link

Regular Vec is faster #42

Closed ccleve closed 1 month ago

ccleve commented 1 month ago

I've got some test code where a regular Vec is significantly faster than an EcoVec. What am I doing wrong?

(The functions below are identical, except one uses Vec and the other EcoVec.)

const RUN_COUNT: usize = 100_000_000;

fn test_ecovec() {
    let mut rows = Vec::with_capacity(RUN_COUNT);
    for i in 0..RUN_COUNT {
        let mut row = EcoVec::with_capacity(10);
        for i in 0u8..10 {
            row.push(i);
        }
        rows.push(row);
    }
    println!("{:?}", rows.len()); // prevent optimizing anything out
}

fn test_vec() { let mut rows = Vec::with_capacity(RUN_COUNT); for i in 0..RUN_COUNT { let mut row = Vec::with_capacity(10); for i in 0u8..10 { row.push(i); } rows.push(row); } println!("{:?}", rows.len()); // prevent optimizing anything out }

Running these both in --release mode, the execution time on my Mac M1 is 10 secs for the regular Vec, and 15 seconds for the EcoVec.

I would think that the EcoVec would be faster because at only 10 u8 elements, it should not need to do a heap allocation.

laurmaedje commented 1 month ago

EcoVec will always do a heap allocation. Only EcoString has a small-size optimization. The point of EcoVec is not having inline capacity, but rather copy-on-write, so cheap clones.

EcoVec has overhead on push because it needs to check whether the vector was cloned. This involves atomic operations.

ccleve commented 1 month ago

I switched to EcoString, and now the code is 14x faster than a Vec. Thank you.