σμ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 AcademyRoadmapsSkills
Ctrl K
AO
+1,2 Tsd. Lernende
AnmeldenBeitreten ↗
RoadmapsSkills
AO
+1,2 Tsd. Lernende
Anmelden
  • 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 AcademyRoadmapsSkills
Ctrl K
AO
+1,2 Tsd. Lernende
AnmeldenBeitreten ↗
RoadmapsSkills
AO
+1,2 Tsd. Lernende
Anmelden
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Cache locality-Skill›Modul 2 · Anordnung und Durchlauf›Thema 2.1·~55 Min.Mittelstufe

Array of Structs gegen Struct of Arrays

Position in der SkillModul 2 von 3 · Thema 1 von 2
2.1 Array of Structs gegen Struct of Arrays
—Lab-Punkte— / 100XP gesamt—Serie—

4 Min. Lesezeit4 Codebeispiele

  1. 01Eine Tabelle, zwei Layouts
  2. 02Ein Durchlauf einer Spalte
  3. 03Ganze Zeilen zu lesen kehrt das Ergebnis um
  4. 04Lassen Sie die Zahlen wählen
  5. 05Was Sie bauen werden

Eine Tick-Tabelle kann auf zwei Arten im Speicher liegen, und die richtige hängt davon ab, wie Sie sie lesen. Eine Spalte einer zeilenweise gespeicherten Tabelle zu durchlaufen lädt fünfmal so viel Speicher, wie Sie nutzen; ganze Zeilen aus getrennten Spalten zu lesen verschwendet umgekehrt genauso viel. Diese Lektion zählt die Fehlzugriffe beider Layouts und lässt die Zahlen entscheiden.

01

Eine Tabelle, zwei Layouts

Der Speicher ist eine lange Reihe von Bytes, eine zweidimensionale Tabelle muss also flachgelegt werden. Es gibt zwei natürliche Wege. In einem Array of Structs liegen die fünf Werte eines Tages (Eröffnung, Hoch, Tief, Schluss, Volumen) zusammen, dann die fünf des nächsten Tages und so weiter. In einem Struct of Arrays kommen zuerst alle Eröffnungen, dann alle Hochs und so weiter. Werte und Programm sind gleich, nur die Adressen unterscheiden sich.

Die Adresse eines Werts folgt aus seiner Zeile und Spalte. Im AoS ist die Wertnummer Zeile mal Spaltenzahl plus Spalte. Im SoA ist sie Spalte mal Zeilenzahl plus Zeile. Mit 8 Byte für einen float64 multiplizieren. Der Code unten gibt die Adressen der Schlusskurse der ersten vier Tage in einer Tabelle mit 100 Tagen und 5 Spalten aus.

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. 1Im AoS liegt die nächste Zeile n_cols Werte weiter, aufeinanderfolgende Schlusskurse sind also 5 Werte (40 Byte) auseinander.

Im AoS liegen die Schlusskurse aufeinanderfolgender Tage 40 Byte auseinander. Im SoA sind es 8 Byte, einer direkt nach dem anderen, genau das Lesen in Reihenfolge aus dem ersten Thema dieser Fähigkeit.

02

Ein Durchlauf einer Spalte

Der Mittelwert der Schlusskurse liest eine Spalte der Tabelle. Wir machen aus diesem Lesen eine Adressliste, schicken sie durch den Cache aus den letzten Themen und zählen die Fehlzugriffe für 1.000 Tage. Die letzte ausgegebene Zahl ist der nützliche Anteil.

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)im AoS lädt ein Spaltendurchlauf ganze Zeilen: 8 ÷ 40 = 20 %; im SoA ist jedes geladene Byte ein Schlusskurs: etwa 100 %

Der SoA-Durchlauf verfehlt 125-mal bei 1.000 Werten, ein Fehlzugriff pro acht Werte und nichts verschwendet. Der AoS-Durchlauf verfehlt 625-mal, fünfmal so oft, und nur 20 % der geladenen Byte sind Schlusskurse. Die übrigen 80 % sind Eröffnungen, Hochs, Tiefs und Volumina, die der Mittelwert nie ansieht und die nützliche Zeilen aus dem Cache verdrängen.

03

Ganze Zeilen zu lesen kehrt das Ergebnis um

Viele Tick-Programme stellen eine andere Frage: Gib mir für diesen Tick Preis, Größe und Seite zusammen. Das liest jeden Wert einer Zeile. Im AoS ist die ganze Zeile ein 40-Byte-Block in einer oder zwei Cache-Zeilen. Im SoA liegen die fünf Werte in fünf verschiedenen Arrays, jede Zeile öffnet also fünf Cache-Zeilen. Der Code liest jede siebte Zeile und zählt die Fehlzugriffe.

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

Jetzt gewinnt AoS mit dem Faktor drei: 214 Fehlzugriffe gegen 625. Das Layout, das beim Spaltendurchlauf fünfmal schlechter war, ist beim Zeilenlesen dreimal besser. Keines der Layouts ist das beste; jedes ist für eine Art des Lesens das beste.

Sie nutzen beide Layouts bereits

Ein zweidimensionales NumPy-Array in der Standardordnung ist ein AoS-Layout: Jede Zeile liegt zusammenhängend. Ein pandas-DataFrame speichert Spalten desselben Typs zusammen, was SoA nahekommt, und das ist ein Grund, warum Spaltenoperationen darauf schnell sind.

04

Lassen Sie die Zahlen wählen

