Wann sich die GPU lohnt: Die Parameter-Sweep-Roofline, wo eine 167x-Schlagzeile in Wahrheit 27x Algorithmus mal 6.2x Hardware ist
Teil der Serie "Backtests ohne Illusionen".
Die Speed-Leiter endete auf einer bewusst unbefriedigenden Note. Wir hatten einen Parameter-Sweep über 80 Kombinationen von 69.9 Sekunden pandas auf 0.23 Sekunden paralleles numba auf einer Laptop-CPU gebracht — gemessene 298x — und dann argumentiert, dass eine GPU nicht die fehlende Sprosse sei. Die Kommentarspalte hat das nicht widerspruchslos hingenommen, und das zu Recht. "Nicht die fehlende Sprosse" ist eine Aussage über einen Workload, in einer Größe. Sie ist kein Naturgesetz. Also tut dieser Artikel das Ehrliche und stellt die GPU auf den Prüfstand.
Das Ergebnis ist kein Urteil, sondern eine Kurve. Der Vorsprung der GPU vor der CPU ist keine einzelne Zahl, die man auf eine Folie drucken kann; er ist eine Funktion davon, wie viel Arbeit du ihr pro Aufruf übergibst. Auf unserem Multi-Timeframe-Indikator-Precompute reicht der Speedup der GPU gegenüber der CPU-Engine von 54.5x, wenn wir eine Parameterkombination pro Aufruf verlangen, bis 359.6x, wenn wir 61 auf einmal verlangen. Derselbe Kernel, dieselben Daten, dieselbe Hardware — geändert hat sich einzig der Batch. Ein Benchmark, der eine dieser Zahlen ausweist und die andere verschweigt, misst nicht die GPU, er wählt eine Schlagzeile.
Und selbst 359.6x ist nicht das, wonach es aussieht. Nimm die Zahl auseinander, und eine große Multi-Timeframe-Schlagzeile von 167x zerfällt in 27x Algorithmus — ein Rewrite, das auch die CPU 27x schneller macht — mal 6.2x tatsächliche Hardware. Die 27x hat nicht die GPU geliefert. Die Mathematik war es. In diesem Artikel geht es darum, die beiden auseinanderzuhalten, denn ihre Vermengung ist die Art, wie eine Grafikkarte für $2,000 verkauft wird, um ein Problem zu lösen, das ein git commit gratis gelöst hätte.
Provenienz: Alle Zahlen unten sind auf einem Apple M2 Max gemessen, fp32 auf der Metal-GPU via MLX gegen fp64-numba auf zwölf CPU-Threads, aus unseren Benchmark-Skripten engine_multitf_gpu.py und bench_param_sweep.py, jeweils abgesichert durch einen Äquivalenz-Check, der bestätigt, dass GPU- und CPU-Pfad dieselben Trades produzieren. Für diesen Artikel gibt es kein öffentliches Begleitpaper — die Skripte sind das Protokoll.
Die Frage ist eine Roofline, kein Benchmark

