非常に長い整数 (10 進数で約 100,000 桁) の乗算の算術演算に取り組んでいます。ライブラリの一部として、2 つの長い数字を追加します。
プロファイリングによると、私のコードは add() および sub() ルーチンで時間の 25% まで実行されるため、可能な限り高速であることが重要です。しかし、私はまだ多くの可能性を見ていません。多分あなたは私に助け、アドバイス、洞察、またはアイデアを与えることができます. 私はそれらをテストし、あなたに戻ってきます。
これまでのところ、私の追加ルーチンはセットアップを行ってから、8 回展開されたループを使用します。
mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
adc rax, r10
mov QWORD PTR [rcx+r11*8-64], rax
異なるオフセットを持つさらに 7 つのブロックが続き、ループします。
以前にメモリから値をロードしようとしましたが、それは役に立ちませんでした。これはプリフェッチが優れているためだと思います。Intel i7-3770 Ivy Bridge 4 コア CPU を使用しています。しかし、最新の CPU で適切に動作するコードを書きたいと思っています。
編集:いくつかのタイミングを取りました:約2.25サイクル/ワードで1kワードを追加します。ADC を削除すると、MOV だけが残り、1 ワードあたり約 1.95 サイクルかかります。したがって、主なボトルネックはメモリアクセスにあるようです。ライブラリmemcpy()
は約 0.65 サイクル/ワードで動作しますが、入力は 2 つではなく 1 つだけです。それでも、SSE レジスタを使用しているため、はるかに高速だと思います。
いくつかの質問:
- 「ロード、ロード、追加、保存」構造を使用することは有用ですか、それとも「ロード、メモリへの追加」が役立ちますか? これまでのところ、私のテストでは利点は見られませんでした。
- いつものように、期待される SSE(2,3,4) からの助けはありませんか?
- アドレッシング (スケーリングされたインデックスとベースとオフセット) は悪影響を及ぼしますか? 代わりに使えます
ADD r11, 8
。 - ループの展開はどうですか?Sandy Bridge アーキテクチャ (Agner Fog http://www.agner.org/optimize/ ) では展開が悪いと読みました。それは優先されるべきですか、それとも回避されるべきですか?
- (編集) SSE レジスタを使用してメモリから大きなチャンクに単語をロードおよび格納し、汎用レジスタおよび SSE レジスタと効率的に単語を交換できますか?
コメントをいただければ幸いです。