← retour à la vitrine

Vitrine TAKT : prévision de séries temporelles en Rust (DLinear et PatchTST sur ETT)

Deux modèles standard de prévision à long terme, DLinear (LTSF-Linear) et PatchTST, entraînés avec les scripts officiels sur les jeux de données ETT, traduits de PyTorch en Rust pur (sans BLAS, sans runtime ML) et compilés deux fois : le build d’origine de ce code Rust et le build TAKT du même code. Les prédictions des deux builds sont identiques au bit près. Vous trouverez ici les programmes prêts à l’emploi pour les deux builds, le code source de la version d’origine, un script qui recrée les fenêtres d’entrée à partir des données officielles, un script de benchmark et nos mesures.

Ce que calculent les programmes

Chaque fenêtre de la partition de test d’un jeu ETT (bornes officielles, features M, StandardScaler ajusté sur la partition d’entraînement) : 336 pas passés × 7 canaux en entrée, les 96 pas suivants × 7 canaux en sortie, en float32. ETTh1/ETTh2 : 2 785 fenêtres ; ETTm1/ETTm2 : 11 425 fenêtres. Les poids du checkpoint du jeu sélectionné sont compilés dans le programme (--data).

Modèle Jeu de données MSE / MAE de test (toutes les fenêtres, tools/score.py) Entraînement officiel, 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 (2 688 premières : 0,381618 / 0,405088) 0,381618 / 0,405088
PatchTST ETTh2 0,274616 / 0,337234 (2 688 premières : 0,274118 / 0,336000) 0,274117 / 0,336000

La boucle de test officielle de PatchTST écarte le dernier lot incomplet de 128 fenêtres, d’où la mention « 2 688 premières ». La traduction en Rust n’est pas identique bit à bit à PyTorch (ordre de sommation différent ; l’écart absolu maximal avec PyTorch eager va de 2·10⁻⁶ à 2·10⁻⁵) ; le build TAKT est identique bit à bit à l’original en Rust.

Mesure (banc TAKT)

Mesuré le 3 octobre 2026. AMD Threadripper PRO 5975WX (Zen 3), Linux. Un CCX de 8 cœurs réservé, leurs jumeaux SMT inactifs ; les exécutions monothread sont épinglées sur un cœur, les exécutions à huit threads sur les 8 cœurs du CCX. Pour chaque jeu de données : une paire de préchauffage, puis 9 tours en ordre alterné, médiane. Temps : la boucle de prédiction, chronométrée par le programme lui-même (la lecture des fenêtres et l’écriture des prédictions sont exclues ; elles coûtent autant dans les deux builds). Cycles : cycles en mode utilisateur de la même boucle (perf stat, additionnés sur tous les threads). Les deux builds partagent le même programme pilote, qui conserve dans le processus la mémoire du tas libérée (mallopt de glibc), comme le ferait un service de longue durée, afin que les tampons temporaires par fenêtre ne se transforment pas en défauts de page. Ce sont exactement les fichiers de bin/ qui ont été mesurés, avec tools/run_bench.sh.

Modèle Jeu de données Fenêtres Threads Original, ms TAKT, ms Accélération Original, Mcycles TAKT, Mcycles Accélération en cycles Tours identiques
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× en temps et 7,2–7,4× en cycles, avec un comme avec huit threads. PatchTST : 2,3× en temps comme en cycles. Avec huit threads, les deux builds consomment à peu près le même total de cycles qu’avec un seul. Le processus complet, y compris la lecture de 26–107 Mo de fenêtres et l’écriture des prédictions, est 5,7–6,0× plus rapide pour DLinear avec un thread, 3,0–3,1× avec huit, et 2,3× pour PatchTST. Les détails, les échantillons bruts et les temps du processus complet figurent dans measurements.json.

Exécution

python3 tools/make_windows.py --out data --targets   # télécharge les CSV ETT officiels (vérifiés par sha256),
                                                     # écrit data/windows_*_test.f32 (et les cibles)
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                 # fenêtres, cibles et prédictions identiques à notre mesure
python3 tools/score.py data/targets_ETTh1_test.f32 out/dlinear_ETTh1.f32