Der Grund, warum "wie viel schneller ist die GPU" keine einzelne Antwort hat, ist das Roofline-Modell (Williams, Waterman & Patterson, 2009). Die erreichbare Leistung eines Prozessors wird von zwei Decken zugleich begrenzt: einer flachen, gesetzt durch die Spitzen-Rechenleistung (FLOP/s), und einer schrägen, gesetzt durch die Speicherbandbreite mal der arithmetischen Intensität — der Zahl nützlicher Operationen pro bewegtem Byte. Arbeit, die pro Byte billig ist, lebt unter der schrägen Decke und ist bandwidth-bound; Arbeit, die pro Byte reich ist, erreicht die flache Decke und ist compute-bound. Die flache Decke der GPU überragt die der CPU turmhoch, aber ihre schräge Decke und — entscheidend — ihre Fixkosten pro Aufruf skalieren nicht annähernd so gnädig nach unten.
Bei einem Parameter-Sweep dominiert ein dritter Term die linke Seite des Diagramms: Launch- und Transfer-Overhead. Jeder GPU-Aufruf zahlt einen ungefähr fixen Preis — den Kernel dispatchen, die Eingaben über die (auf Apple Silicon unified, aber nicht kostenlose) Speichergrenze bewegen, die Ergebnisse zurückbewegen. Modelliere die Wall-Clock-Zeit für einen Batch von Kombinationen als
wobei dieser fixe Overhead ist, die Grenzkosten der GPU pro Kombination und die der CPU. Der Speedup ist dann
Dieser eine Bruch erklärt den gesamten Artikel. Bei kleinem erdrückt das im Nenner das Verhältnis — du hast dafür bezahlt, die GPU zu wecken, und sie kaum benutzt. Wächst , amortisierst du über mehr Kombinationen, und der Speedup klettert seiner Asymptote entgegen, dem wahren Hardware-Verhältnis. Die Halbwegsmarke liegt bei : ein "Ridge Point" im Batch-Raum, der dir sagt, wie breit dein Sweep sein muss, bevor die GPU auch nur die Hälfte dessen ist, was sie sein kann. Ein Sweep aus ein paar Dutzend Kombinationen sitzt weit links von diesem Ridge. Ein Sweep aus Tausenden sitzt auf dem flachen Teil, wo die GPU sich ihre Decke endlich verdient.
Die richtige Frage lautet also nie "ist die GPU schneller". Sie lautet "liegt mein Sweep rechts vom Ridge, und ist meine Arbeit pro Kombination compute-bound genug, um die flache Decke zu erreichen, wenn er dort ankommt". Beides muss zutreffen. Der Rest dieses Artikels misst, wo diese Schwellen tatsächlich liegen.
Das Single-Timeframe-Urteil: die GPU gewinnt nur knapp

