es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué Vec::with_capacity es más lento que Vec::new para longitudes finales pequeñas?

Considere este código.

type Int = i32;
const MAX_NUMBER: Int = 1_000_000;

fn main() {
    let result1 = with_new();
    let result2 = with_capacity();
    assert_eq!(result1, result2)
}

fn with_new() -> Vec<int> {
    let mut result = Vec::new();
    for i in 0..MAX_NUMBER {
        result.push(i);
    }
    result
}

fn with_capacity() -> Vec<int> {
    let mut result = Vec::with_capacity(MAX_NUMBER as usize);
    for i in 0..MAX_NUMBER {
        result.push(i);
    }
    result
}

Ambas funciones producen la misma salida. Una utiliza Vec::new, la otra utiliza Vec::with_capacity. Para valores pequeños de MAX_NUMBER (como en el ejemplo), with_capacity es más lento que new. Solo para longitudes finales más grandes (por ejemplo, 100 millones) la versión que utiliza with_capacity es tan rápida como la que utiliza new.

Flamegraph para 1 millón de elementos
1 millón

Flamegraph para 100 millones de elementos
100 millones

Mi comprensión es que with_capacity siempre debería ser más rápido si se conoce la longitud final, porque los datos en el montón se asignan una vez, lo que debería resultar en un solo bloque. En contraste, la versión con new hace crecer el vector MAX_NUMBER veces, lo que resulta en más asignaciones.

¿Qué me estoy perdiendo?

Editar

La primera sección se compiló con el perfil debug. Si utilizo el perfil release con la siguiente configuración en Cargo.toml

[package]
name = "vec_test"
version = "0.1.0"
edition = "2021"

[profile.release]
opt-level = 3
debug = 2

Todavía obtengo el siguiente resultado para una longitud de 10 millones.

10 millones, perfil release

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    La diferencia de rendimiento entre Vec::new y Vec::with_capacity puede depender de varios factores, incluyendo el hardware y el sistema operativo específicos que se estén utilizando, el tamaño del vector y el nivel de optimización del compilador Rust.

    En general, Vec::with_capacity debería ser más rápido que Vec::new cuando la longitud final del vector se conoce de antemano, ya que asigna el espacio necesario para el vector de antemano, evitando la necesidad de realocar memoria a medida que el vector crece. Por otro lado, Vec::new comienza con una capacidad inicial pequeña y luego realoca según sea necesario, lo que puede resultar en más asignaciones de memoria y copias.

    Sin embargo, en el ejemplo proporcionado, la diferencia de rendimiento entre los dos métodos no es consistente con esta expectativa. Esto puede ser debido al hardware y al sistema operativo específicos que se están utilizando, o a la configuración de optimización del compilador Rust.

    Vale la pena señalar que los flamegraphs mostrados en el ejemplo se generan utilizando el perfil de Rust “debug”. Cuando se utiliza el perfil “release” con un nivel de optimización establecido en 3 (opt-level = 3), la diferencia de rendimiento entre Vec::new y Vec::with_capacity puede ser menos pronunciada o incluso revertirse.

    En resumen, aunque en general Vec::with_capacity debería ser más rápido que Vec::new, la diferencia de rendimiento puede depender de varios factores y debe ser probada caso por caso, teniendo en cuenta el hardware y la configuración de optimización del compilador.

Comments are closed.