σμ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 AcademyHojas de rutaRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Hojas de rutaRutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
σμ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 AcademyHojas de rutaRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Hojas de rutaRutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Cache locality›Módulo 1 · Las cachés desde cero›Tema 1.1·~40 minPrincipiante

La jerarquía de memoria y las cifras de latencia

Posición en la rutaMódulo 1 de 3 · Tema 1 de 4
1.1 La jerarquía de memoria y las cifras de latencia
—Puntuación del lab— / 100XP total—Racha—

6 min de lectura4 ejemplos de código

  1. 01Por qué la memoria, y no la aritmética, marca la velocidad
  2. 02Cuánto cuesta un acceso
  3. 03El tiempo de acceso medio
  4. 04Quién responde y quién cuesta
  5. 05Leer una tabla real
  6. 06Lo que va a construir

Un procesador suma dos números en una fracción de nanosegundo, pero traer un número que no está en ninguna caché puede tardar 80 ns. Esta lección convierte esas cifras de latencia en una sola, el coste medio de un acceso a memoria, y muestra qué nivel de memoria se come su tiempo.

01

Por qué la memoria, y no la aritmética, marca la velocidad

Un programa de trading pasa la mayor parte de su vida esperando datos: un precio, una orden, una fila de una tabla. La aritmética sobre esos datos es casi gratis, pero los datos primero deben viajar de la memoria al procesador, y cuanto más lejos están, más tarda. Piense en un escritorio, una estantería, la planta de una biblioteca y un almacén al otro lado de la ciudad. Los pocos papeles del escritorio son instantáneos, la estantería tarda segundos, la biblioteca un minuto y el almacén una tarde. Los ordenadores copian los datos que más usan en memorias pequeñas y rápidas junto al procesador, llamadas cachés, para que la mayoría de las peticiones se atiendan en el escritorio.

DefiniciónLatencia

El tiempo que pasa entre pedir un valor y tenerlo, medido aquí en nanosegundos (milmillonésimas de segundo).

Un núcleo de servidor típico tiene tres cachés delante de la memoria principal. Las llamamos L1, L2 y L3, y a la memoria principal RAM. Una petición atendida por una caché es un acierto; una petición que debe pasar al siguiente nivel es un fallo. La tabla siguiente pone las cuatro latencias una junto a otra.

main.pyPython
LATENCY_NS = {"L1": 1.0, "L2": 4.0, "L3": 15.0, "RAM": 80.0}for level, ns in LATENCY_NS.items():    print(level, ns, "ns", ns / LATENCY_NS["L1"], "x L1")
L1 1.0 ns 1.0 x L1
L2 4.0 ns 4.0 x L1
L3 15.0 ns 15.0 x L1
RAM 80.0 ns 80.0 x L1

Cada nivel es varias veces más lento que el de arriba, y la RAM es 80 veces más lenta que L1. Toda esta habilidad consiste en lograr que la mayoría de los accesos se atiendan cerca de la cima.

02

Cuánto cuesta un acceso

Un acceso empieza en L1. Si el valor está allí, el acceso cuesta la latencia de L1 y termina. Si no, el procesador pregunta a L2, luego a L3 y luego a la RAM, que siempre tiene el valor. Cada nivel consultado cuesta tiempo, acierte o no, así que un acceso encontrado en L3 ha pagado L1, L2 y L3.

  1. 1
    Preguntar a L1Pague 1 ns. Un acierto termina el acceso.
  2. 2
    Si falla, preguntar a L2Pague 4 ns más. Un acierto termina el acceso.
  3. 3
    Si falla, preguntar a L3Pague 15 ns más. Un acierto termina el acceso.
  4. 4
    Si falla, ir a la RAMPague 80 ns más. La RAM siempre tiene el valor.
main.pyPython
LATENCY_NS = {"L1": 1.0, "L2": 4.0, "L3": 15.0, "RAM": 80.0}cost_found_in_l3 = LATENCY_NS["L1"] + LATENCY_NS["L2"] + LATENCY_NS["L3"]print(cost_found_in_l3, "ns")
20.0 ns

Un acceso atendido por L3 cuesta 20 ns, no 15: el 1 ns y los 4 ns gastados en los dos fallos anteriores forman parte de la cuenta.

Las tasas de acierto son locales

Cada tasa de acierto cuenta solo los accesos que llegan a ese nivel. Una tasa de acierto de L2 de 0,8 significa el 80 % de los accesos que fallaron en L1, no el 80 % de todos. Confundir ambas es el error más común de este tema.

03

El tiempo de acceso medio

Ningún acceso es típico, así que pedimos el promedio de muchos accesos. Pondere el coste de cada camino por su frecuencia. Todo acceso paga la latencia de L1; solo la parte que falla en L1 paga L2; solo la que falla en ambas paga L3; y así sucesivamente. Escrito como una expresión, con paréntesis que se anidan como los niveles:

AMAT = t₁ + (1 − h₁) · (t₂ + (1 − h₂) · (t₃ + (1 − h₃) · t_RAM))(1)t₁, t₂, t₃ y t_RAM son las latencias; h₁, h₂ y h₃ son las tasas de acierto locales de L1, L2 y L3

Este es el tiempo de acceso medio a memoria. Lea los paréntesis de dentro hacia fuera: el más interno es lo que cuesta un fallo en L3, el siguiente suma la visita a L3, y así hasta la visita a L1 que paga todo acceso.

