σμ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 1 · Caches von Grund auf›Thema 1.1·~40 Min.Einsteiger

Die Speicherhierarchie und Latenzzahlen

Position in der SkillModul 1 von 3 · Thema 1 von 4
1.1 Die Speicherhierarchie und Latenzzahlen
—Lab-Punkte— / 100XP gesamt—Serie—

6 Min. Lesezeit4 Codebeispiele

  1. 01Warum der Speicher und nicht die Arithmetik die Geschwindigkeit bestimmt
  2. 02Was ein Zugriff kostet
  3. 03Die durchschnittliche Zugriffszeit
  4. 04Wer antwortet und wer kostet
  5. 05Eine echte Tabelle lesen
  6. 06Was Sie bauen werden

Ein Prozessor addiert zwei Zahlen in einem Bruchteil einer Nanosekunde, aber eine Zahl zu holen, die in keinem Cache liegt, kann 80 ns dauern. Diese Lektion macht aus den Latenzzahlen eine einzige Kennzahl, die durchschnittlichen Kosten eines Speicherzugriffs, und zeigt, welche Speicherebene Ihre Zeit frisst.

01

Warum der Speicher und nicht die Arithmetik die Geschwindigkeit bestimmt

Ein Trading-Programm verbringt die meiste Zeit damit, auf Daten zu warten: einen Preis, eine Order, eine Tabellenzeile. Die Rechnung mit diesen Daten ist fast gratis, aber die Daten müssen erst vom Speicher zum Prozessor reisen, und je weiter weg sie liegen, desto länger dauert das. Denken Sie an einen Schreibtisch, ein Regal, das Stockwerk einer Bibliothek und ein Lager am anderen Ende der Stadt. Die paar Blätter auf dem Schreibtisch sind sofort da, das Regal braucht Sekunden, die Bibliothek eine Minute und das Lager einen Nachmittag. Computer kopieren die am meisten genutzten Daten in kleine, schnelle Speicher neben dem Prozessor, genannt Caches, damit die meisten Anfragen am Schreibtisch beantwortet werden.

DefinitionLatenz

Die Zeit zwischen der Anfrage nach einem Wert und seinem Eintreffen, hier in Nanosekunden (Milliardstel Sekunden) gemessen.

Ein typischer Serverkern hat drei Caches vor dem Hauptspeicher. Wir nennen sie L1, L2 und L3 und den Hauptspeicher RAM. Eine Anfrage, die ein Cache beantwortet, ist ein Treffer; eine Anfrage, die zur nächsten Ebene weiter muss, ist ein Fehlzugriff. Die Tabelle unten stellt die vier Latenzen nebeneinander.

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

Jede Ebene ist mehrfach langsamer als die darüber, und der RAM ist 80-mal langsamer als L1. In dieser ganzen Fähigkeit geht es darum, dass die meisten Zugriffe nahe der Spitze beantwortet werden.

02

Was ein Zugriff kostet

Ein Zugriff beginnt bei L1. Liegt der Wert dort, kostet der Zugriff die L1-Latenz und ist vorbei. Sonst fragt der Prozessor L2, dann L3, dann den RAM, der den Wert immer hat. Jede befragte Ebene kostet Zeit, ob sie trifft oder nicht, ein in L3 gefundener Zugriff hat also L1, L2 und L3 bezahlt.

  1. 1
    L1 fragen1 ns zahlen. Ein Treffer beendet den Zugriff.
  2. 2
    Bei Fehlzugriff L2 fragen4 ns mehr zahlen. Ein Treffer beendet den Zugriff.
  3. 3
    Bei Fehlzugriff L3 fragen15 ns mehr zahlen. Ein Treffer beendet den Zugriff.
  4. 4
    Bei Fehlzugriff zum RAM80 ns mehr zahlen. Der RAM hat den Wert immer.
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

Ein von L3 beantworteter Zugriff kostet 20 ns, nicht 15: die 1 ns und 4 ns für die beiden früheren Fehlzugriffe gehören zur Rechnung.

Trefferquoten sind lokal

Jede Trefferquote zählt nur die Zugriffe, die diese Ebene erreichen. Eine L2-Trefferquote von 0,8 heißt 80 % der Zugriffe, die L1 verfehlt haben, nicht 80 % aller Zugriffe. Beides zu verwechseln ist der häufigste Fehler in diesem Thema.

03

Die durchschnittliche Zugriffszeit

Kein einzelner Zugriff ist typisch, also fragen wir nach dem Durchschnitt über viele Zugriffe. Gewichten Sie die Kosten jedes Pfads mit seiner Häufigkeit. Jeder Zugriff zahlt die L1-Latenz; nur der Anteil, der L1 verfehlt, zahlt L2; nur der, der beide verfehlt, zahlt L3; und so weiter. Als ein Ausdruck, mit Klammern, die wie die Ebenen verschachtelt sind:

AMAT = t₁ + (1 − h₁) · (t₂ + (1 − h₂) · (t₃ + (1 − h₃) · t_RAM))(1)t₁, t₂, t₃ und t_RAM sind die Latenzen; h₁, h₂ und h₃ sind die lokalen Trefferquoten von L1, L2 und L3

Das ist die durchschnittliche Speicherzugriffszeit. Lesen Sie die Klammern von innen nach außen: die innerste ist, was ein Fehlzugriff in L3 kostet, die nächste addiert den L3-Besuch, und so weiter bis zum L1-Besuch, den jeder Zugriff zahlt.

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 ist der Anteil der Zugriffe, die verfehlen, jede Klammer wird also mit dem Anteil multipliziert, der so weit kommt.

