web-dev-qa-db-fra.com

Pourquoi Valarray est-il si lent sur Visual Studio 2015?

Pour accélérer les calculs dans ma bibliothèque, j'ai décidé d'utiliser le std::valarray classe. Le documentation dit:

les classes std :: valarray et helper sont définies pour être exemptes de certaines formes d'alias, permettant ainsi aux opérations sur ces classes d'être optimisées de manière similaire à l'effet du mot clé restrict dans le langage de programmation C. De plus, les fonctions et les opérateurs qui prennent des arguments valarray sont autorisés à renvoyer des objets proxy pour permettre au compilateur d'optimiser une expression telle que v1 = a * v2 + v3; comme une seule boucle qui exécute v1 [i] = a * v2 [i] + v3 [i]; en évitant les passes temporaires ou multiples.

C'est exactement ce dont j'ai besoin. Et cela fonctionne comme décrit dans la documentation lorsque j'utilise le compilateur g ++. J'ai développé un exemple simple pour tester le std::valarray performances:

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;
}

Sur g ++ j'ai eu:

Valarr optimized case: 1484215
Optimal case: 1472202

Il semble que toutes les opérations d = a + b * c; sont vraiment placés dans un cycle, ce qui simplifie le code tout en maintenant les performances. Cependant, cela ne fonctionne pas lorsque j'utilise Visual Studio 2015. Pour le même code, j'obtiens:

Valarr optimized case: 6652402
Optimal case: 1766699

La différence est presque quatre fois; il n'y a pas d'optimisation! Pourquoi est-ce std::valarray ne fonctionne pas comme requis sur Visual Studio 2015? Suis-je en train de tout faire correctement? Comment puis-je résoudre le problème sans abandonner std::valarray?

16
dilbert

Suis-je en train de tout faire correctement?

Tu fais tout bien. Le problème est dans Visual Studio std::valarray la mise en oeuvre.

Pourquoi est-ce std::valarray ne fonctionne pas comme requis sur Visual Studio 2015?

Ouvrez simplement l'implémentation de n'importe quel opérateur valarray, par exemple operator+. Vous verrez quelque chose comme (après l'expansion de la macro):

   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)
   }

Comme vous pouvez le voir, un nouvel objet est créé dans lequel le résultat de l'opération est copié. Il n'y a vraiment aucune optimisation. Je ne sais pas pourquoi, mais c'est un fait. Il ressemble à Visual Studio, std::valarray a été ajouté à des fins de compatibilité uniquement.

Pour comparaison, considérons implémentation GN . Comme vous pouvez le voir, chaque opérateur renvoie la classe de modèle _ Expr qui contient uniquement le opération , mais ne contient pas de données. Le calcul réel est effectué dans opérateur d'affectation et plus précisément dans la fonction __ valarray_copy . Ainsi, jusqu'à ce que vous effectuiez une affectation, toutes les actions sont effectuées sur l'objet proxy _Expr. Juste une fois operator= est appelé, l'opération est-elle stockée dans _Expr effectué en une seule boucle. C'est la raison pour laquelle vous obtenez de si bons résultats avec g ++.

Comment puis-je résoudre le problème?

Vous devez trouver un std::valarray mise en œuvre sur Internet ou vous pouvez écrire la vôtre. Vous pouvez utiliser l'implémentation GNU comme exemple.

21
Dmytro Dadyka