GPUが元を取るとき:パラメータスイープのルーフライン——見出しの167倍は実は27倍のアルゴリズム×6.2倍のハードウェア
「幻想なきバックテスト」シリーズの一篇。
速度の梯子は、あえて座りの悪い調子で幕を閉じました。私たちは80コンボのパラメータスイープを、ラップトップCPU上でpandasの69.9秒から並列numbaの0.23秒まで——測定された298倍——引き下げ、そのうえでGPUは欠けている段ではないと論じました。コメント欄はこれを黙って受け入れはしませんでしたし、それは正しい反応でした。「欠けている段ではない」というのは、1つのワークロードについて、1つのサイズでの主張です。自然法則ではありません。ですから本記事は正直な手を打ち、GPUを試験台に載せます。
その結果は判決ではなく、曲線です。GPUのCPUに対する優位は、スライドに印字できる単一の数字ではありません。1回の呼び出しでどれだけの作業を手渡すかの関数です。私たちのマルチタイムフレームのインジケーター事前計算では、GPUのCPUエンジンに対する高速化率は、1度に1つのパラメータ組合せを要求したときの54.5倍から、一度に61個を要求したときの359.6倍まで変化します。同じカーネル、同じデータ、同じハードウェア——変わったのはバッチだけです。これらの数字の一方を報告して他方を隠すベンチマークは、GPUを測定しているのではなく、見出しを選んでいるのです。
そして359.6倍でさえ、見かけ通りではありません。剥がしてみれば、マルチタイムフレームの大きな見出しである167倍は、27倍のアルゴリズム——CPUをも27倍速くする書き換え——×6.2倍の実際のハードウェアに分解されます。 GPUがその27倍をやったのではありません。数式がやったのです。本記事は、その2つを見分けることについての話です。なぜなら両者を混同することこそ、git commitが無料で解決したはずの問題を解くために2,000ドルのグラフィックカードが売られていく道筋だからです。
出所:以下のすべての数字は、Apple M2 Max上で測定されています——MLX経由でMetal GPU上のfp32を、12個のCPUスレッド上のfp64 numbaと対比し、私たちのengine_multitf_gpu.pyおよびbench_param_sweep.pyのベンチマークスクリプトから取得したもので、それぞれGPU経路とCPU経路が同じトレードを生成することを確認する等価性チェックでゲートされています。今回については公開の姉妹論文はありません——スクリプトそのものが記録です。
問いはベンチマークではなくルーフラインである

「GPUはどれだけ速いのか」に単一の答えがない理由は、ルーフラインモデル(Williams, Waterman & Patterson, 2009)にあります。プロセッサが達成可能な性能は、2つの上限によって同時に頭打ちになります:ピーク計算性能(FLOP/s)によって定まる平坦な上限と、メモリ帯域幅×演算強度——移動する1バイトあたりに行う有用な演算の数——によって定まる傾斜した上限です。1バイトあたりが安価な作業は傾斜した上限の下に位置し、帯域幅律速です。1バイトあたりが濃密な作業は平坦な上限に達し、計算律速です。GPUの平坦な上限はCPUのそれを遥かに凌ぎますが、その傾斜した上限と、決定的なことに、その呼び出しごとの固定コストは、それほど優雅にはスケールダウンしません。
パラメータスイープでは、チャートの左側を第3の項が支配します:起動と転送のオーバーヘッドです。すべてのGPU呼び出しは、おおよそ固定の代価を払います——カーネルをディスパッチし、入力を(Apple Siliconでは統合されているものの、無料ではない)メモリ境界を越えて移動させ、結果を戻す。個の組合せのバッチの実時間を次のようにモデル化します:
ここではその固定オーバーヘッド、はGPUのコンボあたりの限界コスト、はCPUのそれです。すると高速化率は
この1つの分数が記事全体を説明します。が小さいとき、分母のが比を押し潰します——GPUを起こすために代価を払ったのに、ほとんど使わなかったのです。が大きくなるにつれてをより多くのコンボにわたって償却し、高速化率はその漸近値、すなわち真のハードウェア比に向かって登っていきます。中間点はにあります:これはバッチ空間の「リッジポイント」であり、GPUがその実力の半分に達するためにさえ、スイープがどれほど広くなければならないかを教えてくれます。数十コンボのスイープは、そのリッジの遥か左に位置します。数千コンボのスイープは、GPUがついにその上限を勝ち取る平坦な部分に位置します。
ですから正しい問いは決して「GPUは速いか」ではありません。「私のスイープはリッジの右にあるか、そして私のコンボあたりの作業は、そこに到達したとき平坦な上限に届くほど計算律速か」です。両方が真でなければなりません。本記事の残りは、それらの閾値が実際にどこに落ちるのかを測定します。
シングルタイムフレームの評決:GPUはかろうじて勝つ