Selbst wenn 90 % der Zugriffe in L1 treffen, liegt der Durchschnitt noch bei 2,18 ns, mehr als das Doppelte der L1-Latenz. Die wenigen Zugriffe, die tief gehen, sind so teuer, dass sie den Durchschnitt bestimmen. Darum kann eine scheinbar hervorragende Trefferquote ein langsames Programm verbergen.

Takte oder Nanosekunden?

Chip-Hersteller geben die Latenz in Prozessortakten an: 4 Takte bei 4 GHz sind 1 ns. Wir nutzen Nanosekunden, damit sich die Ebenen auf einen Blick vergleichen lassen. Die Zahlen sind typisch, nicht exakt: echte Chips weichen um den Faktor zwei in beide Richtungen ab.

04

Wer antwortet und wer kostet

Zwei Fragen sehen ähnlich aus, haben aber verschiedene Antworten. Welche Ebene beantwortet die Zugriffe? Und welche Ebene nimmt die Zeit? Die erste zählt Zugriffe, die zweite zählt Warten. Eine Ebene wird von einem Anteil der Zugriffe erreicht, und jeder Besuch kostet ihre Latenz, eine selten erreichte, aber langsame Ebene kann das Warten also trotzdem dominieren.

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 ist der Anteil der Zugriffe, die jede Ebene besuchen: alle besuchen L1.
  2. 2sum(cost) ist die durchschnittliche Zugriffszeit: dieselben 2,18 ns wie zuvor.

Der RAM beantwortet nur 0,6 % der Zugriffe, macht aber 22 % der Wartezeit aus, während L1 90 % beantwortet und 46 % der Zeit nimmt. Das ist die Schlüsselaussage hinter jeder Optimierung in dieser Fähigkeit: Zählen Sie nicht Zugriffe, zählen Sie, wohin die Zeit geht.

05

Eine echte Tabelle lesen

Wie viel bedeutet das für eine echte Tabelle? Die SPY-Tagesbalken-Tabelle des Labors hat 5.405 Zeilen und fünf Spalten (Eröffnung, Hoch, Tief, Schluss, Volumen), also 27.025 Werte. Lesen Sie jeden Wert einmal unter drei Bedingungen: heiße Caches, in denen fast alles trifft, warme Caches und kalte Caches, in denen die Hälfte der Zugriffe auf jeder Ebene verfehlt.

Was die echten Daten sagenSPY-Tagesbalken, 2005 bis 2026
Werte in der Tabelle27,0255.405 Zeilen × 5 Spalten
Kalt gegen heiß15.9×langsamer für dieselbe Tabelle
Durchschnittlicher Zugriff, heiße Caches1.05nsTrefferquoten 0,99 · 0,95 · 0,90
Durchschnittlicher Zugriff, kalte Caches16.75nsder RAM macht 60 % dieser Wartezeit aus
Zeit, die Tabelle einmal zu lesen, in Mikrosekunden
Heiße Caches28
Warme Caches59
Kalte Caches453
06

Was Sie bauen werden

Das Labor macht aus dieser Lektion sechs kleine Funktionen, jede baut auf den vorigen auf, und endet damit, das Lesen der echten SPY-Tabelle zu beziffern. Hier sind sie mit der Frage, die jede beantwortet.

  • Funktion · FrageBaut auf
  • average_access_time(hit_rates)Was kostet ein Zugriff im Durchschnitt?Die Latenztabelle
  • slowdown(hit_rates)Wie viel langsamer als immer in L1 zu treffen?average_access_time
  • levels_served(hit_rates)Welche Ebene beantwortet die Zugriffe?Lokale Trefferquoten
  • time_shares(hit_rates)Welche Ebene nimmt die Zeit?average_access_time
  • scan_time_us(n_accesses, hit_rates)Wie lange dauern n Zugriffe?average_access_time
  • study(bars)Was kostet es, die ganze SPY-Tabelle zu lesen?scan_time_ustime_shares

Das Wichtigste

  • Zählen Sie, wohin die Zeit geht. Der Anteil der Zugriffe, den eine Ebene beantwortet, und der Anteil der Zeit, den sie kostet, sind verschiedene Zahlen, und eine seltene, aber langsame Ebene kann dominieren.
  • Trefferquoten sind lokal. Die Quote jeder Ebene zählt nur die Zugriffe, die sie erreichen, der Anteil, den eine Ebene beantwortet, ist also das Produkt der Fehlzugriffe darüber und ihrer eigenen Quote.
  • Der Durchschnitt ist eine Klammerschachtel. AMAT = t₁ + Fehl₁ × (t₂ + Fehl₂ × (…)) ist die Zahl, die jedes spätere Thema senken will.
Schnelltest
Eine L1-Trefferquote von 0,95. Von den Zugriffen, die L2 erreichen, trifft die Hälfte. Welchen Anteil aller Zugriffe beantwortet L2?
Wähle eine Antwort.

Nächstes Thema: Diese Lektion hat die Trefferquoten als gegeben genommen. In Cache-Zeilen und räumliche Lokalität bauen Sie den Cache, der sie erzeugt, und erfahren, warum es fast nichts kostet, einen Wert neben einem gerade gelesenen zu lesen.

Lab: Was bewertet wird

6 Aufgaben · 100 Pkt.
  • Durchschnittliche Zugriffszeit20 Pkt.
  • Verlangsamung gegenüber L110 Pkt.
  • Wer die Zugriffe beantwortet15 Pkt.
  • Wer die Zeit nimmt20 Pkt.
  • Die Zeit von n Zugriffen15 Pkt.
  • Die Studie mit der SPY-Tabelle20 Pkt.
Explorer · lab-cl-1
▾ lab-cl-1
▾ data
…
bash · im Browser
learner@quantcraft:~/lab-cl-1$
Python noch nicht geladenPythonLn 1, Col 1Spaces: 4UTF-8

Dieses Thema zurücksetzen?

Alles in lab-cl-1 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