Fang mit dem Workload an, den die Speed-Leiter benutzt hat: ein Single-Timeframe-HMA/HMA3-Sweep, 80 Kombinationen über 150,000 Bars. Wir haben dieser Leiter eine sechste Sprosse hinzugefügt — M5, Indikatoren auf der Apple-GPU via MLX, die Trades werden weiterhin auf der CPU extrahiert. Aufgewärmt, Bestwert aus drei Läufen, das Äquivalenz-Gate grün:
| Methode | Wall-Clock | vs pandas | Kombinationen/s |
|---|---|---|---|
| M0 pandas + Schleife | 287.08s | 1.0x | 0.3 |
| M2 numba (1 Kern) | 2.02s | 142x | 39.7 |
| M4 mp + numba (12 Kerne) | 0.33s | 883x | 245.9 |
| M5 MLX GPU (fp32) | 0.10s | 2796x | 779.2 |
Gegen die naive serielle Baseline sieht die GPU heroisch aus — 2,796x. Aber das ist ein Vergleich, den niemand ehrlicherweise ziehen sollte: Er tritt eine gute GPU-Implementierung gegen die schlechteste CPU-Implementierung an. Stell die GPU der CPU gegenüber, die du tatsächlich einsetzen würdest — derselbe Kernel auf allen zwölf Kernen, M4 — und der Gewinn kollabiert auf nüchterne 3.2x (779 gegen 246 Kombinationen pro Sekunde). Eine ganze Grafikkarte, die den kompletten Sweep fährt, schlägt einen Zwölf-Kern-CPU-Pool um Faktor drei.
Drei Komma zwei ist nicht nichts. Es ist aber auch nicht der Grund, aus dem irgendjemand eine GPU kauft. Und es ist exakt das, was die Roofline für einen derart schmalen Sweep vorhersagt: 80 Kombinationen liegen links vom Ridge. Der fixe Launch-und-Transfer-Overhead ist bei einem 0.10-Sekunden-Job immer noch ein spürbarer Anteil, wir erreichen die Asymptote also nie. Schlimmer noch: Ein Teil der Kosten pro Kombination ist der O(n)-Pass der Trade-Extraktion, den wir bewusst auf der CPU gelassen haben — ein Term, den die GPU überhaupt nicht beschleunigen kann (warum, dazu mehr im nächsten Abschnitt). Für eine Single-Timeframe-Research-Schleife dieser Größe bleibt das ursprüngliche Urteil der Speed-Leiter bestehen: Die GPU ist nicht die fehlende Sprosse. Paralleles numba hatte dich schon bei 0.23–0.33 Sekunden, und diese auf 0.10 zu drücken ist nicht das, was einen Researcher entblockt. Die Orchestrierung rund um den Sweep ist es.
Das interessante Wort in diesem Urteil ist dieser Größe. Beweg dich auf der Batch-Achse nach rechts, und die Geschichte ändert sich.
Wo die Kosten tatsächlich stecken
Bevor wir den Batch hochskalieren, schau dir an, wofür wir eigentlich bezahlen, denn die Roofline belohnt dich nur, wenn der teure Teil compute-bound ist. Profiliere den Sweep, und fast alles davon ist eine einzige Sache: Weighted-Moving-Average-Faltungen. Ein HMA sind drei WMAs; ein HMA3 sind vier; jede Kombination lässt sie erneut über die ganze Serie laufen. Die Trade-Extraktion — die beiden Indikator-Arrays entlanggehen, die Vorzeichenwechsel von hma - hma3 finden, Fills verbuchen — ist ein einzelner billiger O(n)-Pass. Der Sweep ist ein Faltungs-Workload im Kostüm einer Trading-Strategie.
Diese Aufteilung ist exakt das Regime-Paar der Roofline:
- Faltungen sind compute-bound. Jeder Preis wird in viele überlappende Fenstersummen eingelesen, die arithmetische Intensität — Operationen pro bewegtem Byte — ist also hoch. Diese Arbeit greift nach der flachen Compute-Decke, und die flache Decke der GPU ist die, die alles überragt. Besser noch: Die Fenster überlappen über Kombinationen hinweg. Ein WMA der Länge 40 ist für Dutzende Kombinationen nützlich, ein gebatchter Precompute berechnet es also einmal und teilt es, statt es pro Kombination neu zu rechnen. Batching amortisiert nicht nur den Launch-Overhead; es erhöht die arithmetische Intensität, weil Loads wiederverwendet werden. Das ist der Teil, der auf die GPU gehört.
- Die Trade-Extraktion ist bandwidth-bound und verzweigungslastig. Ein sequentieller Pass, datenabhängige Branches an jeder Kreuzung, praktisch keine Wiederverwendung. Ihre arithmetische Intensität liegt nahe am Boden, und ihr Kontrollfluss ist einem SIMD-Gerät feindlich gesinnt. Sie auf die GPU zu schieben würde wenig einbringen und viel kosten; sie bleibt auf der CPU. Womit sie der serielle Rest einer Aufteilung nach dem Amdahlschen Gesetz ist — ein fixer Boden, den der GPU-Speedup nie durchstoßen kann, und ein Teil des Grundes, warum die Single-TF-Zahl bei 3.2x gesättigt ist.
In der Multi-Timeframe-Version dieses Kernels versteckt sich eine zweite, schärfere Lektion, und sie ist die Quelle der 27x, deren Erklärung wir die ganze Zeit versprechen. Die Multi-TF-Engine richtet ein Higher-Timeframe-HMA ohne Look-ahead am 1-Minuten-Basisindex aus. Auf die offensichtliche Art geschrieben ist das O(length^1.5)-Arbeit pro Bar — die Higher-TF-Moving-Averages werden an jedem Basis-Bar neu berechnet. Aber das ausgerichtete HMA ist linear in einem kurzen Buffer der letzten paar geschlossenen Higher-TF-Kerzen plus dem laufenden Close, also kollabiert die gesamte Rechnung pro Bar zu einem festen Gewichtsvektor: ein conv1d über die Serie der geschlossenen Kerzen, gefolgt von einem O(n)-Gather. Aus Hunderten Millionen redundanter Operationen wird eine Faltung über eine weit kürzere Serie.
Dieser Kollaps ist ein algorithmischer Gewinn, kein Hardware-Gewinn. Er ist eine bessere Formel. Sie läuft auf der GPU, und sie läuft genauso gut auf der CPU — np.correlate plus Gather, in fp64. Behalte das fest im Kopf: Der größte Einzelfaktor in der Multi-TF-Schlagzeile ist ein Rewrite, das auch einer Maschine ganz ohne GPU offensteht. Wenn wir die 167x am Ende zerlegen, ist das die 27x.
Der Vorsprung wächst mit der Batch-Größe

