σμ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 AcademyFeuilles de routeParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Feuilles de routeParcours
AO
+1,2 k apprenants
Connexion
  • 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 AcademyFeuilles de routeParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Feuilles de routeParcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Cache locality›Module 2 · Disposition et parcours›Sujet 2.1·~55 minIntermédiaire

Tableau de structures contre structure de tableaux

Position dans le parcoursModule 2 sur 3 · Sujet 1 sur 2
2.1 Tableau de structures contre structure de tableaux
—Score du lab— / 100XP total—Série—

5 min de lecture4 exemples de code

  1. 01Une table, deux dispositions
  2. 02Un parcours d'une colonne
  3. 03Lire des lignes entières inverse le résultat
  4. 04Laissez les comptes choisir
  5. 05Ce que vous allez construire

Une table de ticks peut se ranger en mémoire de deux façons, et la bonne dépend de la manière dont vous la lisez. Parcourir une colonne d'une table rangée ligne par ligne charge cinq fois plus de mémoire que vous n'en utilisez ; lire des lignes entières dans des colonnes séparées gaspille autant dans l'autre sens. Ce cours compte les défauts des deux dispositions et laisse les chiffres choisir.

01

Une table, deux dispositions

La mémoire est une longue file d'octets : une table à deux dimensions doit donc être aplatie. Il y a deux manières naturelles. Dans un tableau de structures, les cinq valeurs d'un jour (ouverture, plus haut, plus bas, clôture, volume) sont rangées ensemble, puis les cinq du jour suivant, et ainsi de suite. Dans une structure de tableaux, toutes les ouvertures viennent d'abord, puis tous les plus hauts, et ainsi de suite. Les valeurs et le programme sont les mêmes, mais les adresses diffèrent.

L'adresse d'une valeur découle de sa ligne et de sa colonne. En AoS, le numéro de la valeur est ligne fois le nombre de colonnes plus la colonne. En SoA, c'est colonne fois le nombre de lignes plus la ligne. Multipliez par 8 octets pour un float64. Le code ci-dessous affiche les adresses des clôtures des quatre premiers jours dans une table de 100 jours et 5 colonnes.

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 ligne suivante est n_cols valeurs plus loin : deux clôtures consécutives sont espacées de 5 valeurs (40 octets).

En AoS, les clôtures de jours consécutifs sont espacées de 40 octets. En SoA, elles sont à 8 octets l'une de l'autre, l'une juste après l'autre, ce qui est exactement la lecture dans l'ordre du premier sujet de cette compétence.

02

Un parcours d'une colonne

Calculer la moyenne des clôtures lit une colonne de la table. Nous transformons cette lecture en une liste d'adresses, la passons dans le cache construit aux sujets précédents et comptons les défauts pour 1 000 jours. Le dernier nombre affiché est la part utile.

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 parcours de colonne charge des lignes entières : 8 ÷ 40 = 20 % ; en SoA chaque octet chargé est une clôture : environ 100 %

Le parcours SoA échoue 125 fois pour 1 000 valeurs, un défaut pour huit valeurs et rien de gaspillé. Le parcours AoS échoue 625 fois, cinq fois plus, et seuls 20 % des octets chargés sont des clôtures. Les 80 % restants sont des ouvertures, plus hauts, plus bas et volumes que la moyenne ne regarde jamais, et ils chassent des lignes utiles du cache.

03

Lire des lignes entières inverse le résultat

Beaucoup de programmes de ticks posent une autre question : pour ce tick, donne-moi ensemble le prix, la taille et le sens. Cela lit chaque valeur d'une ligne. En AoS, toute la ligne est un bloc de 40 octets, dans une ou deux lignes de cache. En SoA, les cinq valeurs sont dans cinq tableaux différents, donc chaque ligne ouvre cinq lignes de cache. Le code lit une ligne sur sept et compte les défauts.

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

Maintenant AoS gagne d'un facteur trois : 214 défauts contre 625. La disposition cinq fois pire pour le parcours de colonne est trois fois meilleure pour la lecture de lignes. Aucune disposition n'est la meilleure : chacune est la meilleure pour une manière de lire.

Vous utilisez déjà les deux dispositions

