web-dev-qa-db-fra.com

C ++ valarray vs vecteur

J'aime beaucoup les vecteurs. Ils sont chouettes et rapides. Mais je sais que cette chose appelée un valarray existe. Pourquoi devrais-je utiliser un valarray au lieu d'un vecteur? Je sais que les Valarrays ont du sucre syntaxique, mais à part ça, quand sont-ils utiles?

149
rlbond

Les Valarrays (tableaux de valeurs) sont destinés à amener une partie de la vitesse de Fortran en C++. Vous ne feriez pas une liste de pointeurs afin que le compilateur puisse faire des hypothèses sur le code et l'optimiser au mieux. (La raison principale pour laquelle Fortran est si rapide est qu’il n’existe aucun type de pointeur et qu’il ne peut donc pas y avoir d’alias de pointeur.)

Valarrays propose également des classes qui vous permettent de les découper de manière relativement simple bien que cette partie de la norme puisse nécessiter un peu plus de travail. Les redimensionner est destructif et il leur manque des itérateurs.

Donc, s’il s’agit de chiffres avec lesquels vous travaillez et que la commodité n’est pas si importante, utilisez des tableaux de valeur. Sinon, les vecteurs sont beaucoup plus pratiques.

66
Tim Allman

valarray est une sorte d'orphelin qui est né au mauvais endroit au mauvais moment. C'est une tentative d'optimisation, assez spécifiquement pour les machines qui étaient utilisées pour les mathématiques lourdes lors de la rédaction, à savoir les processeurs vectoriels comme le Crays.

Pour un processeur vectoriel, vous vouliez généralement appliquer une opération unique à un tableau entier, puis appliquer l'opération suivante à l'ensemble du tableau, et ainsi de suite jusqu'à ce que vous ayez fait tout ce que vous aviez à faire.

Cependant, à moins que vous n'ayez à traiter avec des baies assez petites, la mise en cache a tendance à mal fonctionner. Sur la plupart des machines modernes, ce que vous préférez en général (dans la mesure du possible) est de charger une partie du tableau, de faire toutes les opérations sur celui-ci, puis de passer à la partie suivante du tableau.

valarray est également supposé éliminer toute possibilité de repliement du spectre, ce qui (du moins théoriquement) permet au compilateur d'améliorer la vitesse, car il est plus libre de stocker des valeurs dans des registres. En réalité, cependant, je ne suis pas du tout certain qu'une réelle mise en œuvre en tire avantage de manière significative. Je suppose que c'est plutôt un problème de type œuf et poule - sans le support du compilateur, il n'est pas devenu populaire, et tant que ce n'est pas le cas, personne ne va avoir la peine de travailler sur leur compilateur pour le prendre en charge.

Il y a aussi une gamme déconcertante (littéralement) de classes auxiliaires à utiliser avec valarray. Vous obtenez slice, slice_array, gslice et gslice_array jouer avec les morceaux d'un valarray et le faire agir comme un tableau multidimensionnel. Vous obtenez également mask_array pour "masquer" une opération (par exemple, ajouter des éléments de x à y, mais uniquement aux positions où z est différent de zéro). Pour utiliser plus que trivialement valarray, vous devez en apprendre beaucoup sur ces classes auxiliaires, dont certaines sont assez complexes et dont aucune ne me semble (du moins à mes yeux) très bien documentée.

En bout de ligne: bien qu’elle ait des moments de génie et puisse faire certaines choses de manière ordonnée, il existe également de très bonnes raisons pour lesquelles elle est (et restera presque certainement) obscure.

Edit (huit ans plus tard, en 2017): Certaines des précédentes sont devenues obsolètes à au moins un certain degré. Par exemple, Intel a implémenté une version optimisée de valarray pour son compilateur. Il utilise les Intel Integrated Performance Primitives (Intel IPP) pour améliorer les performances. Bien que l’amélioration des performances varie sans aucun doute, un test rapide avec un code simple montre une amélioration de la vitesse d’environ 2: 1, par rapport à un code identique compilé avec l’implémentation "standard" de valarray.