Jetzt die Messung, die uns die Roofline aufgetragen hat. Nimm die teure Achse — den ausgerichteten Higher-Timeframe-HMA-Precompute auf der 1-Minuten-Basisserie, dem längsten Kerzenstrom, den wir haben — und füttere die GPU mit einer wachsenden Zahl von Längenkombinationen pro Aufruf, . Die CPU-Baseline ist hier die ehrliche Produktions-Engine: numba mit prange über alle zwölf Kerne. Für jeden Batch messen wir beides und bilden das Verhältnis.
| Batch (Kombinationen/Aufruf) | GPU-Speedup vs. 12-Kern-CPU-Engine |
|---|---|
| 1 | 54.5x |
| 2 | 102.5x |
| 4 | 129.5x |
| 8 | 187.4x |
| 16 | 267.4x |
| 32 | 245.0x |
| 61 | 359.6x |
Das ist , nachgezeichnet in echten Messungen. Bei liegt die GPU schon 54.5x vorn — weil dieser Vergleich gegen die naive per-Bar-Engine läuft, der algorithmische Kollaps also schon bei einer einzigen Kombination eingebacken ist — aber sie ist weit von ihrer Decke entfernt: Der fixe Overhead dominiert einen Ein-Kombinations-Aufruf noch immer. Verdopple den Batch, und der Speedup verdoppelt sich beinahe auf 102.5x; bei sind es 267.4x; bei sind es 359.6x, und die Kurve steigt sichtbar weiter. Der Vorsprung wächst mit der Größe des Problems. Das ist der wichtigste einzelne Satz über GPUs und Parameter-Sweeps, und er ist das exakte Gegenteil davon, wie GPU-Speedups üblicherweise zitiert werden — als wären sie eine konstante Eigenschaft des Chips.
Zwei Anmerkungen zur Ehrlichkeit, denn das hier ist die Serie Backtests ohne Illusionen, und eine saubere monotone Tabelle verdient immer Misstrauen.
Erstens der Einbruch: steht bei 245.0x, unter den 267.4x bei . Das ist kein Rauschen, das man unter den Teppich kehrt — es ist ein Chunk-Grenzen-Artefakt. Unser conv1d packt 32 Längen in einen Kernel-Chunk, füllt also exakt einen Chunk ohne Spielraum, während in einen zweiten Chunk überläuft, der das Gerät zufällig besser sättigt. Der Punkt der Roofline ist der Trend, und ein echtes Gerät hat Quantisierungsstufen; wir berichten den Schlenker, statt per Rosinenpickerei um ihn herumzumessen.
Zweitens, und wichtiger: 54.5x und 359.6x sind beide gegen die naive CPU-Engine gemessen, und keine der beiden Zahlen ist der Hardware-Gewinn. Beide enthalten noch den algorithmischen 27x-Kollaps. Würdest du die CPU-Baseline gegen den kollabierten Algorithmus auf der CPU tauschen — dieselbe Formel, fp64, beide Seiten optimiert — würde jede Zeile um ungefähr diesen Faktor schrumpfen. Was genau die Zerlegung ist, die der nächste Abschnitt exakt macht.
Die ehrliche Zerlegung: 27x Algorithmus mal 6.2x Hardware