速度の梯子が使ったワークロードから始めましょう:シングルタイムフレームのHMA/HMA3スイープ、150,000本のバーにわたる80通りの組合せです。私たちはその梯子に6段目を加えました——M5、MLX経由でApple GPU上のインジケーター、トレードは依然としてCPUで抽出します。ウォームアップ済み、3回中ベスト、等価性ゲートは緑:
| 手法 | 実時間 | pandas比 | Combos/s |
|---|---|---|---|
| M0 pandas + ループ | 287.08s | 1.0x | 0.3 |
| M2 numba(1コア) | 2.02s | 142x | 39.7 |
| M4 mp + numba(12コア) | 0.33s | 883x | 245.9 |
| M5 MLX GPU(fp32) | 0.10s | 2796x | 779.2 |
素朴な逐次ベースラインと比べれば、GPUは英雄的に見えます——2,796倍。しかしそれは誠実な人なら誰もするべきでない比較です:良質なGPU実装を最悪のCPU実装と対戦させているのです。実際にデプロイするであろうCPU——12コアすべてで同じカーネルを走らせるM4——にGPUを並べれば、勝ち分は地味な3.2倍(毎秒779対246コンボ)に崩れ落ちます。グラフィックカード1枚まるごとが、スイープ全体を走らせて、12コアのCPUプールに3倍の差で勝つのです。
3.2という数字は無ではありません。しかしそれは誰かがGPUを買う理由でもありません。そしてそれは、これほど狭いスイープについてルーフラインが予測する通りのものです:80コンボはリッジの左です。固定の起動・転送オーバーヘッドは、0.10秒のジョブの中では依然として無視できない一片を占めるため、私たちは決して漸近値には到達しません。さらに悪いことに、コンボあたりのコストの一部はO(n)のトレード抽出パスであり、私たちはこれを意図的にCPUに残しました——GPUがまったく加速できない項です(その理由は次のセクションで詳述します)。このサイズのシングルタイムフレームの研究ループについては、速度の梯子の当初の評決が有効です:GPUは欠けている段ではありません。 並列numbaはすでにあなたを0.23〜0.33秒に到達させており、それを0.10まで削っても、研究者の詰まりを解消するものではありません。スイープを取り巻くオーケストレーションこそがそれです。
その評決で興味深い言葉はこのサイズです。バッチ軸に沿って右に動けば、話は変わります。
コストが実際にどこにあるか
バッチをスケールアップする前に、私たちが実際に何に対して代価を払っているのかを見てみましょう。なぜならルーフラインは、高価な部分が計算律速である場合にのみ報いてくれるからです。スイープをプロファイルすると、そのほとんどすべてが1つのこと:加重移動平均の畳み込みです。HMAは3つのWMA、HMA3は4つ。すべての組合せがそれらを系列全体にわたって再実行します。トレード抽出——2本のインジケーター配列を辿り、hma - hma3の符号反転を見つけ、約定を記帳する——は、1回の安価なO(n)パスです。このスイープは、取引戦略の衣装を着た畳み込みワークロードなのです。
この分割は、まさにルーフラインの2つのレジームです:
- 畳み込みは計算律速です。 各価格は多数の重なり合うウィンドウ和に読み込まれるため、演算強度——移動バイトあたりの演算数——は高くなります。この作業は平坦な計算上限に手を伸ばし、そしてGPUの平坦な上限こそが聳え立つものです。さらに良いことに、ウィンドウは組合せをまたいで重なります:長さ40のWMAは数十のコンボにとって有用なので、バッチ化された事前計算はそれをコンボごとに再計算する代わりに一度だけ共有します。バッチ化は起動オーバーヘッドを償却するだけではありません。ロードを再利用することで演算強度を高めます。これがGPUに属する部分です。
- トレード抽出は帯域幅律速で分岐だらけです。 1回の逐次パス、クロスするたびのデータ依存分岐、実質的に再利用なし。その演算強度は床の近くにあり、その制御フローはSIMDデバイスにとって敵対的です。これをGPUに押し込んでも得るものは少なく、コストは大きい。だからCPUに留まります。つまりこれはAmdahlの法則の逐次テール——GPUの高速化が決して貫けない固定された床であり、シングルTFの数字が3.2倍で飽和した理由の一部です。
このカーネルのマルチタイムフレーム版には、より鋭い第2の教訓が隠れており、それが私たちが説明すると約束し続けている27倍の源です。マルチTFエンジンは、より上位のタイムフレームのHMAを、ルックアヘッドなしでベースの1分足インデックスにアラインします。素直に書けば、これはバーごとにO(length^1.5)の作業です——ベースのバーごとに上位TFの移動平均を再計算するのです。しかしアラインされたHMAは、直近の数本の確定済み上位TFキャンドルの短いバッファと進行中のクローズに対して線形なので、バーごとの計算全体が固定の重みベクトルに畳み込まれます:確定済みキャンドルの系列に対する1回のconv1dと、それに続くO(n)のギャザーです。数億回の冗長な演算が、遥かに短い系列に対する1回の畳み込みになるのです。
その畳み込みはアルゴリズム的な勝利であって、ハードウェアの勝利ではありません。より良い数式です。GPU上で動き、そしてCPU上でも同じくらいよく動きます——np.correlateとギャザー、fp64で。これをしっかり心に留めてください:マルチTFの見出しにおける最大の単一要因は、GPUをまったく持たないマシンでも利用できる書き換えなのです。私たちがついに167倍を分解するとき、これが27倍です。
優位はバッチサイズとともに拡大する