main.pyPython
LATENCY_NS = {"L1": 1.0, "L2": 4.0, "L3": 15.0, "RAM": 80.0}h1, h2, h3 = 0.9, 0.8, 0.7t = LATENCY_NSamat = t["L1"] + (1 - h1) * (t["L2"] + (1 - h2) * (t["L3"] + (1 - h3) * t["RAM"]))1print(round(amat, 2), "ns")
2.18 ns
  1. 11 - h es la parte de los accesos que fallan, así que cada paréntesis se multiplica por la parte que llega hasta allí.

Con el 90 % de los accesos acertando en L1, el promedio sigue siendo 2,18 ns, más del doble de la latencia de L1. Los pocos accesos que bajan hasta el fondo son tan caros que dominan el promedio. Por eso una tasa de acierto que parece excelente puede esconder un programa lento.

¿Ciclos o nanosegundos?

Los fabricantes dan la latencia en ciclos del procesador: 4 ciclos a 4 GHz son 1 ns. Usamos nanosegundos para comparar los niveles de un vistazo. Estas cifras son típicas, no exactas: los chips reales difieren en un factor de dos en un sentido u otro.

04

Quién responde y quién cuesta

Dos preguntas parecen iguales pero tienen respuestas distintas. ¿Qué nivel atiende los accesos? ¿Y qué nivel se lleva el tiempo? La primera cuenta accesos, la segunda cuenta espera. A un nivel llega una parte de los accesos y cada visita cuesta su latencia, así que un nivel al que se llega rara vez pero que es lento puede dominar la espera.

main.pyPython
h1, h2, h3 = 0.9, 0.8, 0.7served = [h1, (1 - h1) * h2, (1 - h1) * (1 - h2) * h3, (1 - h1) * (1 - h2) * (1 - h3)]reach = [1.0, 1 - h1, (1 - h1) * (1 - h2), (1 - h1) * (1 - h2) * (1 - h3)]1cost = [r * ns for r, ns in zip(reach, [1.0, 4.0, 15.0, 80.0])]2print([round(x, 3) for x in served])print([round(c / sum(cost), 3) for c in cost])
[0.9, 0.08, 0.014, 0.006]
[0.459, 0.183, 0.138, 0.22]
  1. 1reach es la parte de accesos que visitan cada nivel: todos visitan L1.
  2. 2sum(cost) es el tiempo de acceso medio: los mismos 2,18 ns de antes.

La RAM atiende solo el 0,6 % de los accesos pero supone el 22 % del tiempo de espera, mientras que L1 atiende el 90 % y se lleva el 46 % del tiempo. Este es el hecho clave detrás de toda optimización de esta habilidad: no cuente accesos, cuente adónde va el tiempo.

05

Leer una tabla real

¿Cuánto importa esto en una tabla real? La tabla de barras diarias de SPY del laboratorio tiene 5405 filas y cinco columnas (apertura, máximo, mínimo, cierre, volumen), es decir, 27 025 valores. Lea cada valor una vez en tres condiciones: cachés calientes donde casi todo acierta, cachés templadas y cachés frías donde la mitad de los accesos fallan en cada nivel.

Qué dicen los datos realesBarras diarias de SPY, 2005 a 2026
Valores en la tabla27,0255405 filas × 5 columnas
Frío frente a caliente15.9×más lento para la misma tabla
Acceso medio, cachés calientes1.05nstasas de acierto 0,99 · 0,95 · 0,90
Acceso medio, cachés frías16.75nsla RAM es el 60 % de esa espera
Tiempo para leer la tabla una vez, en microsegundos
Cachés calientes28
Cachés templadas59
Cachés frías453
06

Lo que va a construir

El laboratorio convierte esta lección en seis funciones pequeñas, cada una construida sobre las anteriores, y termina valorando la lectura de la tabla real de SPY. Aquí están con la pregunta que responde cada una.

  • Función · preguntaSe apoya en
  • average_access_time(hit_rates)¿Cuánto cuesta un acceso en promedio?La tabla de latencias
  • slowdown(hit_rates)¿Cuántas veces más lento que acertar siempre en L1?average_access_time
  • levels_served(hit_rates)¿Qué nivel atiende los accesos?Tasas de acierto locales
  • time_shares(hit_rates)¿Qué nivel se lleva el tiempo?average_access_time
  • scan_time_us(n_accesses, hit_rates)¿Cuánto tardan n accesos?average_access_time
  • study(bars)¿Cuánto cuesta leer toda la tabla de SPY?scan_time_ustime_shares

Ideas clave

  • Cuente adónde va el tiempo. La parte de accesos que atiende un nivel y la parte del tiempo que cuesta son números distintos, y un nivel raro pero lento puede dominar.
  • Las tasas de acierto son locales. La tasa de cada nivel cuenta solo los accesos que llegan a él, así que la parte que atiende es el producto de los fallos de arriba y su propia tasa.
  • El promedio es un anidamiento de paréntesis. AMAT = t₁ + fallo₁ × (t₂ + fallo₂ × (…)) es el número que cada tema posterior intenta reducir.
Comprobación rápida
Una tasa de acierto de L1 de 0,95. De los accesos que llegan a L2, la mitad acierta. ¿Qué parte de todos los accesos atiende L2?
Elige una respuesta.

Siguiente tema: esta lección tomó las tasas de acierto como dadas. En Líneas de caché y localidad espacial construye la caché que las produce y descubre por qué leer un valor junto a otro recién leído es casi gratis.

Lab: qué se califica

6 tareas · 100 pts
  • Tiempo de acceso medio20 pts
  • Ralentización frente a L110 pts
  • Quién atiende los accesos15 pts
  • Quién se lleva el tiempo20 pts
  • El tiempo de n accesos15 pts
  • El estudio con la tabla de SPY20 pts
Explorador · lab-cl-1
▾ lab-cl-1
▾ data
…
bash · en el navegador
learner@quantcraft:~/lab-cl-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-cl-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