σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Rutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Python›Módulo 10 · Ingeniería Python de alto rendimiento›Tema 10.4·~50 minAvanzado

Profiling de código Python

Posición en la rutaMódulo 10 de 11 · Tema 4 de 13
10.4 Profiling de código Python
—Puntuación del lab— / 100XP total—Racha—

Medir primero, optimizar después

Un backtest de cuatro horas o una función de valoración llamada un millón de veces por segundo hacen que todos quieran acelerar, y casi todos se equivocan sobre adónde va el tiempo. El bucle que parece pesado puede no costar nada; una línea de aspecto inocente puede ser toda la ejecución. El perfilado consiste en medir dónde gasta su tiempo un programa antes de cambiar nada. La regla es simple: no optimices una línea que no hayas medido y, tras un cambio, mide de nuevo para ver si ayudó.

# The simplest measurement: read a clock before and after
import time

def slow_sum(n):
    total = 0
    for i in range(n):
        total += i * i
    return total

start = time.perf_counter()
result = slow_sum(200_000)
elapsed = time.perf_counter() - start
print(result)                                        # 2666646666700000
print(elapsed > 0)                                   # True  a real duration; the exact value changes every run

Ruido, repeticiones y mínimo

Una sola medición no es fiable: el sistema operativo, otros programas, la caché de la CPU y el arranque de Python añaden ruido, y solo añaden tiempo. Por eso se cronometra una función varias veces y se toma el mínimo, la ejecución menos perturbada; timeit hace justo eso. Usa time.perf_counter() (un reloj monótono de alta resolución), no time.time(). Mide con tamaños de entrada realistas, porque una función rápida con diez filas puede ser lenta con diez millones, y calienta antes para que los costes únicos (imports, cachés) no se carguen al código medido.

# Repeat and take the minimum: the least disturbed run
import time

def work():
    return sum(i * i for i in range(50_000))

def best_of(func, repeats):
    best = None
    for _ in range(repeats):
        start = time.perf_counter()
        func()
        elapsed = time.perf_counter() - start
        if best is None or elapsed < best:
            best = elapsed
    return best

runs = [best_of(work, 1) for _ in range(5)]
print(best_of(work, 5) <= max(runs))                 # True  the minimum is never above the worst single run
print(min(runs) > 0)                                 # True

Adónde va el tiempo: perfiladores y la ley de Amdahl

Un cronómetro dice cuánto, un perfilador dice dónde. cProfile, de la biblioteca estándar, registra cada llamada a función e informa, para cada una, cuántas llamadas recibió, el tiempo gastado en la propia función y el tiempo acumulado incluyendo lo que llamó; python -m cProfile -s cumtime script.py muestra primero lo peor. La parte de cada función es entonces su tiempo sobre el total. La ganancia de arreglar una está limitada por la ley de Amdahl: acelerar una fracción f de la ejecución en un factor s da en total 1 / ((1 - f) + f / s). Hacer 60 % de una ejecución diez veces más rápido da solo 2,2 veces en total, e incluso una mitad infinitamente rápida limita la ganancia a 2. Ataca primero el mayor punto caliente.

# Shares of the total and the payoff of fixing the biggest one (the timings are made-up numbers)
timings = {"load_prices": 6.0, "price_options": 3.0, "write_report": 1.0}
total = sum(timings.values())
for name, seconds in sorted(timings.items(), key=lambda item: -item[1]):
    print(f"{name:14s} {seconds / total:5.1%}")      # load_prices 60.0%, price_options 30.0%, write_report 10.0%

def amdahl_gain(fraction, speedup):
    return 1 / ((1 - fraction) + fraction / speedup)

print(round(amdahl_gain(0.6, 10), 2))                # 2.17  ten times faster on 60% of the run
print(round(amdahl_gain(0.1, 1000), 2))              # 1.11  a huge win on the smallest part barely helps
# cProfile records every call; the function that sleeps shows up as the hotspot
import cProfile
import time

def slow_part():
    time.sleep(0.02)

def fast_part():
    return sum(range(10))

def run_all():
    slow_part()
    for _ in range(100):
        fast_part()

profiler = cProfile.Profile()
profiler.runcall(run_all)
entries = {str(entry.code): entry for entry in profiler.getstats()}
slow = next(entry for name, entry in entries.items() if "slow_part" in name)
fast = next(entry for name, entry in entries.items() if "fast_part" in name)
print(fast.callcount, slow.callcount)                # 100 1   calls are counted exactly
print(slow.totaltime > fast.totaltime)               # True    one slow call outweighs 100 fast ones
Perfila en condiciones que importenUn perfilador ralentiza el programa que observa y distorsiona las funciones muy pequeñas: confía en el panorama general (qué función domina) más que en los segundos exactos y confirma la mejora con un cronómetro simple. Perfila con tamaños de datos reales, no de juguete. Comprueba que el resultado sigue siendo correcto tras cada cambio, pues una respuesta equivocada más rápida es la más cara. Y para cuando el código sea lo bastante rápido: el tiempo dedicado a pulir un punto caliente que ya no importa es tiempo que no se dedica a la estrategia.
  • Mide antes de cambiar nada; cronometra con perf_counter, repite y quédate con el mínimo, con tamaños de entrada realistas.
  • Un perfilador (cProfile) muestra adónde va el tiempo: llamadas, tiempo propio y acumulado por función; cada parte es su tiempo sobre el total.
  • Ley de Amdahl: acelerar una fracción f en un factor s da 1 / ((1 - f) + f / s); arregla primero el mayor punto caliente y vuelve a medir tras cada cambio.

A continuación: optimizar bucles críticos, los cambios que aceleran el código señalado por el perfilador.

Lab: qué se califica

  • Cronometrar una llamada con time_call25 pts
  • Tomar lo mejor de varias ejecuciones con best_of25 pts
  • Hallar adónde va el tiempo con hotspots25 pts
  • Predecir la ganancia con amdahl_gain25 pts
Explorador · lab-120-1
▾ lab-120-1
…
bash · en el navegador
learner@quantcraft:~/lab-120-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-120-1 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas

Inicia sesión para ejecutar tu código.

Todas las clases se pueden leer gratis. Crea una cuenta o inicia sesión para ejecutar código, lanzar las pruebas y enviar este lab.

  • IDE en el navegador con terminal bash — sin instalar nada
  • Corrección instantánea, XP y tu lugar en la ruta
Crear una cuentaIniciar sesión
O
Continuar con GitHub