ARMプロセッサの文字列からスペースをすばやく削除する-代替分析

オリジナル記事

投稿者:Martin Krastev







私の友人はhabrahabr.ruの興味深い記事に注目しました-Daniel Lemira による記事のロシア語の翻訳 ARMプロセッサの文字列からのスペースの高速削除 。 この記事は2つの理由で興味をそそられました。まず、誰かが実際に非x86アーキテクチャの一般的な問題に対する最適な解決策を見つけるために時間と労力を費やしました(hooray!)。私を困惑させました:Intelの6倍の利点について? 著者は、この単純なタスクにおいて、ARMが「クロックあたりの効率」とIntelの「ビッグアイアン」の比率に非常に大きいという明確な結論を下しました。







電話受付中!







しかし、最初から始めましょう! 著者は、ある種のベースライン-順次実装から始めたので、そこから始めて上に進むことも決めました。 より大きな混乱のために、この基本testee00



を呼び出しましょう:







 inline void testee00() { size_t i = 0, pos = 0; while (i < 16) { const char c = input[i++]; output[pos] = c; pos += (c > 32 ? 1 : 0); } }
      
      





さまざまなバージョンのGCCおよびClangコンパイラを使用して、いくつかのamd64プロセッサと1つのarm64プロセッサでtestee00を実行し、常に最良のコンパイル結果をベースにしています。 以下は、 perf -e cycles



を処理された文字数(この場合は5 10 ^ 7 16)で除算し、小数点以下4桁目で切り捨てたビート/シンボル比の結果です。







CPU コンパイラとフラグ 時計/キャラクター
Intel Xeon E5-2687W(SNB) g ++-4.8 -Ofast 1.6363
Intel Xeon E3-1270v2(IVB) g ++-5.1 -Ofast 1.6186
Intel i7-5820K(HSW) g ++-4.8 -Ofast 1.5223
AMD Ryzen 7 1700(Zen) g ++-5.4 -Ofast 1.4113
マーベル8040(Cortex-A72) g ++-5.4 -Ofast 1.3805


表1.デスクトップコアでのtestee00









面白いですね。 小さな携帯電話チップ(3-Decoder OoO)は、大きなデスクトップチップよりも優れたクロック/シンボル比を提供します(この記事の最後で、実際の統計を見ることができます)。







それでは、SIMDに移りましょう。 NEONで経験豊富なエンコーダーと見なされるふりはしませんが、時々ARM SIMDに悩まされます。 読者を怖がらせないために、この記事の本文ではSIMDルーチンをインライン化しません。 代わりに、すべてのテストコードと参加するテスト手順は、この記事の最後にあります。







私はダニエルの最初のSSSE3プルーニング手順を変更する自由を取りました-実際、テストには私のバージョンを使用しました。 理由は? コードで2 ^ 16 * 2 ^ 4 = 1 MBのルックアップテーブルを取得することはできません-これは、asciiスレッドをトリムするだけでなく、サブルーチンを呼び出すと他の作業が簡単になるシナリオでは大きなキャッシュイーターになります。 LSSなしのSSSE3バージョンには、いくつかの計算の価格が付属しますが、レジスタでのみ機能します。また、ご覧のように、テーブルを除外するための価格は、大量のクロッピング負荷があっても法外ではありません。 さらに、SSSE3の新しいバージョンとNEONのバージョン(ASIMD2)は同じアルゴリズムを使用しているため、物理的に可能な限り直接比較できます。







CPU コンパイラとフラグ 時計/キャラクター
Intel Xeon E5-2687W(SNB) g ++-4.8 -Ofast -mssse3 .4230
Intel Xeon E3-1270v2(IVB) g ++-5.4 -Ofast -mssse3 .3774
マーベル8040(Cortex-A72) g ++-5.4 -Ofast -mcpu = cortex-a57 1.0503


表2.デスクトップコアでのtestee01



パフォーマンス







注:A57のマイクロアーキテクチャチューニングはarm64ビルドに転送されます。これは、コンパイラからのデフォルトスケジューラがNEONコードに関してこのバージョンでは明らかに悪いためであり、A57は計画に関してはARMv8のかなり「一般的な」共通分母です。







ご覧のとおり、クロックあたりの効率は2倍で、サンディブリッジが有利です。コアは、同じ(または同様の)ファブノードで、エリアA72で4倍大きくなります。 したがって、すべてが電話チップにとってそれほど悪くはありません。 )







ボーナスマテリアル:小さなarm64およびamd64 CPでの同じテスト:







CPU コンパイラとフラグ クロック/キャラクター、スカラー 時計/キャラクター、ベクトル
AMD C60(ボブキャット) g ++-4.8 -Ofast -mssse3 3.5751 1.8215
MediaTek MT8163(Cortex-A53) clang ++-3.6 -march = armv8-a -mtune = cortex-a53 -Ofast 2.6568 1.7100


表3.エントリーレベルのコアのtestee01



パフォーマンスtestee00












Xeon E5-2687W @ 3.10GHz







スカラー版







 $ g++-4.8 prune.cpp -Ofast $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 421.886991 task-clock (msec) # 0.998 CPUs utilized 1,309,087,898 cycles # 3.103 GHz 4,603,132,268 instructions # 3.52 insns per cycle 0.422602570 seconds time elapsed $ echo "scale=4; 1309087898 / (5 * 10^7 * 16)" | bc 1.6363
      
      





SSSE3バージョン(16のバッチ、不整合書き込み)







 $ g++-4.8 prune.cpp -Ofast -mssse3 $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234a Performance counter stats for './a.out': 109.063426 task-clock (msec) # 0.997 CPUs utilized 338,414,215 cycles # 3.103 GHz 1,052,118,398 instructions # 3.11 insns per cycle 0.109422808 seconds time elapsed $ echo "scale=4; 338414215 / (5 * 10^7 * 16)" | bc .4230
      
      








