← zurück zum Showcase

TAKT-Showcase: Zeitreihenprognose in Rust (DLinear und PatchTST auf ETT)

Zwei Standardmodelle für Langzeitprognosen, DLinear (LTSF-Linear) und PatchTST, mit den offiziellen Skripten auf den ETT-Datensätzen trainiert, aus PyTorch in reines Rust übersetzt (kein BLAS, keine ML-Laufzeitumgebung) und zweimal gebaut: der ursprüngliche Build dieses Rust-Codes und der TAKT-Build desselben Codes. Die Vorhersagen beider Builds sind bis aufs Bit identisch. Das Paket enthält fertige Programme für beide Builds, den Quellcode der ursprünglichen Version, ein Skript, das die Eingabefenster aus den offiziellen Daten neu erzeugt, ein Benchmark-Skript und unsere Messungen.

Was die Programme berechnen

Jedes Fenster des Test-Splits eines ETT-Datensatzes (offizielle Grenzen, Features M, StandardScaler auf dem Train-Split angepasst): 336 vergangene Schritte x 7 Kanäle als Eingabe, die nächsten 96 Schritte x 7 Kanäle als Ausgabe, float32. ETTh1/ETTh2: 2.785 Fenster; ETTm1/ETTm2: 11.425 Fenster. Die Gewichte des Checkpoints für den gewählten Datensatz sind in das Programm einkompiliert (--data).

Modell Datensatz Test-MSE / -MAE (alle Fenster, tools/score.py) Offizieller Trainingslauf, Test
DLinear ETTh1 0,384144 / 0,404713 0,384144 / 0,404713
DLinear ETTh2 0,290098 / 0,353328 0,290098 / 0,353328
DLinear ETTm1 0,301222 / 0,344619 0,301222 / 0,344619
DLinear ETTm2 0,171852 / 0,267122 0,171852 / 0,267122
PatchTST ETTh1 0,385126 / 0,405953 (erste 2.688: 0,381618 / 0,405088) 0,381618 / 0,405088
PatchTST ETTh2 0,274616 / 0,337234 (erste 2.688: 0,274118 / 0,336000) 0,274117 / 0,336000

Die offizielle Testschleife von PatchTST verwirft den letzten unvollständigen Batch von 128 Fenstern, daher die Angabe „erste 2.688“. Die Rust-Übersetzung ist nicht bitidentisch mit PyTorch (andere Summationsreihenfolge; die größte absolute Abweichung gegenüber PyTorch eager beträgt 2e-6 bis 2e-5); der TAKT-Build ist bitidentisch mit dem Rust-Original.

Messung (TAKT-Messstand)

Gemessen am 03.10.2026. AMD Threadripper PRO 5975WX (Zen 3), Linux. Ein CCX mit 8 Kernen reserviert, deren zweite SMT-Threads im Leerlauf; Läufe mit einem Thread an einen Kern gebunden, Läufe mit acht Threads an die 8 Kerne des CCX. Für jeden Datensatz: ein Aufwärmpaar, dann 9 Runden in abwechselnder Reihenfolge, Median. Zeit: die vom Programm selbst gemessene Vorhersageschleife (Lesen der Fenster und Schreiben der Vorhersagen sind ausgenommen; sie kosten in beiden Builds gleich viel). Takte: Takte derselben Schleife im User-Modus (perf stat, summiert über alle Threads). Beide Builds nutzen denselben Treiber, der freigegebenen Heap-Speicher im Prozess behält (glibc mallopt), wie es ein langlaufender Dienst täte, damit temporäre Puffer pro Fenster nicht zu Page Faults werden. Gemessen wurden genau die Dateien in bin/, mit tools/run_bench.sh.

Modell Datensatz Fenster Threads Original, ms TAKT, ms Beschleunigung Original, Mio. Takte TAKT, Mio. Takte Beschleunigung nach Takten Identische Runden
DLinear ETTh1 2.785 1 788,4 111,0 7,10× 3.518 486 7,23× 9/9
DLinear ETTh2 2.785 1 785,0 108,4 7,24× 3.512 476 7,38× 9/9
DLinear ETTm1 11.425 1 3.223,9 447,8 7,20× 14.421 1.978 7,29× 9/9
DLinear ETTm2 11.425 1 3.228,4 442,6 7,29× 14.405 1.984 7,26× 9/9
DLinear ETTh1 2.785 8 103,3 14,5 7,11× 3.515 486 7,23× 9/9
DLinear ETTh2 2.785 8 103,3 14,2 7,26× 3.514 477 7,37× 9/9
DLinear ETTm1 11.425 8 423,3 58,6 7,22× 14.426 1.988 7,26× 9/9
DLinear ETTm2 11.425 8 422,1 58,9 7,17× 14.407 1.998 7,21× 9/9
PatchTST ETTh1 2.785 1 13.440,8 5.733,4 2,34× 59.973 25.507 2,35× 9/9
PatchTST ETTh2 2.785 1 13.484,3 5.749,2 2,35× 60.068 25.645 2,34× 9/9
PatchTST ETTh1 2.785 8 1.777,5 765,7 2,32× 60.424 25.955 2,33× 9/9
PatchTST ETTh2 2.785 8 1.785,3 769,3 2,32× 60.677 26.070 2,33× 9/9

DLinear: 7,1–7,3× nach Zeit und 7,2–7,4× nach Takten, mit einem und mit acht Threads. PatchTST: 2,3× nach Zeit und nach Takten. Mit acht Threads verbrauchen beide Builds insgesamt etwa gleich viele Takte wie mit einem. Der gesamte Prozess, einschließlich des Lesens von 26–107 MB Fenstern und des Schreibens der Vorhersagen, ist bei DLinear mit einem Thread 5,7–6,0× schneller, mit acht 3,0–3,1× und bei PatchTST 2,3×. Details, Rohmessungen und Zeiten des Gesamtprozesses stehen in measurements.json.