さて、ルーフラインが私たちに行うよう告げた測定です。高価な軸——1分足のベース系列に対するアラインされた上位タイムフレームのHMA事前計算、私たちが持つ最長のキャンドルストリーム——を取り、呼び出しごとに増加する数の長さ組合せをGPUに与えます、。ここでのCPUベースラインは正直な本番エンジンです:12コアすべてにわたるprangeつきのnumba。各バッチについて両方を測定し、その比を取ります。
| バッチ (コンボ/呼び出し) | 12コアCPUエンジンに対するGPU高速化率 |
|---|---|
| 1 | 54.5x |
| 2 | 102.5x |
| 4 | 129.5x |
| 8 | 187.4x |
| 16 | 267.4x |
| 32 | 245.0x |
| 61 | 359.6x |
これはを実際の測定値で描いたものです。で、GPUはすでに54.5倍先行しています——この比較は素朴なバーごとのエンジンに対するものなので、アルゴリズムの畳み込みが1コンボでも織り込まれているからです——が、その上限には程遠い:固定オーバーヘッドが依然として1コンボの呼び出しを支配しています。バッチを倍にすれば高速化率はほぼ倍の102.5倍になります。では267.4倍、では359.6倍で、目に見えてなお登り続けています。優位は問題のサイズとともに拡大します。 これがGPUとパラメータスイープについての最も重要な唯一の文であり、GPUの高速化が通常引用される仕方——あたかもチップの一定の性質であるかのように——とは正反対です。
正直な注記を2つ。なぜならこれは幻想なきバックテストシリーズであり、きれいな単調増加の表は常に疑いに値するからです。
第一に、くぼみ:は245.0倍を示し、の267.4倍を下回っています。これは絨毯の下に掃き込むべきノイズではありません——チャンク境界のアーティファクトです。私たちのconv1dは32個の長さを1つのカーネルチャンクに詰め込むため、はちょうど1チャンクを余裕なく満たし、一方は2つ目のチャンクに溢れ出て、たまたまデバイスをよりよく飽和させます。ルーフラインの要点はトレンドであり、実機には量子化のステップが存在します。私たちはその揺らぎの周りを都合よく選び取るのではなく、揺らぎをそのまま報告します。
第二に、そしてより重要なこと:54.5倍も359.6倍も、どちらも素朴なCPUエンジンに対するものであり、どちらもハードウェアの勝利ではありません。 どちらの数字も依然として27倍のアルゴリズム的畳み込みを含んでいます。CPUベースラインを畳み込まれたアルゴリズムをCPU上で走らせたもの——同じ数式、fp64、両側とも最適化——に差し替えれば、すべての行がおおよそその係数だけ縮むでしょう。それこそが、次のセクションが厳密にする分解です。
正直な分解:27倍のアルゴリズム×6.2倍のハードウェア

