Para medir el tiempo de ejecución de un bloque de código en Rust, se utiliza std::time::Instant, que proporciona un reloj monótono. A diferencia del reloj del sistema (que puede saltar hacia atrás o adelantarse si se sincroniza con un servidor NTP), el reloj monótono garantiza que el tiempo siempre avance, lo que lo hace indispensable para el benchmarking. El método Instant::now() captura el punto de partida, y elapsed() devuelve un objeto de tipo Duration que representa el tiempo transcurrido.
El error más común al realizar microbenchmarks es el “benchmarking ingenuo”. Los compiladores de Rust, especialmente en modo --release, son extremadamente agresivos optimizando código. Si usted llama a una función pero el compilador detecta que el resultado no se utiliza en ninguna parte, aplicará una técnica llamada Dead Code Elimination (eliminación de código muerto) y omitirá la ejecución de la función por completo para ahorrar ciclos. Esto resultará en mediciones de tiempo de 0 nanosegundos, lo cual es un falso positivo. Para evitar esto, se utiliza std::hint::black_box, una función que actúa como una barrera para el optimizador, indicándole que el valor pasado debe ser tratado como algo cuyo valor no puede conocerse en tiempo de compilación, forzando así la ejecución real del código.
Para que una medición sea válida, se deben seguir criterios de microbenchmarking: realizar un “warm-up” (calentamiento) del CPU para que este alcance su frecuencia máxima, ejecutar el código durante múltiples iteraciones para promediar la varianza y evitar medir operaciones costosas de asignación de memoria (allocations) si el objetivo es medir la lógica algorítmica pura.
use std::time::{Instant, Duration};
use std::hint::black_box;
/// Función de ejemplo que realiza un cálculo intensivo
fn calcular_suma_acumulativa(n: u64) -> u64 {
(1..=n).fold(0, |acc, x| acc + x)
}
fn main() {
let limite = 10_000_000;
// --- ESCENARIO 1: Benchmarking ingenuo (Potencialmente erróneo) ---
// El compilador podría notar que 'resultado_falso' no se usa y eliminar la llamada.
let inicio_falso = Instant::now();
let _resultado_falso = calcular_suma_acumulativa(limite);
let duracion_falsa = inicio_falso.elapsed();
println!("Tiempo ingenuo: {:?}", duracion_falsa);
// --- ESCENARIO 2: Benchmarking correcto con black_box ---
// Usamos black_box para asegurar que el cálculo se ejecute realmente.
let inicio_real = Instant::now();
// Aplicamos black_box tanto a la entrada como al resultado para evitar optimizaciones
let resultado_real = black_box(calcular_suma_acumulativa(black_box(limite)));
let duracion_real: Duration = inicio_real.elapsed();
// Evitamos que el compilador elimine el resultado final
black_box(resultado_real);
println!("--- Resultados Correctos ---");
println!("Segundos: {:.6} s", duracion_real.as_secs_f64());
println!("Milisegundos: {} ms", duracion_real.as_millis());
println!("Microsegundos: {} µs", duracion_real.as_micros());
println!("Nanosegundos: {} ns", duracion_real.as_nanos());
}
Explicación del Código
calcular_suma_acumulativa(n: u64) -> u64: Es la función objetivo que queremos medir. Utiliza unfoldpara sumar números.inicio_falso: De tipoInstant, captura el tiempo antes de la ejecución potencialmente optimizada._resultado_falso: Al estar precedido por un guion bajo y no usarse, el compilador tiene vía libre para eliminar la llamada acalcular_suma_acumulativaen optimización de alto nivel.inicio_real: Un nuevoInstantpara iniciar una medición limpia.black_box(limite): Se aplica al argumento de la función para evitar que el compilador pre-calcule el resultado basándose en la constantelimite.black_box(calcular_suma_acumulativa(...)): Envuelve el resultado de la función para evitar que el compilador elimine la llamada al ver que su valor no se usa en unprintln!o similar.duracion_real: Duration: El objeto resultante deelapsed()que contiene la magnitud del tiempo transcurrido.as_secs_f64(): Devuelve el tiempo en segundos como un punto flotante (f64), ideal para precisiones altas en lecturas de segundos.as_millis(),as_micros(),as_nanos(): Métodos deDurationque convierten el tiempo a unidades enteras más pequeñas.
N° 112