まず、使用中のラティス ラッピング ソリューション ( (i+1) & (LATTICE_SIZE-1)
) は、LATTICE_SIZE が 2 のべき乗である場合にのみ適切に機能します。たとえば、期待値が 0である場合LATTICE_SIZE == 100
とi == 99
、
の場合です。(i+1)&(LATTICE_SIZE-1) == 100 & 99 == 0x64 & 0x63 == 0x60 == 96
そのため、多次元配列のインデックス作成がコンパイラとプラットフォームでどのように機能するかを確認することをお勧めします。LATTICE_SIZE が 2 の累乗に等しい場合、n 番目のインデックスの乗算は、一部のアーキテクチャでは大幅に高速な左シフトに効果的に置き換えることができます。VC++11 はこの最適化を自動的に行いますが、私はあなたのコンパイラが何であるかを知りません。
頭に浮かぶもう1つの改善点は、高次のインデックスからのオフセットの再計算を回避しようとすることです。同じ高次のインデックスをグループ化すると、オプティマイザーがそれを達成するのに役立ちます。式をソートするだけでそれを達成しました:
if (V != lattice[(i+1) & (LATTICE_SIZE-1)][(j+1) & (LATTICE_SIZE-1)][k ].t) n++;
if (V != lattice[(i+1) & (LATTICE_SIZE-1)][j ][k+1].t) n++;
if (V != lattice[(i+1) & (LATTICE_SIZE-1)][j ][k-1].t) n++;
if (V != lattice[(i+1) & (LATTICE_SIZE-1)][(j-1) & (LATTICE_SIZE-1)][k ].t) n++;
if (V != lattice[(i-1) & (LATTICE_SIZE-1)][(j+1) & (LATTICE_SIZE-1)][k ].t) n++;
if (V != lattice[(i-1) & (LATTICE_SIZE-1)][j ][k+1].t) n++;
if (V != lattice[(i-1) & (LATTICE_SIZE-1)][j ][k-1].t) n++;
if (V != lattice[(i-1) & (LATTICE_SIZE-1)][(j-1) & (LATTICE_SIZE-1)][k ].t) n++;
私のオプティマイザーはそれを利用しましたが、その結果、スピードアップはわずか 4% でした。ただし、システムによっては異なる値になる場合があります。
また、最適化の多くは実際には関数の使用に依存します。たとえば、次のような簡単なテストを作成しました。
volatile int n = 0;
for ( int i = 0; i != LATTICE_SIZE; ++i )
for ( int j = 0; j != LATTICE_SIZE; ++j )
for ( int k = 0; k != LATTICE_SIZE; ++k )
n += neighbour ( i, j, k );
私の測定では、neighbour() 呼び出しごとに約 12 ns でした。その後、隣人は 2 つの高次平面でのみチェックされることに気付きました。関数をリファクタリングして、オプティマイザーにより明確なヒントを与えました。
int neighbour_in_plane ( elem_t l[LATTICE_SIZE][LATTICE_SIZE], int j, int k )
{
int n = 0;
if (V != l[(j-1) & (LATTICE_SIZE-1)][k ].t) n++;
if (V != l[j ][k-1].t) n++;
if (V != l[j ][k+1].t) n++;
if (V != l[(j+1) & (LATTICE_SIZE-1)][k ].t) n++;
return n;
}
//calculates number of neighbours that aren't vacancies
int neighbour(int i, int j, int k)
{
return neighbour_in_plane ( lattice[(i-1) & (LATTICE_SIZE-1)], i, j ) +
neighbour_in_plane ( lattice[(i+1) & (LATTICE_SIZE-1)], i, j );
}
そして驚くべきことに、呼び出しごとに 4 ns しか見られませんでした。コンパイラの出力を確認したところ、今回は両方の関数が呼び出しループにインライン化され、多くの最適化が行われていることがわかりました。つまり、2 つの内部ループを neighbour_in_plane() 関数に効果的に移動したため、何千もの
lattice[(i-+1) & (LATTICE_SIZE-1)]
式の再計算を回避できました。
肝心なのは、コード+コンパイラ+プラットフォーム環境でこの関数を試して、速度を最大限に引き出す必要があるということです。