Ainsi, bien que je ne sois pas entièrement convaincu que les programmeurs C++ vont commencer à utiliser valarray en très grand nombre, il existe au moins certaines circonstances dans lesquelles cela peut apporter une amélioration de la vitesse.

141
Jerry Coffin

Au cours de la normalisation de C++ 98, valarray a été conçu pour permettre une sorte de calcul mathématique rapide. Cependant, à peu près à la même époque, Todd Veldhuizen inventa les modèles d'expression et créa blitz ++ . Des techniques similaires de modèle-méta furent également inventées, ce qui rendit les tableaux très obsolètes avant même que le standard ne soit publié. Le IIRC, le ou les proposants initiaux de valarray l’a abandonné à mi-chemin de la normalisation, ce qui (si cela est vrai) ne l’a pas aidé non plus.

ISTR que la raison principale pour laquelle il n'a pas été supprimé de la norme est que personne n'a pris le temps d'évaluer le problème de manière approfondie et de rédiger une proposition pour le supprimer.

Veuillez garder à l'esprit, cependant, que tout cela est un ouï-dire dont on se souvient vaguement. Prenez ceci avec un grain de sel et espérez que quelqu'un le corrige ou le confirme.

38
sbi

Je sais que les Valarrays ont du sucre syntaxique

Je dois dire que je ne pense pas que std::valarrays Ait beaucoup de sucre syntaxique. La syntaxe est différente, mais je n’appellerais pas la différence "sucre". L'API est bizarre. La section sur std::valarray Dans Le langage de programmation C++ mentionne cette API inhabituelle et le fait que, puisque std::valarray Doit être hautement optimisé, toute erreur les messages que vous recevez en les utilisant seront probablement non intuitifs.

Par curiosité, il y a environ un an, j'ai opposé std::valarray À std::vector. Je n'ai plus le code ni les résultats précis (bien qu'il ne soit pas difficile d'écrire le vôtre). Utiliser GCC I did obtenir un petit avantage en termes de performances lorsque vous utilisez std::valarray Pour des calculs simples, mais pas pour mes implémentations qui calculent l'écart type (et bien sûr, l'écart type ne ce complexe, en ce qui concerne les mathématiques). Je soupçonne que les opérations sur chaque élément d'un grand std::vector Fonctionnent mieux avec des caches que des opérations sur std::valarray. ( [~ # ~] note [~ # ~] , en suivant les conseils de musiphil , j'ai réussi à obtenir presque performances identiques de vector et valarray).

Finalement, j'ai décidé d'utiliser std::vector Tout en accordant une attention particulière à des choses comme l'allocation de mémoire et la création d'objets temporaires.


std::vector Et std::valarray Stockent les données dans un bloc contigu. Cependant, ils accèdent à ces données à l'aide de modèles différents et, plus important encore, l'API pour std::valarray Encourage des modèles d'accès différents de l'API pour std::vector.

Pour l'exemple d'écart-type, à une étape particulière, j'avais besoin de trouver la moyenne de la collection et la différence entre la valeur de chaque élément et la moyenne.

Pour le std::valarray, J'ai fait quelque chose comme:

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> temp(mean, original_values.size());
std::valarray<double> differences_from_mean = original_values - temp;

J'ai peut-être été plus intelligent avec std::slice Ou std::gslice. Cela fait plus de cinq ans maintenant.

Pour std::vector, J'ai fait quelque chose dans le sens de:

std::vector<double> original_values = ... // obviously, I put something here
double mean = std::accumulate(original_values.begin(), original_values.end(), 0.0) / original_values.size();

std::vector<double> differences_from_mean;
differences_from_mean.reserve(original_values.size());
std::transform(original_values.begin(), original_values.end(), std::back_inserter(differences_from_mean), std::bind1st(std::minus<double>(), mean));

Aujourd'hui, j'écrirais certainement cela différemment. Si rien d'autre, je profiterais de lambdas C++ 11.

Il est évident que ces deux extraits de code font des choses différentes. D'une part, l'exemple std::vector Ne crée pas une collection intermédiaire comme le fait l'exemple std::valarray. Cependant, je pense qu'il est juste de les comparer car les différences sont liées aux différences entre std::vector Et std::valarray.

