web-dev-qa-db-ja.com

Visual Studio 2015でvalarrayが非常に遅いのはなぜですか?

ライブラリでの計算を高速化するために、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を放棄せずに問題を解決するにはどうすればよいですか?

16
dilbert

私はすべてを正しく行っていますか?

あなたはすべてを正しく行っています。問題は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実装 を検討してください。ご覧のとおり、各 operatoroperation のみを含み、データは含まないテンプレートクラス _ Expr を返します。実際の計算は 代入演算子 、より具体的には __ valarray_copy 関数で実行されます。したがって、割り当てを実行するまで、すべてのアクションはプロキシオブジェクト_Exprで実行されます。 operator=が呼び出されると、_Exprに格納されている操作が1つのループで実行されます。これが、g ++でこのような良い結果が得られる理由です。

どうすれば問題を解決できますか?

インターネット上で適切なstd::valarray実装を見つける必要があります。そうでない場合は、独自に作成できます。 GNU実装を例として使用できます。

21
Dmytro Dadyka