1. Query-diff テストによって発見された問題
Query-diff は、取得側で一般的に使用されるテスト方法であり、同じ取得情報のセットを使用してベースラインを要求するというものです。システムまたはモジュールのバージョンとベータ版。通常、ベースライン バージョンとテスト対象のバージョンの間には、わずかな違い (プログラムの機能/構成など) しかありません。リクエストを送信した後、2 つのバージョンから返された検索結果を比較して、その違いが最終的な計算結果に影響するかどうかを確認します。
この場合、テストされるモジュール A は C++ で書かれており、出力コア データは単精度浮動小数点数であり、Q として記録されます。
モジュール A の特定のアップグレード後にクエリ差分テストを実行すると、Q 値に精度差分があり、その割合は約 1%、最大差分は数万であることが判明しました。このアップグレードは差分なしで行われる予定です。
2.綿密な調査
通常、差分が発生した場合は、まず調査の方向性を明確にする必要があり、一目で原因がわからない場合は、疑わしいオブジェクトを 1 つずつ検証する必要があります。 、範囲を狭め、不必要なエネルギー投資を削減します。したがって、トラブルシューティングの主な方向性として、環境またはプログラムの 2 つがリストされています。
最初に環境を確認します:
私は環境サイトで新旧の環境の構成と語彙を注意深くチェックしました。環境構築ツールに関連する要素を除いて、それらは期待どおりでした。
l このアップグレードは上位互換性があるため、古い環境と新しい環境の構成とボキャブラリーが統合され、再テストされ、差分が再現され、構成の違いが排除されます。
環境に問題はないようですので、検証プロセスに進みましょう:
l 複数セットのテストが実行されたため、検証結果に変化はなく、ランダムな戦略差分の可能性は排除されています。
l デバッグログを出力し、処理の各ステップの中間結果を確認します。Q 値の計算の最後のステップでは diff のみが表示されます。 、などのリスクポイントが順次除外されます。
l 完全に確認するには、古い環境と新しい環境のプログラムを新しいバージョンに直接置き換えて再テストしてください。本当にプログラムが原因である場合は、差分は存在しないはずです。ただし、差分は再び表示されます。明らかにランダムな差分はありません。 ! !
現時点で、ボトルネックの原因は環境とプログラムが間違っているようです。
落ち着いてもう一度考えてください。前のトラブルシューティングでは、使用される構成と語彙として環境の概念を説明しました。この 2 つが同じであれば、環境は同じであると考えられていました。これは一方的なものですが、環境の意味には、システムやハードウェアのコンパイル環境や実行環境も含まれます。そこで、新しい検証アイデアを考えます:
l プログラムの新旧バージョンは両方とも、同社のクラウド コンパイル クラスターを使用して生成されているため、問題はないはずですが、当たり前のことと思われないように、コンパイルを慎重にチェックしました。パラメータを変更し、同じローカル マシン上で古いバージョンと新しいバージョンを再コンパイルし、差分が再発することを確認し、コンパイル要因を排除します。
l 古い環境と新しい環境を同じマシンにコピーし、リクエストを再実行すると、差分は消えます。動作環境要因であることが確認されました
動作環境には、オペレーティング システムとハードウェア レベルが含まれます。どちらも centos 4.3 であり、オペレーティング システムは除外されています
l ハードディスクとメモリ モデルの違いは差分を引き起こす可能性が低いため、まだ検証しません
l マシンの CPU バージョン新しい環境は Xeon E5645 で、古い環境が存在するマシンの CPU バージョンは Xeon E5-2620 です。これは CPU モデルが異なるためだと思われるため、別の環境を見つけました。古い環境と同じ CPU を搭載したマシンで再テストすると、差分が消え、ターゲットが CPU にロックされます。
2. 真実を明らかにする
CPU を分析した後、コア数、最大スレッド数、および第 1、第 2、および第 3 レベルのキャッシュを単純に除外した後、CPU 機能リストの命令セットの違いに注目しました。
補足知識1: CPU命令セットの役割
命令セットは、CPU内部に格納され、CPUの動作を導き最適化するハードプログラムです。これらの命令セットを使用すると、CPU をより効率的に実行できます。命令セットがどのように最適化されるかを説明するには、SISD (単一命令単一データ) と SIMD (単一命令複数データ) という 2 つのテクノロジーについて言及する必要があります。
加算命令を例に挙げます。SISD CPU を使用して加算命令をデコードした後、実行ユニットは最初にメモリにアクセスして最初のオペランドを取得し、次に再びメモリにアクセスして 2 番目のオペランドを取得してから、合計を実行します。手術。 SIMD を使用する CPU では、命令がデコードされた後、複数の実行ユニットが同時にメモリにアクセスし、すべてのオペランドを一度に取得して動作します。この機能により、SIMD はデータ集約型の操作に特に適しています。
CPU命令セットのSSEシリーズとAVXは浮動小数点演算に使用されており、AVXは2つのCPUの違いの1つであり、非常に疑わしいです。次に、プログラムが AVX を使用して最適化されているという証拠を見つける必要があります。
ただし、ASQ モジュールには直接最適化されたコード ロジックはありません。Q 値の計算を含むプログラムは静的 libA インターフェイスを呼び出しますが、libA コードは命令セットを使用しません。ただし、libA は静的 libB をコンパイルしたため、最下層まで追跡したところ、コンパイルの依存関係の 4 番目の層が IDL によって提供された libX であることがわかりました。コードは機密であり、閲覧できませんでした。
関係する RD にアドバイスを求めなければなりませんでした。RD は、libX は SSE 命令の最適化と Intel が提供する数学関数ライブラリ MKL を使用しますが、AVX は使用しないと伝えました。
これはまた行き止まりですか?最後の望みをかけて、Intel での MKL の公式紹介をチェックしたところ、予想外の利点が見つかりました。AVX 最適化が MKL に導入されたのです。 【1】
これで、AVX が diff ソースの犯人であることを確認する最後のステップが完了しました。すぐに、インテルの製品でさらなる証拠が見つかりました [2]: AVX2 の FMA 命令は、行列乗算、ドット積、多項式評価、およびその他の浮動小数点演算において、これまでよりも効率的かつ正確です。 FMA は乗算と累積演算を一度に完了できるため、改善されました。また、それを裏付ける関連技術者からの投稿が公式フォーラムで見つかりました [3]:
補足知識 2: コンピュータにおける浮動小数点数の格納方法
float と double はどちらも、IEEE 仕様に準拠しています。 float は IEEE R32.24 に準拠し、double は R64.53 に準拠します。
単精度か倍精度かに関係なく、ストレージは 3 つの部分に分割されます: 1. 符号ビット (Sign): 0 は正を表し、1 は負を表します 2: 指数データのストレージに使用されます。科学表記法で、シフト格納を使用します 3. 仮数部 (Mantissa): 仮数部 float の格納方法は次の表に示すとおりです。
|
全長 |
仮数部 |
指数部 |
符号ビット |
単精度 |
32bit |
0-22 |
23-30 |
31 |
ダブル |
64ビット |
0-51 |
52-62 |
63 |
拡張倍精度 |
80bit |
0 - 63 |
64-78 |
79 |
ハードウェア レベルでは、CPU の浮動小数点演算ロジックは FPU (浮動小数点演算ユニット) (SSE または AVX のいずれか) に実装されます。FPU のデフォルトの計算精度は 80 ビットですが、SSE と AVX によって出力される浮動小数点演算精度は 80 ビットです。 AVX はそれほど高くありません (両方とも 32 ビットです)。FPU での計算精度に差がある場合 (両方が 32 ビットより大きい場合)、計算出力は 32 ビットに切り捨てられてからメモリに格納されます。近似的な切り捨てのため、必然的に diff になります。
インテルの基盤となるアルゴリズムは機密であるため、AVX と SSE の最適化機能を実装する際に設定された FPU 精度が異なると推測することしかできませんが、精度の違いの結論は確かです。
この時点で真実が明らかになりました: AVX の FMA は SSE よりも 1 ビット正確です。反復計算がある場合、その差は累積されます。 Q 値の生成には複雑な行列演算が行われ、この小さな 1 ビットの差が小数点の 10,000 分の 1 に拡大されます。同時に、インテルは、AVX をサポートしていない CPU 上で実行されるさまざまなマシンの互換性を SSE にダウングレードすることを保証します。
補足知識その3: SSEとAVXを使ってプログラムを最適化する方法
ここでは追加命令を例として、関連するヘッダファイルの紹介やコンパイル命令の作成については紹介していません。
基本バージョン:
単純にループして累積および合計します。
SSE 最適化バージョン
SSE レジスタ 128 ビット、16 バイト、一度に 4 つの単精度浮動小数点数を格納でき、4 つのグループでレジスタに格納でき、組み込みの加算関数を使用して合計します、次に 4 つのグループを追加して追加し、最後にグループ内の残りの項目を追加して最終結果を取得します。
AVX 最適化バージョン
AVX の最適化方法は SSE と似ていますが、AVX レジスタは 256 ビット、32 バイトを使用し、8 つの単精度浮動小数点数をレジスタに格納する必要があります。
次に、入力配列をランダムに生成し、最適化の効果を検証するための簡単なテスト ケースを作成します。以下は、1 秒あたりに蓄積できる浮動小数点数の単位での 3 つのアルゴリズムのパフォーマンスの比較です。その結果、SSE効率は通常バージョンの4倍、AVXは8倍に向上しました。 【4】
2. まとめと啓発
問題の概要:
l Query-diff 互換性テスト中に、モジュール A の古いバージョンと新しいバージョンによって計算された Q 値に diff があることが判明しました。
l 調査後、diff の精度を決定します プログラムの実行環境による CPU がサポートする浮動小数点命令セット (AVX/SSE) の違い
l この場合の割合と絶対値現時点ではオンラインサービスに影響はありませんが、アルゴリズムがさらに複雑になると、パーセンタイルまでの差分を蓄積すると戦略が無効になります。
l 他のモジュールが浮動小数点演算に命令セットの最適化を使用している場合は、同じ問題が存在するかどうかも確認する必要があります。
解決策:
l テストリソースを割り当てるときは、新しい環境と古い環境が配置されているマシンの CPU に一貫性があることを確認します。
l query-diff を実行する前に、環境チェックメカニズムを追加して、環境チェックメカニズムが存在しないことを再度確認します。ハードウェアの違い
l サービスをオンラインで展開する場合は、最適なパフォーマンスと精度を実現するためにマシンが AVX 命令セットをサポートしていることを確認する必要もあります。
l リスクを事前に回避するために、他のモジュールも同様に命令セットを使用して最適化されているかどうかを確認します。
インスピレーションと提案:
l 浮動小数点演算を多用するプログラムでは、SSE/AVX などの命令セット関数を使用してパフォーマンスを最適化することを検討できます。これにより、通常、動作効率が大幅に向上します (SSE: 4 倍、AVX: 8 倍)。
l 命令セットを使用するときは、反復回数の制御 (つまり、命令セット関数の出力を再度命令セット関数の入力として使用) に注意して、精度の差分が許容できないレベルまで蓄積されないようにしてください。無視されます;
l CPU、オペレーティング システム、基本ライブラリなどのアプリケーションに対する基盤となるシステムとハードウェアの違いの影響を比較するなど、クエリ差分テストをより多くの互換性テスト シナリオに適用できます。
ソフトウェア エンジニアリングはハードウェア サポートと切り離せません。コンパイル環境と実行環境の違いにより、サービスのパフォーマンスと最終的な計算結果に違いが生じる可能性があります。このような問題には、開発、テスト、発売のすべての段階で特別な注意が必要です。ソフトウェアとハードウェアを組み合わせるプログラマーになることが重要です!
参考資料:
【1】 https://software.intel.com/zh-cn/articles/whats-new-in-intel-mkl
【2】 https://software.intel.com/ zh-cn/articles/intel-xeon-processor-e7-88004800-v3-product-family-technical-overview
【3】 https://software.intel.com/en-us/forums/topic/507004
【4】 http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html
を要求することです。