アルゴリズムをシリコンから切り離すには、同じマルチタイムフレームのグリッド上で、2つではなく3つの経路を測定しなければなりません。そこでベンチマークは次を実行します:
- cpu-engine — 本番のnumbaエンジン、全コアにわたるバーごとのアラインされたHMA。素朴だが並列なベースライン。
- cpu-collapsed — 畳み込まれた重みベクトル、
np.correlateとギャザー、fp64、CPU上。(1)と同じハードウェア、より良いアルゴリズム。 - gpu-mlx — 畳み込まれた重みをMetal GPU上でバッチ化された
conv1dとして、fp32。(2)と同じアルゴリズム、異なるハードウェア。
それらを並べれば、マルチタイムフレームの見出し全体がきれいに因数分解されます:
左の係数、27倍はアルゴリズムです——前のセクションのバーごとから畳み込みへの畳み込みです。GPUとは何の関係もありません。numpyで実装すれば、あなたのラップトップCPUはこのワークロードでリファクタリングの代価だけで27倍速くなります。右の係数、6.2倍はハードウェアです——12個のCPUコア上の同じ最適化されたアルゴリズムに対する、Metal GPUの正直で同条件の勝利です。その6.2倍こそが、あなたが実際にGPUを必要とした唯一の部分です。
これは記事の教訓全体を算術として述べたものです。ベンダーのベンチマーク、ライブラリのREADME、あるいは興奮した同僚が「GPUで167倍」を見せてきたとき、条件反射は1つの問いであるべきです:CPUベースラインは何だったのか? もしベースラインが素朴な実装だったなら——そしてほとんど常にそうです、なぜなら遅いベースラインはより見栄えのするスライドを作るからです——見出しのほとんどはCPUもまた享受する権利のあったアルゴリズム的勝利であり、ハードウェアはその残余だけです。ここでは残余は6.2倍。167倍という引用は、ハードウェアの貢献をおよそ27倍も過大に表現しています。
そしてハードウェア係数それ自体が問題サイズとともに動いたことに注目してください。小さなシングルタイムフレームのスイープでは、真のGPU対最良CPUの勝利は3.2倍でした。より大きなマルチタイムフレームの事前計算では6.2倍——同じ2つのチップ、ほぼ倍のハードウェア優位で、その理由は純粋に、より大きなワークロードがCPUが歩調を合わせられるより先に、ルーフラインをGPUの平坦な計算上限に向かってさらに上へ押し上げるからです。ハードウェアの優位もまた定数ではありません。それは同じ上昇曲線の上の1点であり、その曲線上で右に動く方法は、バッチをより大きく、コンボあたりの作業をより濃密にすることです。
判断ガイド:スイープはどれほど広くなければならないか?

