timeit en Python: mide el rendimiento correctamente

Publicado el: 11/07/2026
Tempo de leitura: 6 minutos
Medição de tempo de execução de código Python com timeit

El módulo timeit de Python permite medir pequeños fragmentos de código de forma más confiable que una resta manual con el reloj del sistema. Ejecuta la operación varias veces, reduce interferencias comunes y facilita comparar alternativas. Es especialmente útil para responder preguntas concretas: ¿una comprensión es más rápida que un bucle?, ¿conviene usar un set para pertenencia?, ¿una función de la biblioteca estándar supera una implementación manual?

Antes de optimizar, identifica qué parte del programa realmente consume tiempo. La guía de cProfile en Python ayuda a localizar cuellos de botella en aplicaciones completas. timeit sirve después para analizar una operación pequeña y aislada.

Por qué no basta con time.time()

Una medición manual puede verse afectada por la resolución del reloj, procesos del sistema, calentamiento de cachés y errores en la preparación del test.

import time

inicio = time.time()
resultado = sum(range(1000))
fin = time.time()
print(fin - inicio)

Este ejemplo ejecuta la operación una sola vez. Para fragmentos rápidos, el ruido puede ser mayor que la duración real. timeit repite el código y utiliza un temporizador apropiado para medir intervalos cortos.

Primera medición con timeit()

import timeit

duracion = timeit.timeit("sum(range(1000))", number=100_000)
print(duracion)

El resultado representa el tiempo total de todas las ejecuciones. Para obtener el promedio, divide por number:

promedio = duracion / 100_000
print(f"{promedio:.10f} segundos por ejecución")

No interpretes diferencias diminutas sin repetir la prueba. El sistema operativo, la CPU y la versión de Python influyen en el resultado.

Usar una función en lugar de una cadena

La forma más clara suele ser pasar un objeto invocable:

import timeit


def calcular():
    return sum(range(1000))

resultado = timeit.timeit(calcular, number=100_000)
print(resultado)

Así evitas problemas de comillas y mantienes el código bajo prueba dentro del editor, con análisis estático y formato normal.

Comparar dos implementaciones

Supongamos que quieres construir cuadrados mediante un bucle o una comprensión:

def con_bucle():
    resultado = []
    for numero in range(1000):
        resultado.append(numero * numero)
    return resultado


def con_comprension():
    return [numero * numero for numero in range(1000)]
import timeit

veces = 10_000
tiempo_bucle = timeit.timeit(con_bucle, number=veces)
tiempo_comprension = timeit.timeit(con_comprension, number=veces)

print(tiempo_bucle)
print(tiempo_comprension)

La guía de comprensiones de listas explica sintaxis, filtros y criterios de legibilidad. Una implementación ligeramente más rápida no es mejor si vuelve el código difícil de mantener.

Usar repeat() para reducir ruido

repeat() ejecuta varios grupos de mediciones:

resultados = timeit.repeat(
    stmt=con_comprension,
    repeat=7,
    number=10_000,
)

print(resultados)
print(f"Mejor resultado: {min(resultados):.6f}")

La documentación suele recomendar observar el valor mínimo porque las interrupciones externas pueden hacer una ejecución más lenta, pero rara vez la vuelven artificialmente más rápida. Aun así, informa también la dispersión cuando la estabilidad importe.

Elegir un number adecuado

Si number es demasiado pequeño, el ruido domina. Si es excesivo, la prueba tarda demasiado, calienta la CPU y puede cambiar su frecuencia. El objetivo es obtener una duración suficiente para medir, sin convertir el benchmark en una carga prolongada.

La línea de comandos puede elegir automáticamente un número razonable:

python -m timeit "sum(range(1000))"

Utilizar setup con cadenas

Cuando empleas una sentencia como texto, setup prepara nombres fuera de la parte cronometrada:

import timeit

resultado = timeit.timeit(
    stmt="objetivo in datos",
    setup="datos = list(range(10000)); objetivo = 9999",
    number=10_000,
)
print(resultado)

No incluyas la creación de los datos dentro de stmt si solo quieres medir la búsqueda. En cambio, inclúyela cuando la construcción forme parte real de la operación que deseas evaluar.

Comparar list y set para pertenencia

datos_lista = list(range(10000))
datos_set = set(datos_lista)
objetivo = 9999

lista_tiempo = timeit.timeit(
    lambda: objetivo in datos_lista,
    number=100_000,
)
set_tiempo = timeit.timeit(
    lambda: objetivo in datos_set,
    number=100_000,
)

print(lista_tiempo, set_tiempo)

El operador in en Python tiene costes diferentes según la estructura. Sin embargo, construir el set también cuesta tiempo y memoria. Si solo realizas una búsqueda, la conversión puede no compensar.

No incluir print() en el benchmark

La salida de terminal es lenta y variable. Este benchmark mediría principalmente el sistema de entrada y salida:

# Evitar para comparar cálculo puro
timeit.timeit(lambda: print(sum(range(1000))), number=100)

Devuelve el resultado o consúmelo de una forma mínima. También evita acceso a red, disco y APIs en microbenchmarks; su latencia es demasiado variable para una comparación local sencilla.

Comprobar que ambas funciones hacen lo mismo

Antes de comparar velocidad, verifica resultados:

assert con_bucle() == con_comprension()