Xeon E3-1270v2 @ 1.60GHz







スカラー版







 $ g++-5 -Ofast prune.cpp $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 810.515709 task-clock (msec) # 0.999 CPUs utilized 1,294,903,960 cycles # 1.598 GHz 4,601,118,631 instructions # 3.55 insns per cycle 0.811646618 seconds time elapsed $ echo "scale=4; 1294903960 / (5 * 10^7 * 16)" | bc 1.6186
      
      





SSSE3バージョン(16のバッチ、不整合書き込み)







 $ g++-5 -Ofast prune.cpp -mssse3 $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234a Performance counter stats for './a.out': 188.995814 task-clock (msec) # 0.997 CPUs utilized 301,931,101 cycles # 1.598 GHz 1,050,607,539 instructions # 3.48 insns per cycle 0.189536527 seconds time elapsed $ echo "scale=4; 301931101 / (5 * 10^7 * 16)" | bc .3774
      
      








Intel i7-5820K







スカラー版







 $ g++-4.8 -Ofast prune.cpp $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 339.202545 task-clock (msec) # 0.997 CPUs utilized 1,204,872,493 cycles # 3.552 GHz 4,602,943,398 instructions # 3.82 insn per cycle 0.340089829 seconds time elapsed $ echo "scale=4; 1204872493 / (5 * 10^7 * 16)" | bc 1.5060
      
      








AMD Ryzen 7 1700







スカラー版







 $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 356,169901 task-clock:u (msec) # 0,999 CPUs utilized 1129098820 cycles:u # 3,170 GHz 4602126161 instructions:u # 4,08 insn per cycle 0,356353748 seconds time elapsed $ echo "scale=4; 1129098820 / (5 * 10^7 * 16)" | bc 1.4113
      
      








マーベルARMADA 8040(Cortex-A72)@ 1.30GHz







スカラー版







 $ g++-5 prune.cpp -Ofast $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 849.549040 task-clock (msec) # 0.999 CPUs utilized 1,104,405,671 cycles # 1.300 GHz 3,251,212,918 instructions # 2.94 insns per cycle 0.850107930 seconds time elapsed $ echo "scale=4; 1104405671 / (5 * 10^7 * 16)" | bc 1.3805
      
      





ASIMD2バージョン(16のバッチ、不整合書き込み)







 $ g++-5 prune.cpp -Ofast -mcpu=cortex-a57 -mtune=cortex-a57 $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 646.394560 task-clock (msec) # 0.999 CPUs utilized 840,305,966 cycles # 1.300 GHz 801,000,092 instructions # 0.95 insns per cycle 0.646946289 seconds time elapsed $ echo "scale=4; 840305966 / (5 * 10^7 * 16)" | bc 1.0503
      
      





ASIMD2バージョン(32バッチ、不整合書き込み)







 $ clang++-3.7 prune.cpp -Ofast -mcpu=cortex-a57 -mtune=cortex-a57 $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 1140.643640 task-clock (msec) # 0.999 CPUs utilized 1,482,826,308 cycles # 1.300 GHz 1,504,011,807 instructions # 1.01 insns per cycle 1.141241760 seconds time elapsed $ echo "scale=4; 1482826308 / (5 * 10^7 * 32)" | bc .9267
      
      








AMD C60(ボブキャット)@ 1.333GHz







スカラー版







 $ g++-4.8 prune.cpp -Ofast $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234 Performance counter stats for './a.out': 2208.190651 task-clock (msec) # 0.997 CPUs utilized 2,860,081,604 cycles # 1.295 GHz 4,602,968,860 instructions # 1.61 insns per cycle 2.214173331 seconds time elapsed $ echo "scale=4; 2860081604 / (5 * 10^7 * 16)" | bc 3.5751
      
      





SSSE3バージョン(16のバッチ、不整合書き込み)







 $ clang++-3.5 prune.cpp -Ofast -mssse3 $ perf stat -e task-clock,cycles,instructions -- ./a.out alabalanica1234a Performance counter stats for './a.out': 1098.519499 task-clock (msec) # 0.998 CPUs utilized 1,457,266,396 cycles # 1.327 GHz 1,053,073,591 instructions # 0.72 insns per cycle 1.101240320 seconds time elapsed $ echo "scale=4; 1457266396 / (5 * 10^7 * 16)" | bc 1.8215
      
      








MediaTek MT8163(Cortex-A53)@ 1.50GHz(sans perf)







スカラー版







 $ ../clang+llvm-3.6.2-aarch64-linux-gnu/bin/clang++ prune.cpp -march=armv8-a -mtune=cortex-a53 -Ofast $ time ./a.out alabalanica1234 real 0m1.417s user 0m1.410s sys 0m0.000s $ echo "scale=4; 1.417 * 1.5 * 10^9 / (5 * 10^7 * 16)" | bc 2.6568
      
      





ASIMD2バージョン(16のバッチ、不整合書き込み)







 $ ../clang+llvm-3.6.2-aarch64-linux-gnu/bin/clang++ prune.cpp -march=armv8-a -mtune=cortex-a53 -Ofast $ time ./a.out alabalanica1234 real 0m0.912s user 0m0.900s sys 0m0.000s $ echo "scale=4; 0.912 * 1.5 * 10^9 / (5 * 10^7 * 16)" | bc 1.7100
      
      





マーティン・クラステフ。







翻訳:ドミトリー・アレクサンドロフ








All Articles