18

以前にファイルから読み取った行列に対してステンシル計算を実行しています。2 種類の行列 (NonZero タイプと Zero タイプ) を使用します。どちらのタイプも境界の値 (通常は 1000) を共有しますが、残りの要素は Zero タイプの場合は 0、NonZero タイプの場合は 1 です。

このコードは、ファイルの行列を同じサイズの 2 つの割り当てられた行列に格納します。次に、1 つの行列のすべての要素で、それ自体の値と隣接要素の値を使用して演算を実行し (x 4 と mul x 1 を加算)、結果を 2 番目の行列に格納します。計算が完了すると、行列のポインターが交換され、同じ操作が有限回実行されます。ここにコアコードがあります:

#define GET(I,J) rMat[(I)*cols + (J)]
#define PUT(I,J) wMat[(I)*cols + (J)]

for (cur_time=0; cur_time<timeSteps; cur_time++) {
    for (i=1; i<rows-1; i++) {
        for (j=1; j<cols-1; j++) {
            PUT(i,j) = 0.2f*(GET(i-1,j) + GET(i,j-1) + GET(i,j) + GET(i,j+1) + GET(i+1,j));
        }
    }
    // Change pointers for next iteration
    auxP = wMat;
    wMat = rMat;
    rMat = auxP;
}

私が公開しているケースでは、固定量の 500 timeSteps (外側の反復) と 8192 行と 8192 列のマトリックス サイズを使用していますが、timeSteps の数またはマトリックス サイズを変更しても問題は解決しません。アルゴリズムのこの具体的な部分の時間のみを測定することに注意してください。そのため、ファイルからの行列の読み取りやその他の時間測定には影響しません。

それが起こるのは、使用するマトリックスのタイプに応じて異なる時間が得られ、Zero タイプを使用するとパフォーマンスが大幅に低下することです (他のすべてのマトリックスは NonZero タイプと同じように動作します。値)。

私はそれが乗算演算であることを確信しています。それを削除して加算のみを残した場合、それらは同じように実行されます。ゼロ行列タイプでは、ほとんどのタイプで合計の結果が 0 になるため、演算は「0.2*0」になることに注意してください。

浮動小数点演算はオペランドの値とは無関係であると考えていたので、この動作は確かに奇妙です。ここではそうではありません。問題が発生した場合に備えて、SIGFPE例外をキャプチャして表示しようとしましたが、結果は得られませんでした。

参考までに、Intel Nehalem プロセッサと gcc 4.4.3 を使用しています。

4

2 に答える 2

19

問題はすでにほとんど診断されていますが、ここで何が起こるかを正確に書きます。

基本的に、質問者は拡散のモデリングです。境界上の初期量は、大きなグリッド全体に拡散します。各タイム ステップ t で、拡散のリーディング エッジでの値は 0.2^t になります (コーナーでの影響は無視されます)。

正規化された単精度の最小値は 2^-126 です。の場合cur_time = 55、拡散の最前線での値は 0.2^55 で、2^-127 より少し小さいです。この時間ステップ以降、グリッド内の一部のセルに非正規値が含まれます。質問者の Nehalem では、非正規化データに対する操作は、正規化された浮動小数点データに対する同じ操作よりも約 100 倍遅く、速度低下を説明しています。

グリッドが最初に の定数データで満たされている場合1.0、データが小さくなりすぎることはないため、非正規化ストールが回避されます。

データ型を に変更するdoubleと遅延が発生しますが、問題が軽減されるわけではないことに注意してください。計算に倍精度が使用される場合、非正規化値 (現在は 2^-1022 より小さい) は 441 回目の反復で最初に発生します。

拡散の最先端での精度を犠牲にして、「Flush to Zero」を有効にすることでスローダウンを修正できます。これにより、プロセッサは算術演算の非正規化の結果ではなくゼロを生成します。<fenv.h>これは、できればC ライブラリのヘッダーで定義された関数を介して、FPSCR または MXSCR でビットを切り替えることによって行われます。

もう1つの(よりハッカーで、あまり良くない)「修正」は、最初にマトリックスを非常に小さなゼロ以外の値(0x1.0p-126f最小の正規数)で埋めることです。これにより、計算で非正規化が発生するのを防ぐこともできます。

于 2011-03-03T21:14:14.013 に答える
0

おそらく、ZeroMatrix はスパース行列の典型的なストレージ スキームを使用します。つまり、すべての非ゼロ値をリンク リストに格納します。その場合、通常の配列ベースのストレージ スキームよりもパフォーマンスが低下する理由は理解できます。実行する操作ごとにリンク リストを 1 回実行する必要があるからです。その場合、疎行列を持つことを考慮した行列乗算アルゴリズムを使用して、プロセスを高速化できます。そうでない場合は、最小限の完全なコードを投稿してください。

スパース行列を効率的に乗算する可能性の 1 つを次に示します。

http://www.cs.cmu.edu/~scandal/cacm/node9.html

于 2011-03-03T11:40:43.913 に答える