Ausführen

python3 tools/make_windows.py --out data --targets   # lädt die offiziellen ETT-CSVs herunter (mit sha256-Prüfung),
                                                     # schreibt data/windows_*_test.f32 (und die Zielwerte)
mkdir -p out
for d in ETTh1 ETTh2 ETTm1 ETTm2; do
  bin/ltsf-dlinear-takt --data $d --windows data/windows_${d}_test.f32 --threads 1 --out out/dlinear_$d.f32
done
for d in ETTh1 ETTh2; do
  bin/ltsf-patchtst-takt --data $d --windows data/windows_${d}_test.f32 --threads 1 --out out/patchtst_$d.f32
done
sha256sum -c expected.sha256                 # Fenster, Zielwerte und Vorhersagen wie in unserer Messung
python3 tools/score.py data/targets_ETTh1_test.f32 out/dlinear_ETTh1.f32

tools/run_bench.sh -m dlinear -n 9 -c 2          # beide Builds, abwechselnd, Prüfung Byte für Byte, Kern 2
tools/run_bench.sh -m dlinear -n 9 -t 8 -c 2-9   # acht Threads auf den Kernen 2-9
tools/run_bench.sh -m patchtst -n 9 -c 2

--threads T akzeptiert jedes T >= 1; die Vorhersagen hängen nicht von T ab. Ein- und Ausgabedateien sind rohe Little-Endian-float32-Daten (n x 336 x 7 bzw. n x 96 x 7); tools/make_windows.py benötigt nur numpy.

Linux x86-64 (glibc). Die Programme sind für x86-64-v3 gebaut (AVX2, FMA, BMI2: Intel Haswell und neuer, AMD Zen und neuer); FMA-Instruktionen werden nicht verwendet. PatchTST ruft erff aus der C-Bibliothek des Systems auf: Auf einer glibc, deren erff sich von glibc 2.39 (Ubuntu 24.04) unterscheidet, können seine Vorhersagen von expected.sha256 abweichen, während Original und TAKT-Build weiterhin Byte für Byte übereinstimmen.

Äquivalenz

Original neu bauen

cd source
RUSTFLAGS="-C target-cpu=x86-64-v3 --remap-path-prefix=$HOME/.rustup=. \
  --remap-path-prefix=$HOME/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f=." \
  cargo +1.96.0 build --release --locked
strip --strip-all -o ltsf-dlinear-original target/release/ltsf-dlinear

Mit Rust 1.96.0 aus rustup (mit der Komponente rust-src) reproduziert dies auf unserer Maschine bin/ltsf-dlinear-original und bin/ltsf-patchtst-original Bit für Bit; bei einem anderen Setup unterscheiden sich die eingebetteten Pfade, die Vorhersagen nicht.

Früherer Vergleich mit PyTorch (mit diesem Paket nicht reproduzierbar)

Am 02.10.2026 haben wir dieselben Modelle auf derselben Maschine unter anderen Bedingungen gemessen: ohne Reservierung des Messstands (39 logische CPUs, mit anderen Jobs geteilt); beide Rust-Versionen als Shared Libraries für die native CPU gebaut und im selben Prozess aufgerufen; PyTorch 2.14 auf der CPU, eager und torch.compile (Inductor), Batches von 64 oder 512. Für jede Implementierung wurden die Thread-Anzahl (1, 8 oder 32) und die Batch-Größe nach der niedrigsten Latenz auf dem Validierungs-Split gewählt, dann wurde der Test-Split gemessen (Median aus 5).

Modell Datensatz Original-Rust TAKT-Build Schnellstes PyTorch (Inductor) PyTorch / TAKT
DLinear ETTh1 28,7 ms (32 Thr.) 5,1 ms (32) 18,5 ms (32, Batch 512) 3,63×
DLinear ETTh2 30,1 ms (32) 5,3 ms (32) 12,5 ms (32, Batch 512) 2,34×
DLinear ETTm1 115,4 ms (32) 19,7 ms (32) 65,1 ms (8, Batch 512) 3,31×
DLinear ETTm2 114,9 ms (32) 20,4 ms (32) 49,5 ms (32, Batch 512) 2,43×
PatchTST ETTh1 593 ms (32) 236 ms (32) 634 ms (32, Batch 64) 2,69×
PatchTST ETTh2 519 ms (32) 234 ms (32) 551 ms (32, Batch 64) 2,35×

Mit einem Thread war die Reihenfolge anders: PyTorch mit Inductor und Batches war schneller als der TAKT-Build (DLinear ETTh1: 37,9 ms gegenüber 111,4 ms; PatchTST ETTh1: 2,03 s gegenüber 5,78 s). In jenem Lauf war der TAKT-Build bei den gewählten 32 Threads 5,6–5,9× (DLinear) und 2,2–2,5× (PatchTST) schneller als das ursprüngliche Rust, mit einem Thread 7,2–7,3× bzw. 2,3×. Alle Zahlen jenes Laufs stehen in measurements.json (earlier_measurement_2026_10_02).

Inhalt

Die ETT-Daten sind nicht enthalten (CC BY-ND 4.0, sie werden von tools/make_windows.py heruntergeladen). Der Quellcode des TAKT-Builds wird nicht veröffentlicht: Wir liefern Builds.

Quelldatei: README.de.md

Telegram