Lorsque j’ai écrit cette réponse, j’imaginais que soustraire la valeur des éléments à deux std::valarray (Dernière ligne de l’exemple std::valarray) Serait moins convivial en cache que la ligne correspondante du std::vector Exemple (qui se trouve être également la dernière ligne).

Il s'avère cependant que

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> differences_from_mean = original_values - mean;

Fait la même chose que l'exemple std::vector Et a des performances presque identiques. En fin de compte, la question est de savoir quelle API vous préférez.

26
Max Lybbert

valarray était censé laisser un peu de bonté du traitement vectoriel FORTRAN sur C++. D'une manière ou d'une autre, le support nécessaire du compilateur n'est jamais vraiment arrivé.

Les livres de Josuttis contiennent des commentaires intéressants (quelque peu décourageants) sur valarray ( ici et ici ).

Cependant, Intel semble maintenant revenir sur valarray dans ses dernières versions du compilateur (voir par exemple diapositive 9 ); C’est un développement intéressant étant donné que leur jeu d’instructions SIMD à 4 voies SSE) est sur le point d’être complété par des instructions AVX à 8 voies et Larrabee à 16 voies et, dans l’intérêt de la portabilité, être beaucoup mieux de coder avec une abstraction comme valarray que (par exemple) intrinsics.

23
timday

J'ai trouvé un bon usage pour Valarray. C'est utiliser valarray comme des tableaux numpy.

auto x = linspace(0, 2 * 3.14, 100);
plot(x, sin(x) + sin(3.f * x) / 3.f + sin(5.f * x) / 5.f);

enter image description here

Nous pouvons mettre en œuvre ci-dessus avec valarray.

valarray<float> linspace(float start, float stop, int size)
{
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + i * (stop-start)/size;
    return v;
}

std::valarray<float> arange(float start, float step, float stop)
{
    int size = (stop - start) / step;
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + step * i;
    return v;
}

string psstm(string command)
{//return system call output as string
    string s;
    char tmp[1000];
    FILE* f = popen(command.c_str(), "r");
    while(fgets(tmp, sizeof(tmp), f)) s += tmp;
    pclose(f);
    return s;
}

string plot(const valarray<float>& x, const valarray<float>& y)
{
    int sz = x.size();
    assert(sz == y.size());
    int bytes = sz * sizeof(float) * 2;
    const char* name = "plot1";
    int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, bytes);
    float* ptr = (float*)mmap(0, bytes, PROT_WRITE, MAP_SHARED, shm_fd, 0);
    for(int i=0; i<sz; i++) {
        *ptr++ = x[i];
        *ptr++ = y[i];
    }

    string command = "python plot.py ";
    string s = psstm(command + to_string(sz));
    shm_unlink(name);
    return s;
}

De plus, nous avons besoin du script python).

import sys, posix_ipc, os, struct
import matplotlib.pyplot as plt

sz = int(sys.argv[1])
f = posix_ipc.SharedMemory("plot1")
x = [0] * sz
y = [0] * sz
for i in range(sz):
    x[i], y[i] = struct.unpack('ff', os.read(f.fd, 8))
os.close(f.fd)
plt.plot(x, y)
plt.show()
13
Zeta

La norme C++ 11 dit:

Les classes de tableau valarray sont définies comme étant exemptes de certaines formes de repliement, ce qui permet d'optimiser les opérations sur ces classes.

Voir C++ 11 26.6.1-2.

7
Lingxi

std :: valarray est destiné aux tâches numériques lourdes, telles que la dynamique des fluides numérique ou la dynamique de la structure numérique, dans lesquelles vous avez des tableaux contenant des millions, parfois des dizaines de millions d'éléments, et que vous parcourez en boucle avec des millions d'empreintes. Peut-être qu'aujourd'hui std :: vector a une performance comparable, mais il y a environ 15 ans, valarray était presque obligatoire si vous vouliez écrire un solveur numérique efficace.

0
mrpivello