Ein echtes Programm macht beide Arten des Lesens, die Wahl hängt also von der Mischung ab. Addieren Sie für jedes Layout die Fehlzugriffe der Last: Spaltendurchläufe mal Spaltenfehlzugriffe plus Zeilenlesen mal Zeilenfehlzugriffe. Das Layout mit der kleineren Summe gewinnt, ein Gleichstand geht an AoS. Der Code unten probiert vier Mischungen an derselben Tabelle.

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
Wählen Sie nach dem Zugriffsmuster, nicht nach Gewohnheit

Bei einem Spaltendurchlauf je zwei Zeilenlesen gewinnt AoS; bei einem Durchlauf je Zeilenlesen gewinnt schon SoA. Der Break-even liegt bei etwa einem halben Durchlauf pro Zeilenlesen, und Ihre echte Last entscheidet, auf welcher Seite Sie sind. Zählen Sie die Zugriffe, die Ihr Programm wirklich macht, bevor Sie eine Tabelle umbauen.

Was die echten Daten sagenSPY-Tagesbalken, 5.405 Zeilen × 5 Spalten, 32-KB-Cache mit 8 Wegen
Durchlauf der Schlusskurse: SoA67799,8 % der geladenen Byte genutzt
Durchlauf der Schlusskurse: AoS3,37820 % der geladenen Byte genutzt
Lesen von 772 Zeilen: AoS1,159jede siebte Zeile
Lesen von 772 Zeilen: SoA3,383fünf Zeilen pro Zeile geöffnet
Fehlzugriffe von Schlusskurs-Durchlauf und Zeilenlesen nach Layout
Spalte, SoA677
Spalte, AoS3378
Zeilen, AoS1159
Zeilen, SoA3383
05

Was Sie bauen werden

Das Labor folgt der Lektion: die zwei Layouts, die zwei Arten des Lesens, der Vergleich, die Wahl für eine Last, der nützliche Anteil und die Studie an der echten SPY-Tabelle.

  • Funktion · FrageBaut auf
  • address(layout, row, col, n_rows, n_cols)Wo liegt dieser Wert?Einfache Arithmetik
  • scan_column(layout, col, n_rows, n_cols)Welche Adressen liest ein Spaltendurchlauf?address
  • scan_rows(layout, rows, n_rows, n_cols)Welche Adressen berührt ein Zeilenlesen?address
  • compare(n_rows, n_cols, col, rows)Wie viele Fehlzugriffe kostet jedes Lesen in jedem Layout?scan_columnscan_rows
  • best_layout(...)Welches Layout passt zu dieser Last?compare
  • useful_fraction(layout, col, n_rows, n_cols)Wie viel vom Geladenen wird genutzt?scan_column
  • study(bars)Welches Layout passt zur SPY-Tabelle?comparebest_layout

Das Wichtigste

  • Ein Layout ist eine Menge von Schrittweiten. AoS macht den Spaltendurchlauf zur Schrittweite der Zeilenbreite; SoA macht ihn zu einer Schrittweite von einem Wert.
  • Zählen Sie die nützlichen Byte. Ein Spaltendurchlauf einer AoS-Tabelle mit 5 Spalten nutzt nur 20 % des Geladenen, der Rest verdrängt nützliche Zeilen.
  • Wählen Sie nach der Last. Multiplizieren Sie jede Art des Lesens mit ihren Fehlzugriffen in jedem Layout und nehmen Sie die kleinere Summe; der Break-even lag bei etwa einem halben Durchlauf pro Zeilenlesen.
Schnelltest
Eine Tabelle hat 4 Spalten mit 8-Byte-Werten, als AoS gespeichert. Ein Durchlauf einer Spalte lädt Zeilen von 32 Byte, zwei pro 64-Byte-Cache-Zeile. Welcher Anteil der geladenen Byte wird genutzt?
Wähle eine Antwort.

Nächstes Thema: Das Layout hat die Schrittweite einer Tabelle bestimmt. In Durchlaufreihenfolge und Loop Tiling ist die Tabelle eine Matrix, Sie wählen die Reihenfolge der Schleifen und zerlegen sie dann in Kacheln, damit ein Block der Matrix im Cache bleibt, solange er genutzt wird.

Lab: Was bewertet wird

7 Aufgaben · 100 Pkt.
  • Die Adresse eines Werts15 Pkt.
  • Ein Spaltendurchlauf10 Pkt.
  • Ganze Zeilen lesen10 Pkt.
  • Die Fehlzugriffe beider Lesearten20 Pkt.
  • Das Layout für eine Last15 Pkt.
  • Der nützliche Anteil15 Pkt.
  • Die Studie mit der SPY-Tabelle15 Pkt.
Explorer · lab-cl-5
▾ lab-cl-5
▾ data
…
bash · im Browser
learner@quantcraft:~/lab-cl-5$
Python noch nicht geladenPythonLn 1, Col 1Spaces: 4UTF-8

Dieses Thema zurücksetzen?

Alles in lab-cl-5 wird auf die Startdateien zurückgesetzt und das Terminal geleert. Ihre aktuelle Lösung geht verloren – das lässt sich nicht rückgängig machen.

Wird zurückgesetzt
  • main.py → Startcode
  • Terminal-Verlauf und Testausgabe
  • Nicht eingereichte Änderungen
Bleibt erhalten
  • Beste Lab-Punktzahl
  • Ihre XP und Ihre Serie
  • Vorlesungsfortschritt und Notizen