tools/run_bench.sh -m dlinear -n 9 -c 2          # les deux builds, entrelacés, vérification octet par octet, cœur 2
tools/run_bench.sh -m dlinear -n 9 -t 8 -c 2-9   # huit threads sur les cœurs 2-9
tools/run_bench.sh -m patchtst -n 9 -c 2

--threads T accepte tout T ≥ 1 ; les prédictions ne dépendent pas de T. Les fichiers d’entrée et de sortie sont des float32 bruts little-endian (n × 336 × 7 et n × 96 × 7) ; tools/make_windows.py ne nécessite que numpy.

Linux x86-64 (glibc). Les programmes sont compilés pour x86-64-v3 (AVX2, FMA, BMI2 : Intel Haswell et plus récents, AMD Zen et plus récents) ; les instructions FMA ne sont pas utilisées. PatchTST appelle erff de la bibliothèque C du système : avec une glibc dont erff diffère de celui de glibc 2.39 (Ubuntu 24.04), ses prédictions peuvent différer de expected.sha256, tandis que l’original et le build TAKT restent identiques octet par octet.

Équivalence

Recompiler l’original

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

Avec Rust 1.96.0 installé via rustup (avec le composant rust-src), cela reproduit bin/ltsf-dlinear-original et bin/ltsf-patchtst-original bit à bit sur notre machine ; avec une autre configuration, les chemins embarqués diffèrent, mais pas les prédictions.

Comparaison antérieure avec PyTorch (non reproductible avec cette archive)

Le 2 octobre 2026, nous avons mesuré les mêmes modèles sur la même machine dans d’autres conditions : sans réservation du banc (39 CPU logiques partagés avec d’autres tâches) ; les deux versions Rust compilées en bibliothèques partagées pour le CPU natif et appelées dans le processus ; PyTorch 2.14 sur CPU, en mode eager et avec torch.compile (Inductor), par lots de 64 ou 512. Pour chaque implémentation, le nombre de threads (1, 8 ou 32) et la taille de lot ont été choisis selon la latence la plus faible sur la partition de validation, puis la partition de test a été chronométrée (médiane de 5).

Modèle Jeu de données Rust d’origine Build TAKT PyTorch le plus rapide (Inductor) PyTorch / TAKT
DLinear ETTh1 28,7 ms (32 threads) 5,1 ms (32) 18,5 ms (32, lot de 512) 3,63×
DLinear ETTh2 30,1 ms (32) 5,3 ms (32) 12,5 ms (32, lot de 512) 2,34×
DLinear ETTm1 115,4 ms (32) 19,7 ms (32) 65,1 ms (8, lot de 512) 3,31×
DLinear ETTm2 114,9 ms (32) 20,4 ms (32) 49,5 ms (32, lot de 512) 2,43×
PatchTST ETTh1 593 ms (32) 236 ms (32) 634 ms (32, lot de 64) 2,69×
PatchTST ETTh2 519 ms (32) 234 ms (32) 551 ms (32, lot de 64) 2,35×

En monothread, l’ordre était différent : PyTorch avec Inductor et des lots était plus rapide que le build TAKT (DLinear ETTh1 : 37,9 ms contre 111,4 ms ; PatchTST ETTh1 : 2,03 s contre 5,78 s). Lors de cette exécution, le build TAKT était 5,6–5,9× (DLinear) et 2,2–2,5× (PatchTST) plus rapide que le Rust d’origine avec les 32 threads retenus, et 7,2–7,3× et 2,3× en monothread. Tous les chiffres de cette exécution figurent dans measurements.json (earlier_measurement_2026_10_02).

Contenu

Les données ETT ne sont pas incluses (CC BY-ND 4.0, téléchargées par tools/make_windows.py). Le code source du build TAKT n’est pas publié : nous livrons des builds.

Texte source : README.fr.md

Telegram