σμ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 2 · Disposición y recorrido›Tema 2.1·~55 minIntermedio

Arreglo de estructuras frente a estructura de arreglos

Posición en la rutaMódulo 2 de 3 · Tema 1 de 2
2.1 Arreglo de estructuras frente a estructura de arreglos
—Puntuación del lab— / 100XP total—Racha—

5 min de lectura4 ejemplos de código

  1. 01Una tabla, dos disposiciones
  2. 02Un recorrido de una columna
  3. 03Leer filas enteras invierte el resultado
  4. 04Deje que los recuentos elijan
  5. 05Lo que va a construir

Una tabla de ticks puede estar en memoria de dos maneras, y la correcta depende de cómo la lea. Recorrer una columna de una tabla guardada fila a fila carga cinco veces la memoria que usa; leer filas enteras de columnas separadas desperdicia lo mismo al revés. Esta lección cuenta los fallos de ambas disposiciones y deja que los recuentos elijan.

01

Una tabla, dos disposiciones

La memoria es una larga fila de bytes, así que una tabla bidimensional hay que aplanarla. Hay dos maneras naturales. En un arreglo de estructuras los cinco valores de un día (apertura, máximo, mínimo, cierre, volumen) se guardan juntos, luego los cinco del día siguiente, y así. En una estructura de arreglos primero van todas las aperturas, luego todos los máximos, y así. Los valores y el programa son los mismos, pero las direcciones cambian.

La dirección de un valor se deduce de su fila y su columna. En AoS el número del valor es fila por el número de columnas más la columna. En SoA es columna por el número de filas más la fila. Se multiplica por 8 bytes para un float64. El código siguiente muestra las direcciones de los cierres de los cuatro primeros días en una tabla de 100 días y 5 columnas.

main.pyPython
def address(layout, row, col, n_rows, n_cols, elem_bytes=8):    if layout == "aos":        return (row * n_cols + col) * elem_bytes1    return (col * n_rows + row) * elem_bytesclose = 3print("aos:", [address("aos", row, close, 100, 5) for row in range(4)])print("soa:", [address("soa", row, close, 100, 5) for row in range(4)])
aos: [24, 64, 104, 144]
soa: [2400, 2408, 2416, 2424]
  1. 1En AoS la fila siguiente está n_cols valores más allá, así que dos cierres consecutivos están separados 5 valores (40 bytes).

En AoS los cierres de días consecutivos están separados 40 bytes. En SoA están a 8 bytes, uno justo tras otro, que es exactamente la lectura en orden del primer tema de esta habilidad.

02

Un recorrido de una columna

Promediar los cierres lee una columna de la tabla. Convertimos esa lectura en una lista de direcciones, la pasamos por la caché construida en los temas anteriores y contamos los fallos para 1000 días. El último número que mostramos es la parte útil.

main.pyPython
def scan_column(layout, col, n_rows, n_cols):    return [address(layout, row, col, n_rows, n_cols) for row in range(n_rows)]for layout in ("aos", "soa"):    misses = count_misses(scan_column(layout, 3, 1000, 5))    print(layout, "misses:", misses, "- useful share:", round(1000 * 8 / (misses * 64), 3))
aos misses: 625 - useful share: 0.2
soa misses: 125 - useful share: 1.0
useful share = value bytes ÷ row bytes (while a row fits in one line)(1)en AoS un recorrido de columna carga filas enteras: 8 ÷ 40 = 20 %; en SoA cada byte cargado es un cierre: casi el 100 %

El recorrido SoA falla 125 veces para 1000 valores, un fallo por cada ocho valores y nada desperdiciado. El recorrido AoS falla 625 veces, cinco veces más, y solo el 20 % de los bytes cargados son cierres. El otro 80 % son aperturas, máximos, mínimos y volúmenes que el promedio nunca mira, y expulsan líneas útiles de la caché.

03

Leer filas enteras invierte el resultado

Muchos programas de ticks hacen otra pregunta: para este tick, deme el precio, el tamaño y el lado juntos. Eso lee todos los valores de una fila. En AoS la fila entera es un bloque de 40 bytes, que está en una o dos líneas. En SoA los cinco valores están en cinco arreglos distintos, así que cada fila abre cinco líneas. El código lee una fila de cada siete y cuenta los fallos.

main.pyPython
def scan_rows(layout, rows, n_rows, n_cols):    return [address(layout, row, col, n_rows, n_cols) for row in rows for col in range(n_cols)]rows = list(range(0, 1000, 7))for layout in ("aos", "soa"):    print(layout, "misses:", count_misses(scan_rows(layout, rows, 1000, 5)))
aos misses: 214
soa misses: 625

Ahora AoS gana por un factor de tres: 214 fallos contra 625. La disposición que era cinco veces peor para el recorrido de columna es tres veces mejor para la lectura de filas. Ninguna disposición es la mejor: cada una lo es para una forma de leer.

Ya usa las dos disposiciones

