Skip to content

Avoid over allocations & reallocations

Josh Megnauth requested to merge josh/relibc:dont-overallocate-cstrings into master

As we know, vectors amortize the cost of adding new elements by reserving space for multiple elements when full. This is useful but may lead to allocating more memory than necessary.

relibc generally avoids over allocations by reserving the exact amount of space when possible. I fixed a few areas that still over allocated or reallocated unnecessarily by leveraging iterators that are more likely to know sizes.


Here is a condensed, isolated code snippet of the changes made:

use std::{ffi::c_int, io::Write};

fn example_1() {
    let s = "Redox";
    let mut bad = s.as_bytes().to_vec();
    // Allocates and doubles capacity to fit one byte
    bad.push(b'\0');
    assert_ne!(bad.len(), bad.capacity());

    // Smartly allocates once
    let good: Vec<_> = s.bytes().chain(Some(b'\0')).collect();
    assert_eq!(good.len(), good.capacity());
}

fn example_2() {
    let projects = ["Redox", "Orbital", "relibc"];
    let mut v1 = Vec::default();
    for project in projects {
        println!(
            "[before ({})] Capacity: {} Length: {}",
            project,
            v1.capacity(),
            v1.len()
        );
        v1.extend(project.as_bytes());
        v1.push(b'\0');
        println!(
            "[after ({})] Capacity: {} Length: {}",
            project,
            v1.capacity(),
            v1.len()
        );
    }

    // Avoids little allocations and over allocations
    let v2: Vec<u8> = projects
        .iter()
        .flat_map(|project| project.as_bytes().iter().chain(b"\0"))
        .copied()
        .collect();

    assert!(v1.into_iter().eq(v2.into_iter()));
}

fn example_3() {
    let filedes: c_int = 111222;

    // Reallocates at least once
    let mut proc_path_old = b"/proc/self/fd/".to_vec();
    write!(proc_path_old, "{}", filedes).unwrap();
    proc_path_old.push(0);

    // Simpler; doesn't realloc
    let proc_path_exact = format!("/proc/self/fd/{}\0", filedes).into_bytes();

    assert_eq!(proc_path_old.len(), proc_path_exact.len());
    assert!(proc_path_old.into_iter().eq(proc_path_exact.into_iter()));
}

fn main() {
    example_1();
    example_2();
    example_3();
}
Edited by Josh Megnauth

Merge request reports