ライブラリでの計算を高速化するために、std::valarray
クラスを使用することにしました。 documentation はこう言っています:
std :: valarrayクラスとヘルパークラスは、特定の形式のエイリアシングを含まないように定義されているため、Cプログラミング言語のキーワード制限の効果と同様に、これらのクラスの操作を最適化できます。さらに、valarray引数を取る関数と演算子は、プロキシオブジェクトを返すことを許可され、コンパイラーがv1 = a * v2 + v3;のような式を最適化できるようにします。 v1 [i] = a * v2 [i] + v3 [i];を実行する単一のループとして。一時的または複数のパスを回避します。
これはまさに私が必要とするものです。また、g ++コンパイラーを使用すると、ドキュメントに記載されているように機能します。 std::valarray
のパフォーマンスをテストする簡単な例を開発しました。
void check(std::valarray<float>& a)
{
for (int i = 0; i < a.size(); i++)
if (a[i] != 7)
std::cout << "Error" << std::endl;
}
int main()
{
const int N = 100000000;
std::valarray<float> a(1, N);
std::valarray<float> c(2, N);
std::valarray<float> b(3, N);
std::valarray<float> d(N);
auto start = std::chrono::system_clock::now();
d = a + b * c;
auto end = std::chrono::system_clock::now();
std::cout << "Valarr optimized case: "
<< (end - start).count() << std::endl;
check(d);
// Optimal single loop case
start = std::chrono::system_clock::now();
for (int i = 0; i < N; i++)
d[i] = a[i] + b[i] * c[i];
end = std::chrono::system_clock::now();
std::cout << "Optimal case: " << (end - start).count() << std::endl;
check(d);
return 0;
}
G ++で私は得ました:
Valarr optimized case: 1484215
Optimal case: 1472202
すべての操作d = a + b * c;
は実際には1つのサイクルに配置されるため、パフォーマンスを維持しながらコードが簡素化されます。ただし、これはVisual Studio 2015を使用している場合は機能しません。同じコードの場合、次のようになります。
Valarr optimized case: 6652402
Optimal case: 1766699
違いはほぼ4倍です。最適化はありません! Visual Studio 2015でstd::valarray
が必要に応じて機能しないのはなぜですか?私はすべてを正しく行っていますか? std::valarray
を放棄せずに問題を解決するにはどうすればよいですか?
私はすべてを正しく行っていますか?
あなたはすべてを正しく行っています。問題はVisual Studio std::valarray
実装にあります。
Visual Studio 2015で
std::valarray
が必要に応じて機能しないのはなぜですか?
valarray
演算子の実装(operator+
など)を開くだけです。次のようなものが表示されます(マクロ展開後):
template<class _Ty> inline
valarray<_Ty> operator+(const valarray<_Ty>& _Left,
const valarray<_Ty>& _Right)
{
valarray<TYPE> _Ans(_Left.size());
for (size_t _Idx = 0; _Idx < _Ans.size(); ++_Idx)
_Ans[_Idx] = _Left[_Idx] + _Right[_Idx];
return (_Ans)
}
ご覧のように、操作の結果がコピーされた新しいオブジェクトが作成されます。最適化は実際にはありません。理由はわかりませんが、事実です。 Visual Studioではstd::valarray
は互換性のためだけに追加されたようです。
比較のために、 GNU実装 を検討してください。ご覧のとおり、各 operator は operation のみを含み、データは含まないテンプレートクラス _ Expr を返します。実際の計算は 代入演算子 、より具体的には __ valarray_copy 関数で実行されます。したがって、割り当てを実行するまで、すべてのアクションはプロキシオブジェクト_Expr
で実行されます。 operator=
が呼び出されると、_Expr
に格納されている操作が1つのループで実行されます。これが、g ++でこのような良い結果が得られる理由です。
どうすれば問題を解決できますか?
インターネット上で適切なstd::valarray
実装を見つける必要があります。そうでない場合は、独自に作成できます。 GNU実装を例として使用できます。