Um den Algorithmus vom Silizium zu trennen, musst du drei Pfade auf demselben Multi-Timeframe-Gitter messen, nicht zwei. Der Benchmark fährt also:
- cpu-engine — die numba-Produktions-Engine, per-Bar ausgerichtetes HMA über alle Kerne. Die naive-aber-parallele Baseline.
- cpu-collapsed — die kollabierten Gewichtsvektoren,
np.correlateplus Gather, fp64, auf der CPU. Dieselbe Hardware wie (1), besserer Algorithmus. - gpu-mlx — die kollabierten Gewichte als gebatchtes
conv1dauf der Metal-GPU, fp32. Derselbe Algorithmus wie (2), andere Hardware.
Stell sie nebeneinander, und die volle Multi-Timeframe-Schlagzeile faktorisiert sauber:
Der linke Faktor, 27x, ist der Algorithmus — der Kollaps von per-Bar zu Faltung aus dem vorigen Abschnitt. Mit der GPU hat er nichts zu tun. Implementiere ihn in numpy, und deine Laptop-CPU wird auf diesem Workload 27x schneller, zum Preis eines Refactorings. Der rechte Faktor, 6.2x, ist die Hardware — der ehrliche Eins-zu-eins-Gewinn der Metal-GPU gegenüber demselben optimierten Algorithmus auf zwölf CPU-Kernen. Diese 6.2x sind der einzige Teil, für den du tatsächlich eine GPU gebraucht hast.
Das ist die ganze Moral des Artikels, als Arithmetik formuliert. Wenn dir ein Vendor-Benchmark, ein Bibliotheks-README oder ein begeisterter Kollege "167x auf der GPU" zeigt, sollte der Reflex eine einzige Frage sein: Was war die CPU-Baseline? War die Baseline die naive Implementierung — und das ist sie fast immer, denn eine langsame Baseline macht die bessere Folie — dann ist der Großteil der Schlagzeile ein algorithmischer Gewinn, auf den auch die CPU Anspruch hatte, und nur der Rest ist Hardware. Hier beträgt der Rest 6.2x. Eine Angabe von 167x überzeichnet den Beitrag der Hardware um grob das 27-Fache.
Und beachte, wie der Hardware-Faktor selbst mit der Problemgröße gewandert ist. Auf dem kleinen Single-Timeframe-Sweep lag der wahre Gewinn GPU gegen beste CPU bei 3.2x. Auf dem größeren Multi-Timeframe-Precompute waren es 6.2x — dieselben zwei Chips, fast der doppelte Hardware-Vorteil, allein weil der größere Workload weiter die Roofline hinauf Richtung der flachen Compute-Decke der GPU drückt, bevor die CPU Schritt halten kann. Auch der Hardware-Vorsprung ist keine Konstante. Er ist ein Punkt auf derselben steigenden Kurve, und nach rechts bewegst du dich auf dieser Kurve, indem du den Batch größer und die Arbeit pro Kombination reicher machst.
Ein Entscheidungsleitfaden: wie breit muss der Sweep sein?