ルーフラインを、お金を使う前に下せる判断へと畳み直しましょう。GPUが元を取るのは、ルーフラインの2つの条件が同時に成り立つときです:あなたのスイープがバッチリッジの右にあり(、つまり固定の起動・転送オーバーヘッドが償却される)、かつあなたのコンボあたりの作業が計算律速であること(平坦な上限に届くほど十分に濃密な演算強度で、薄いO(n)パスではないこと)。具体的に、私たちが測定したものから:
- シングルタイムフレーム戦略の数十コンボ:GPUは見送りましょう。 あなたはリッジの左にいます。並列numbaに対する正直な勝利は、すでに10分の1秒しかかからないジョブで約3.2倍です。ボトルネックはカーネルではなく、その周りのすべてです。
- 数千コンボ、あるいは正真正銘のマルチタイムフレーム/マルチインジケーターの事前計算:GPUはその居場所を勝ち取ります。 オーバーヘッドは償却され、共有された畳み込みが演算強度を高め、ハードウェアの勝利は6.2倍まで登り、バッチとともに上昇し続けます。これは、GPUが一晩がかりのスイープをコーヒーブレイクに変えるレジームです。
- まずCPUの梯子を登りましょう——そのほうが安く、そして先に来ます。 CPUでの298倍と27倍のアルゴリズム的畳み込みは無料かほぼ無料であり、それらは代替ではなく前提条件です:GPUの6.2倍は、どのみち書かなければならなかった畳み込まれたアルゴリズムの上に乗っています。素朴なパイプラインにボルト留めされたGPUは、ほとんど素朴さを測っているだけです。
GPU分岐には、速度とは何の関係もない税金もあり、それを織り込まなければなりません:AppleのMetal GPUにはfp64がまったくありません。 すべてがfp32、相対精度約1.2e-7で動きます。これは高速移動平均の教科書的なトリック——O(n)のプレフィックス和WMA——を殺します。なぜなら150k本のバーにわたる30,000近い価格スケールでは、累積和が約1e14に達し、fp32の安全な整数範囲を7桁も超えるからです。私たちは最大約2e2の相対誤差(2パーセントではなく200倍)を測定しました。機能する定式化は直接の窓付き畳み込みで、そこでは各ウィンドウ和が同程度の大きさの有界個の項からなり、fp32は約8e-7の精度を保ちます。それでもなお、hma - hma3の符号で判断する戦略は、2本の曲線がほぼ触れ合う際どいバーで、fp32の丸めが僅差を傾けるために、時折クロスを反転させます。だからこそGPU経路は、決して提供できないビット単位で同一の出力を主張する代わりに、トレードがどれだけ乖離したかを測定する等価性ゲート——ベーシスポイント単位のPnLデルタ、トレード数の相対的なシフト——とともに出荷されます。私たちの実行では、その乖離は479,016件中90件のシフトした約定(0.019%)で、許容範囲の十分内側でしたが、負担は実在します:GPUに行くということは、単に速いクロックを得ることではなく、数値パリティの物語を引き受けることを意味します。 そのエンジニアリングコストもまた損益分岐点の一部です。
数字はApple型だが、曲線はそうではない
上のすべての数値はApple M2 Maxのものです:GPUとCPUが1つのプールを共有する統合メモリデバイスであり、倍精度を持たないfp32専用のGPUです。ディスクリートなNVIDIAやAMDのカードは定数を変えますが、それぞれがどちらの方向に動くかを明示する価値があります。なぜなら、数字は生き残らなくても、議論の形は生き残るからです。
- 転送オーバーヘッドは、良くなるのではなく悪化します。 ディスクリートなカードはPCIeの向こうに座るため、入力と結果は統合メモリが回避するバスを越えた本物のコピーを行います。それはバッチリッジを右へ押しやります——ディスクリートGPUがその起動を償却するには、さらに広いスイープが必要です。ルーフラインの左端は、PCIeデバイスでは緩やかになるのではなく、より急峻になります。
- 平坦な上限は高くなります。 データセンターGPUは統合型よりも遥かに多くのFLOP/sと帯域幅を持つため、飽和するスイープでの漸近的なハードウェアの勝利は、私たちの6.2倍より大きくなります。曲線の右側に到達する報酬は増え、左側に居座る代償もまた増えます。
- fp64が戻り、それとともにプレフィックス和のトリックも戻ります。 本物の倍精度を持つカードでは、O(n)のプレフィックス和WMAが再び実行可能になり、パリティゲートをビット単位の厳密さに向けて締め上げられます。私たちが払った特定のfp32税——プレフィックス和ではなく直接畳み込み、assertではなく乖離を測定するゲート——は、法則ではなくApple Siliconの細部です。
これらのいずれも論旨を変えません。どんなデバイスでも、:償却しなければならない固定オーバーヘッド、右からのみ近づく漸近値です。定数はハードウェア、曲線は算術です。誰かの見出し——私たちのものも含めて——を信じる前に、5行のバッチスイープで自分自身の、、を測定してください。
これがどこにつながるか
これは、バックテストの速度が実際にどこから来るのかについての小さなサブシリーズにおける4番目の測定であり、その断片はお金を使う前に何を最適化すべきかについての単一の議論として組み合わさります:
- 速度の梯子はpandasから並列numbaまで登り、CPUだけで298倍を達成し、GPUを未解決の問いとして残しました。本記事はそれに答えます:GPUは実在するが条件付きの5段目であり、最上段のCPUに対して3.2倍〜6.2倍の価値があり、しかもスイープがルーフラインを登れるほど十分に広い場合に限ります。
- IPC税は同じ動きを逆方向に行い——プロセスを出ることのコストを測定し——同じ形の結論に達しました:境界(ソケット、GPU起動)は安い。税金はどれだけ頻繁に、どれだけチャッティに越えるかにある。IPCをバッチ化するのと同じ理由でGPU呼び出しをバッチ化しましょう:越境ごとの固定コストを償却するために。
- 集約されたparquetキャッシュは、GPUの事前計算が行うことのCPU側バージョンです——共有されたインジケーターを一度計算し、すべてのコンボにわたって再利用する。GPUは、その再利用とバッチ化の原理をシリコンへと持ち込むだけです。
- そしてfp32パリティゲートは、バックテスト-ライブパリティ問題の縮図です:あなたの高速経路がリファレンスとわずかに異なる何かを計算した瞬間、あなたは手を振ってごまかすのではなく、乖離の定量化された説明を負うことになります。
つなぎとなる規律は、このシリーズ全体が推し進めるものと同じです:あなたが実際に売られているものを測定せよ。高速化は比であり、比には分子と分母があります。GPUの失望のほとんどは、見栄えよく選ばれた分母——CPUベースライン——から来ており、GPUの無駄のほとんどは、ルーフラインの左端を離れるには小さすぎるスイープを走らせることから来ています。
まとめ
- GPUの高速化は数字ではなく曲線です。 私たちのマルチタイムフレームの事前計算では、CPUに対する優位は、呼び出しあたり1コンボの54.5倍から61コンボの359.6倍まで変化しました——同じチップ、同じデータ。どんな単一の数値もその曲線上の1点です。それがどのバッチサイズで測定されたのかを問いましょう。
- 常にCPUベースラインを問い詰めましょう。 167倍というマルチタイムフレームの見出しは、27倍のアルゴリズム(バーごとから畳み込みへの畳み込みで、CPUを同じだけ速くする)×6.2倍の実際のハードウェアにきれいに因数分解されます。ここでの公正なGPU対最良CPUの勝利は、シングルタイムフレームで3.2倍、マルチで6.2倍——167倍ではありません。
- 優位は問題サイズとともに拡大し、ハードウェア係数もまた拡大します。 より大きなバッチとより濃密なコンボあたりの作業が、あなたをルーフラインの上へ押し上げます:正直なハードウェアの勝利それ自体が、ワークロードを大きくしただけで3.2倍から6.2倍へと上昇しました。小さなスイープはリッジの左に位置し、ほとんど恩恵を受けません。
- まずアルゴリズムを直し、CPUの梯子を登りましょう——GPUの勝利はそれらの代わりではなく、その上に乗ります。 6.2倍は、どのみち書かなければならなかった畳み込まれたアルゴリズムに対して測定されています。素朴なパイプラインにGPUをボルト留めすれば、あなたが測るもののほとんどはシリコンではなく素朴さです。
- GPUに行くことは、数値パリティの物語を引き受けることを意味します。 Metalにfp64はなく、プレフィックス和WMAのトリックは価格スケールで死に(相対誤差約2e2)、符号クロス戦略は際どいバーで反転します。乖離をベーシスポイント単位で定量化する等価性ゲートを出荷しましょう。そのエンジニアリングコストを損益分岐点に数え入れてください。
誰かがGPUで自分のバックテストが100倍速くなったと言ってきたとき、彼らはあなたにほとんど何も伝えていません。バッチサイズとCPUベースラインを尋ねてみれば、その100倍はたいてい、無料で手に入れられたはずのアルゴリズム的勝利に包まれた1桁のハードウェアの勝利へと解けていきます——十分に大きなスイープでなら、ルーフラインが言う通りの理由で、そしてそれ以上のいかなる理由でもなく、手に入れる価値のあるものです。
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.