Un arreglo bidimensional de NumPy en el orden por defecto es una disposición AoS: cada fila es contigua. Un DataFrame de pandas guarda juntas las columnas de un mismo tipo, lo cual se parece a SoA, y es una de las razones por las que las operaciones por columna son rápidas.

04

Deje que los recuentos elijan

Un programa real hace los dos tipos de lectura, así que la elección depende de la mezcla. Para cada disposición sume los fallos de la carga: recorridos de columna por fallos de columna más lecturas de filas por fallos de filas. Gana la disposición con el total menor, y un empate va a AoS. El código siguiente prueba cuatro mezclas en la misma tabla.

main.pyPython
def compare(n_rows, n_cols, col, rows):    out = {}    for layout in ("aos", "soa"):        out[layout] = {            "column": count_misses(scan_column(layout, col, n_rows, n_cols)),            "rows": count_misses(scan_rows(layout, rows, n_rows, n_cols)),        }    return outdef best_layout(n_column_scans, n_row_reads, n_rows, n_cols, col, rows):    counts = compare(n_rows, n_cols, col, rows)    totals = {layout: n_column_scans * counts[layout]["column"] + n_row_reads * counts[layout]["rows"] for layout in counts}    return "soa" if totals["soa"] < totals["aos"] else "aos"rows = list(range(0, 1000, 7))for scans, reads in ((1, 4), (1, 2), (1, 1), (4, 1)):    print(scans, "column scans per", reads, "row reads:", best_layout(scans, reads, 1000, 5, 3, rows))
1 column scans per 4 row reads: aos
1 column scans per 2 row reads: aos
1 column scans per 1 row reads: soa
4 column scans per 1 row reads: soa
Elija según el patrón de acceso, no por costumbre

Con un recorrido de columna por cada dos lecturas de filas gana AoS; con un recorrido por cada lectura de fila ya gana SoA. El punto de equilibrio está cerca de medio recorrido por lectura de fila, y su carga real decide de qué lado está. Cuente las lecturas que su programa hace de verdad antes de reorganizar una tabla.

Qué dicen los datos realesBarras diarias de SPY, 5405 filas × 5 columnas, caché de 32 KB de 8 vías
Recorrido de los cierres: SoA67799,8 % de los bytes cargados usados
Recorrido de los cierres: AoS3,37820 % de los bytes cargados usados
Lectura de 772 filas: AoS1,159una fila de cada siete
Lectura de 772 filas: SoA3,383cinco líneas abiertas por fila
Fallos del recorrido de cierres y de las lecturas de filas, según la disposición
Columna, SoA677
Columna, AoS3378
Filas, AoS1159
Filas, SoA3383
05

Lo que va a construir

El laboratorio sigue la lección: las dos disposiciones, los dos tipos de lectura, la comparación, la elección para una carga, la parte útil y el estudio sobre la tabla real de SPY.

  • Función · preguntaSe apoya en
  • address(layout, row, col, n_rows, n_cols)¿Dónde vive este valor?Aritmética simple
  • scan_column(layout, col, n_rows, n_cols)¿Qué direcciones lee un recorrido de columna?address
  • scan_rows(layout, rows, n_rows, n_cols)¿Qué direcciones toca una lectura de filas?address
  • compare(n_rows, n_cols, col, rows)¿Cuántos fallos cuesta cada lectura en cada disposición?scan_columnscan_rows
  • best_layout(...)¿Qué disposición conviene a esta carga?compare
  • useful_fraction(layout, col, n_rows, n_cols)¿Cuánto de lo cargado se usa?scan_column
  • study(bars)¿Qué disposición conviene a la tabla de SPY?comparebest_layout

Ideas clave

  • Una disposición es un conjunto de pasos. AoS hace que un recorrido de columna tenga un paso del ancho de fila; SoA lo hace de un valor.
  • Cuente los bytes útiles. Un recorrido de columna de una tabla AoS de 5 columnas usa solo el 20 % de lo que carga, y el resto expulsa líneas útiles.
  • Elija según la carga. Multiplique cada tipo de lectura por sus fallos en cada disposición y tome el total menor; el equilibrio era de medio recorrido por lectura de fila.
Comprobación rápida
Una tabla tiene 4 columnas de valores de 8 bytes guardada como AoS. Un recorrido de una columna carga filas de 32 bytes, dos por línea de 64 bytes. ¿Qué parte de los bytes cargados se usa?
Elige una respuesta.

Siguiente tema: la disposición decidió el paso de una tabla. En Orden de recorrido y mosaico de bucles la tabla es una matriz y usted elige el orden de los bucles y luego los corta en teselas para que un bloque de la matriz siga en la caché mientras se usa.

Lab: qué se califica

7 tareas · 100 pts
  • La dirección de un valor15 pts
  • Un recorrido de columna10 pts
  • Leer filas enteras10 pts
  • Los fallos de ambas lecturas20 pts
  • La disposición para una carga15 pts
  • La parte útil15 pts
  • El estudio con la tabla de SPY15 pts
Explorador · lab-cl-5
▾ lab-cl-5
▾ data
…
bash · en el navegador
learner@quantcraft:~/lab-cl-5$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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