Un tableau NumPy à deux dimensions dans l'ordre par défaut est une disposition AoS : chaque ligne est contiguë. Un DataFrame pandas range ensemble chaque colonne d'un même type, ce qui est proche de SoA, et c'est l'une des raisons pour lesquelles les opérations par colonne y sont rapides.

04

Laissez les comptes choisir

Un vrai programme fait les deux sortes de lecture : le choix dépend donc du mélange. Pour chaque disposition, additionnez les défauts de la charge : parcours de colonne fois défauts de colonne plus lectures de lignes fois défauts de lignes. La disposition au total le plus petit gagne, et une égalité va à AoS. Le code ci-dessous essaie quatre mélanges sur la même table.

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
Choisissez d'après le motif d'accès, pas par habitude

Avec un parcours de colonne pour deux lectures de lignes, le gagnant est AoS ; avec un parcours pour chaque lecture de ligne, c'est déjà SoA. Le point d'équilibre se situe vers un demi-parcours par lecture de ligne, et votre charge réelle décide de quel côté vous êtes. Comptez les lectures que fait vraiment votre programme avant de réorganiser une table.

Ce que disent les vraies donnéesBarres quotidiennes de SPY, 5 405 lignes × 5 colonnes, cache de 32 Ko à 8 voies
Parcours des clôtures : SoA67799,8 % des octets chargés utilisés
Parcours des clôtures : AoS3,37820 % des octets chargés utilisés
Lecture de 772 lignes : AoS1,159une ligne sur sept
Lecture de 772 lignes : SoA3,383cinq lignes ouvertes par ligne
Défauts du parcours des clôtures et des lectures de lignes, selon la disposition
Colonne, SoA677
Colonne, AoS3378
Lignes, AoS1159
Lignes, SoA3383
05

Ce que vous allez construire

Le labo suit le cours : les deux dispositions, les deux sortes de lecture, la comparaison, le choix pour une charge, la part utile et l'étude sur la vraie table SPY.

  • Fonction · questionS'appuie sur
  • address(layout, row, col, n_rows, n_cols)Où vit cette valeur ?De l'arithmétique simple
  • scan_column(layout, col, n_rows, n_cols)Quelles adresses un parcours de colonne lit-il ?address
  • scan_rows(layout, rows, n_rows, n_cols)Quelles adresses une lecture de lignes touche-t-elle ?address
  • compare(n_rows, n_cols, col, rows)Combien de défauts chaque lecture coûte-t-elle dans chaque disposition ?scan_columnscan_rows
  • best_layout(...)Quelle disposition convient à cette charge ?compare
  • useful_fraction(layout, col, n_rows, n_cols)Quelle part de ce qui est chargé est utilisée ?scan_column
  • study(bars)Quelle disposition convient à la table SPY ?comparebest_layout

À retenir

  • Une disposition est un ensemble de pas. AoS fait d'un parcours de colonne un pas de la largeur de ligne ; SoA en fait un pas d'une valeur.
  • Comptez les octets utiles. Un parcours de colonne d'une table AoS à 5 colonnes n'utilise que 20 % de ce qu'il charge, et le reste chasse des lignes utiles.
  • Choisissez selon la charge. Multipliez chaque sorte de lecture par ses défauts dans chaque disposition et prenez le total le plus petit ; l'équilibre était d'environ un demi-parcours par lecture de ligne.
Vérification rapide
Une table a 4 colonnes de valeurs de 8 octets rangée en AoS. Un parcours d'une colonne charge des lignes de 32 octets, deux par ligne de cache de 64 octets. Quelle part des octets chargés est utilisée ?
Choisissez une réponse.

Sujet suivant : la disposition a décidé du pas d'une table. Dans Ordre de parcours et tuilage de boucles, la table est une matrice : vous choisissez l'ordre des boucles, puis les découpez en tuiles pour qu'un bloc de la matrice reste dans le cache pendant qu'on l'utilise.

Lab : ce qui est noté

7 tâches · 100 pts
  • L'adresse d'une valeur15 pts
  • Un parcours de colonne10 pts
  • Lire des lignes entières10 pts
  • Les défauts des deux lectures20 pts
  • La disposition pour une charge15 pts
  • La part utile15 pts
  • L'étude sur la table SPY15 pts
Explorateur · lab-cl-5
▾ lab-cl-5
▾ data
…
bash · dans le navigateur
learner@quantcraft:~/lab-cl-5$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-cl-5 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes