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
?
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.