Falte die Roofline zurück in eine Entscheidung, die du treffen kannst, bevor du Geld ausgibst. Die GPU lohnt sich, wenn beide Bedingungen der Roofline zugleich gelten: Dein Sweep liegt rechts vom Batch-Ridge (, der fixe Launch-und-Transfer-Overhead ist also amortisiert), und deine Arbeit pro Kombination ist compute-bound (genug arithmetische Intensität, um die flache Decke zu erreichen, kein dünner O(n)-Pass). Konkret, aus dem, was wir gemessen haben:
- Ein paar Dutzend Kombinationen einer Single-Timeframe-Strategie: Lass die GPU weg. Du bist links vom Ridge; der ehrliche Gewinn gegenüber parallelem numba beträgt ~3.2x auf einem Job, der ohnehin nur eine Zehntelsekunde dauert. Der Flaschenhals ist nicht der Kernel, sondern alles um ihn herum.
- Tausende Kombinationen oder ein wirklich Multi-Timeframe-/Multi-Indikator-Precompute: Die GPU verdient sich ihren Platz. Der Overhead amortisiert sich, die geteilten Faltungen erhöhen die arithmetische Intensität, und der Hardware-Gewinn klettert auf 6.2x und steigt mit dem Batch weiter. Das ist das Regime, in dem eine GPU einen Sweep über Nacht in eine Kaffeepause verwandelt.
- Erklimm zuerst die CPU-Leiter — sie ist billiger, und sie kommt zuerst. Die 298x auf der CPU und der algorithmische 27x-Kollaps sind gratis oder beinahe gratis, und sie sind Voraussetzungen, keine Alternativen: Die 6.2x der GPU kommen obendrauf auf den kollabierten Algorithmus, den du ohnehin schreiben musstest. Eine GPU, an eine naive Pipeline geschraubt, misst überwiegend die Naivität.
Auf dem GPU-Zweig liegt außerdem eine Steuer, die nichts mit Geschwindigkeit zu tun hat, und du musst sie einpreisen: Apples Metal-GPU hat überhaupt kein fp64. Alles läuft in fp32, ~1.2e-7 relative Präzision. Das killt den Lehrbuch-Trick für schnelle Moving Averages — das O(n)-Präfixsummen-WMA — denn bei einer Preisskala nahe 30,000 über 150k Bars erreichen die laufenden Summen ~1e14, sieben Größenordnungen jenseits des sicheren Integer-Bereichs von fp32; wir haben relative Fehler bis ~2e2 gemessen (ein Faktor zweihundert, nicht zwei Prozent). Die funktionierende Formulierung ist die direkte gefensterte Faltung, bei der jede Fenstersumme aus einer begrenzten Zahl vergleichbar großer Terme besteht und fp32 bis auf ~8e-7 genau bleibt. Selbst dann wird eine Strategie, die auf dem Vorzeichen von hma - hma3 entscheidet, gelegentlich eine Kreuzung an einem Grenz-Bar kippen, an dem sich die beiden Kurven fast berühren, weil fp32-Rundung ein Beinahe-Unentschieden umwirft. Deshalb wird der GPU-Pfad mit einem Äquivalenz-Gate ausgeliefert, das misst, wie stark die Trades divergiert sind — PnL-Delta in Basispunkten, relative Verschiebung der Trade-Anzahl — statt bit-identische Ausgabe zu behaupten, die er nie liefern kann. In unserem Lauf betrug diese Divergenz 90 verschobene Fills von 479,016 (0.019%), komfortabel innerhalb der Toleranz, aber die Last ist real: Auf die GPU zu gehen heißt, eine numerische Paritäts-Story zu besitzen, nicht bloß einen schnelleren Takt. Auch diese Engineering-Kosten gehören in den Break-even.
Die Zahlen sind Apple-förmig; die Kurve ist es nicht
Jede Zahl oben ist ein Apple M2 Max: ein Unified-Memory-Gerät, bei dem GPU und CPU sich einen Pool teilen, und eine reine fp32-GPU ohne doppelte Genauigkeit. Eine diskrete NVIDIA- oder AMD-Karte ändert die Konstanten, und es lohnt sich, explizit zu sagen, in welche Richtung jede davon wandert, denn die Form des Arguments überlebt, auch wenn die Zahlen es nicht tun.
- Der Transfer-Overhead wird schlimmer, nicht besser. Eine diskrete Karte sitzt hinter PCIe, Eingaben und Ergebnisse machen also eine echte Kopie über den Bus, die Unified Memory vermeidet. Das schiebt den Batch-Ridge nach rechts — du brauchst einen noch breiteren Sweep, bevor eine diskrete GPU ihren Launch amortisiert. Die linke Kante der Roofline ist auf einem PCIe-Gerät steiler, nicht sanfter.
- Die flache Decke wird höher. Eine Rechenzentrums-GPU hat weit mehr FLOP/s und Bandbreite als eine integrierte, der asymptotische Hardware-Gewinn auf einem sättigenden Sweep ist also größer als unsere 6.2x. Die Belohnung dafür, die rechte Seite der Kurve zu erreichen, wächst; die Maut dafür, links zu sitzen, wächst mit.
- fp64 kommt zurück, und mit ihm der Präfixsummen-Trick. Auf einer Karte mit echter doppelter Genauigkeit ist das O(n)-Präfixsummen-WMA wieder gangbar, und das Paritäts-Gate lässt sich Richtung Bit-Exaktheit anziehen. Die spezifische fp32-Steuer, die wir gezahlt haben — direkte Faltung statt Präfixsummen, ein divergenzmessendes Gate statt eines Asserts — ist ein Apple-Silicon-Detail, kein Gesetz.
Nichts davon ändert die These. Auf jedem Gerät gilt : ein fixer Overhead, den du amortisieren musst, eine Asymptote, der du dich nur von rechts näherst. Die Konstanten sind Hardware; die Kurve ist Arithmetik. Miss deine eigenen , und mit einem fünfzeiligen Batch-Sweep, bevor du irgendjemandes Schlagzeile traust — unserer eingeschlossen.
Wo das anknüpft
Das ist die vierte Messung in einer kleinen Unterserie darüber, woher Backtest-Geschwindigkeit tatsächlich kommt, und die Teile fügen sich zu einem einzigen Argument darüber, was du optimieren solltest, bevor du Geld ausgibst:
- Die Speed-Leiter kletterte von pandas zu parallelem numba, für 298x allein auf der CPU, und ließ die GPU als offene Frage zurück. Dieser Artikel beantwortet sie: Die GPU ist eine echte, aber bedingte fünfte Sprosse, 3.2x–6.2x über der obersten CPU-Sprosse wert — und das erst, sobald der Sweep breit genug ist, um die Roofline hinaufzuklettern.
- Die IPC-Steuer machte denselben Zug in die Gegenrichtung — messen, was es kostet, den Prozess zu verlassen — und kam zur selben Form von Schlussfolgerung: Die Grenze (ein Socket, ein GPU-Launch) ist billig; die Steuer liegt darin, wie oft und wie geschwätzig du sie überquerst. Batche deine GPU-Aufrufe aus demselben Grund, aus dem du dein IPC batchst: um fixe Kosten pro Überquerung zu amortisieren.
- Der aggregierte Parquet-Cache ist die CPU-seitige Version dessen, was der GPU-Precompute tut — die geteilten Indikatoren einmal berechnen und über jede Kombination wiederverwenden. Die GPU trägt dieses Wiederverwenden-und-Batchen-Prinzip nur bis ins Silizium.
- Und das fp32-Paritäts-Gate ist ein Problem der Backtest-Live-Parität im Kleinen: In dem Moment, in dem dein schneller Pfad etwas leicht anderes berechnet als deine Referenz, schuldest du eine quantifizierte Rechenschaft über die Divergenz, kein Handwedeln.
Die verbindende Disziplin ist dieselbe, auf die diese ganze Serie drängt: Miss das, was dir tatsächlich verkauft wird. Ein Speedup ist ein Verhältnis, und ein Verhältnis hat einen Zähler und einen Nenner. Die meiste GPU-Enttäuschung kommt von einem Nenner — der CPU-Baseline — der gewählt wurde, um zu schmeicheln, und die meiste GPU-Verschwendung kommt davon, einen Sweep zu fahren, der zu klein ist, um die linke Kante der Roofline zu verlassen.
Kernaussagen
- Ein GPU-Speedup ist eine Kurve, keine Zahl. Auf unserem Multi-Timeframe-Precompute lief der Vorsprung vor der CPU von 54.5x bei einer Kombination pro Aufruf bis 359.6x bei einundsechzig — derselbe Chip, dieselben Daten. Jede einzelne Zahl ist ein Punkt auf dieser Kurve; frag, bei welcher Batch-Größe sie gemessen wurde.
- Verhöre immer die CPU-Baseline. Die Multi-Timeframe-Schlagzeile von 167x faktorisiert sauber in 27x Algorithmus (ein Kollaps von per-Bar zu Faltung, der die CPU genauso beschleunigt) mal 6.2x tatsächliche Hardware. Der faire Gewinn GPU gegen beste CPU beträgt hier 3.2x single-timeframe, 6.2x multi — nicht 167x.
- Der Vorsprung wächst mit der Problemgröße, und der Hardware-Faktor auch. Größerer Batch und reichere Arbeit pro Kombination schieben dich die Roofline hinauf: Der ehrliche Hardware-Gewinn selbst stieg von 3.2x auf 6.2x, allein weil der Workload größer wurde. Kleine Sweeps sitzen links vom Ridge und profitieren kaum.
- Fixe zuerst den Algorithmus und erklimm die CPU-Leiter — der GPU-Gewinn sitzt obendrauf, nicht an ihrer Stelle. Die 6.2x sind gegen den kollabierten Algorithmus gemessen, den du ohnehin schreiben musstest. Schraub eine GPU an eine naive Pipeline, und das meiste, was du misst, ist die Naivität, nicht das Silizium.
- Auf die GPU zu gehen heißt, eine numerische Paritäts-Story zu besitzen. Kein fp64 auf Metal, der Präfixsummen-WMA-Trick stirbt an der Preisskala (relativer Fehler ~2e2), und Strategien, die auf dem Vorzeichen einer Kreuzung entscheiden, kippen an Grenz-Bars. Liefere ein Äquivalenz-Gate mit, das die Divergenz in Basispunkten quantifiziert; rechne diese Engineering-Kosten in deinen Break-even ein.
Wenn dir jemand erzählt, die GPU habe seinen Backtest hundertmal schneller gemacht, hat er dir fast nichts erzählt. Frag nach der Batch-Größe und der CPU-Baseline, und die hundert löst sich meist in einen einstelligen Hardware-Gewinn auf, gewickelt um einen algorithmischen, den es gratis gegeben hätte — durchaus lohnend, auf einem ausreichend breiten Sweep, aus exakt den Gründen, die die Roofline nennt, und aus keinem einzigen mehr.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.