Una función que omite validaciones puede parecer más rápida porque realiza menos trabajo. Define entradas, salidas y errores equivalentes.

Medir generadores correctamente

Crear un generador es barato porque su cuerpo todavía no se ejecuta:

def generar():
    return (numero * numero for numero in range(1000))

Medir solo generar() no evalúa el procesamiento. Debes consumirlo:

tiempo = timeit.timeit(lambda: list(generar()), number=10_000)

La guía de yield y generadores explica evaluación perezosa, memoria y pipelines.

Evitar optimizaciones eliminadas o resultados no usados

CPython no elimina agresivamente cálculos como algunos compiladores, pero debes diseñar el test para representar el uso real. Guarda o devuelve el resultado y evita medir código simplificado que nunca aparece en la aplicación.

Comparar varias versiones de Python

Los resultados pueden cambiar entre versiones, sistemas y arquitecturas. Registra al menos:

import platform
import sys

print(sys.version)
print(platform.platform())
print(platform.processor())

Un benchmark publicado sin entorno, datos y número de repeticiones es difícil de reproducir.

Calentamiento y cachés

La primera ejecución puede cargar módulos, asignar memoria o llenar cachés. timeit reduce algunos problemas al repetir, pero no sustituye un diseño cuidadoso. Si comparas funciones que usan caché, decide si quieres medir la primera llamada o llamadas posteriores.

El artículo sobre lru_cache en Python muestra cómo la memoización cambia completamente el coste entre una llamada inicial y una repetida.

GC y comportamiento de timeit

timeit desactiva temporalmente la recolección de basura por defecto durante la medición. Esto hace comparables muchos fragmentos, pero puede ocultar un coste real si el código crea muchos objetos cíclicos. Puedes activarla explícitamente en el setup cuando forme parte del escenario:

timeit.timeit(
    stmt="crear_objetos()",
    setup="gc.enable()",
    globals={"crear_objetos": crear_objetos, "gc": __import__("gc")},
    number=1000,
)

Usar Timer y globals

from timeit import Timer

medidor = Timer(
    stmt="funcion(datos)",
    globals={"funcion": procesar, "datos": datos},
)
print(medidor.repeat(repeat=5, number=1000))

globals evita imports artificiales dentro de cadenas y facilita trabajar con objetos del programa.

Microbenchmark frente a rendimiento real

Una mejora local puede no afectar al tiempo total de la aplicación. Si una función representa el 1% del proceso, duplicar su velocidad apenas cambia el resultado final. Primero perfila el programa y después optimiza el cuello de botella.

La guía sobre por qué un script Python es lento presenta algoritmos, estructuras, I/O, NumPy y concurrencia como causas más importantes que pequeños cambios sintácticos.

Presentar resultados

No digas únicamente “A es más rápido”. Indica datos, código, versión, hardware, repeticiones y unidad por operación. Calcula una proporción:

mejor_a = min(timeit.repeat(funcion_a, repeat=7, number=10000))
mejor_b = min(timeit.repeat(funcion_b, repeat=7, number=10000))

print(f"A/B: {mejor_a / mejor_b:.2f}x")

Una diferencia pequeña puede desaparecer con otra entrada. Prueba tamaños representativos y casos normales, mejores y peores.

Errores frecuentes

Los errores más comunes son medir una sola vez, incluir preparación distinta, comparar funciones que producen resultados diferentes, usar entradas diminutas, medir print o red, ignorar calentamiento y afirmar conclusiones universales. También es frecuente optimizar antes de perfilar.

Fuentes oficiales

La documentación oficial de timeit explica timeit(), repeat(), Timer, línea de comandos y recolección de basura. La documentación oficial de los perfiles de Python aclara cuándo usar cProfile para programas completos.

Conclusión

timeit es la herramienta adecuada para microbenchmarks reproducibles dentro de Python. Separa preparación y operación, repite varias veces, consume generadores, verifica resultados y registra el entorno. Usa el benchmark para responder una pregunta concreta después de encontrar un cuello de botella real, no para reemplazar la claridad con optimizaciones prematuras.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Enums en Python: evita valores mágicos y errores

    Aprende enums en Python con Enum, IntEnum, StrEnum, auto, unique, Flag, validación, JSON, bases de datos y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Uso de dataclasses para simplificar classes em Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Dataclasses en Python: clases de datos más limpias

    Aprende dataclasses en Python: campos, valores predeterminados, field, frozen, order, __post_init__, herencia, asdict y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Criação de hashes seguros para senhas usando Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Hash de contraseñas con bcrypt en Python

    Aprende bcrypt en Python para almacenar contraseñas: salt, coste, hash, verificación, rehash, límites, errores y recomendaciones de seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Problemas de travamento com threading em scripts Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Threading en Python: evita que tus scripts se bloqueen

    Aprende threading en Python: hilos, tareas de entrada y salida, join, locks, colas, ThreadPoolExecutor, errores y límites del GIL.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Dicas para otimizar scripts Python lentos
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Por qué tu script Python es lento y cómo mejorarlo

    Descubre por qué un script Python es lento y cómo mejorarlo con cProfile, timeit, estructuras correctas, generadores y mejores algoritmos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Leitura de variáveis de ambiente em projetos Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Variables de entorno en Python: guía segura

    Aprende a usar variables de entorno en Python, archivos .env, validación, conversión de tipos, secretos, pruebas y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026