Valarray C ++ vs vecteur


159

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


2
Je réfléchissais juste à cela l'autre jour, aussi. Autant que je sache, c'est vraiment un vecteur mathématique aussi spécialisé.
GManNickG

Valarray ne fait-il pas des modèles d'expression?
Mooing Duck

Le physicien Ulrich Mutze fournit un cas d'utilisation pour valarray ici et ici
lifebalance

Réponses:


70

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

Les Valarrays ont également des classes qui vous permettent de les découper de manière assez simple, bien que cette partie de la norme puisse nécessiter un peu plus de travail. Leur redimensionnement est destructeur et ils manquent d'itérateurs.

Donc, si ce sont des nombres avec lesquels vous travaillez et que la commodité n'est pas si importante, utilisez les valarres. Sinon, les vecteurs sont beaucoup plus pratiques.


11
Ils ne sont pas conçus pour éviter les pointeurs. C ++ 11 définit begin () et end () dans valarray qui leur renvoient des itérateurs
Mohamed El-Nakib

3
@ user2023370: c'est pourquoi tant d'utilisateurs de Fortran préfèrent Fortran 77. :)
Michael

152

valarrayest 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 ont été utilisées pour les mathématiques lourdes quand il a été écrit - en particulier, les processeurs vectoriels comme les Crays.

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

Sauf si vous avez affaire à des tableaux assez petits, cependant, cela a tendance à mal fonctionner avec la mise en cache. Sur la plupart des machines modernes, ce que vous préférez généralement (dans la mesure du possible) serait de charger une partie du tableau, d'effectuer toutes les opérations que vous allez effectuer dessus, puis de passer à la partie suivante du tableau.

valarrayest également censé éliminer toute possibilité d'aliasing, ce qui (du moins en théorie) 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 sûr qu'une mise en œuvre réelle en profite de manière significative. Je soupçonne que c'est plutôt un problème de poule et d'oeuf - sans le support du compilateur, il n'est pas devenu populaire, et tant que ce n'est pas populaire, personne ne va se donner la peine de travailler sur son compilateur pour le supporter.

Il existe également un tableau déroutant (littéralement) de classes auxiliaires à utiliser avec valarray. Vous obtenez slice, slice_array, gsliceet gslice_arrayde jouer avec des morceaux d'un valarray, et le faire agir comme un tableau multidimensionnel. Vous pouvez également mask_array"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 faire un usage plus que trivial devalarray , vous devez en apprendre beaucoup sur ces classes auxiliaires, dont certaines sont assez complexes et dont aucune ne semble (du moins à moi) très bien documentée.

Conclusion: bien qu'il ait des moments de brillance et qu'il puisse faire certaines choses assez bien, il y a aussi de très bonnes raisons pour lesquelles il est (et restera presque certainement) obscur.

Edit (huit ans plus tard, en 2017): Certains des précédents sont devenus obsolètes au moins dans une certaine mesure. Pour un exemple, Intel a implémenté une version optimisée de valarray pour son compilateur. Il utilise les primitives Intel Integrated Performance (Intel IPP) pour améliorer les performances. Bien que l'amélioration exacte des performances varie sans aucun doute, un test rapide avec du code simple montre une amélioration de la vitesse d'environ 2: 1, par rapport à un code identique compilé avec l'implémentation "standard" devalarray .

Donc, bien que je ne sois pas entièrement convaincu que les programmeurs C ++ commenceront à l'utiliser valarrayen grand nombre, il y a moins de circonstances dans lesquelles cela peut améliorer la vitesse.


1
Est-il spécifiquement interdit de stocker des types d'objets arbitraires dans valarray?
user541686

6
@Mehrdad: Oui - il y a une (assez longue) liste de restrictions dans [Numeric.Requirements]. Pour quelques exemples seulement, toutes les classes abstraites et exceptions sont interdites. Il nécessite également une équivalence entre (par exemple) la construction de copie et une séquence de construction par défaut suivie d'une affectation.
Jerry Coffin

@JerryCoffin Sheesh c'est effrayant. nous promettons de ne pas l'utiliser.
Hani Goc

4
Je ne déciderais pas cela sur la base de la peur. Je déciderais si vous avez besoin de stocker des éléments qui utilisent des fonctionnalités interdites.
Jerry Coffin

3
@annoying_squid: Si vous avez des informations plus spécifiques et (vous pensez) précises à ajouter, n'hésitez pas à ajouter une réponse en les montrant. Dans l'état actuel des choses, votre commentaire ne semble cependant pas ajouter d'informations utiles.
Jerry Coffin

39

Lors de la standardisation de C ++ 98, valarray a été conçu pour permettre une sorte de calculs mathématiques rapides. Cependant, à cette époque, Todd Veldhuizen a inventé des modèles d'expression et créé blitz ++ , et des techniques de modèle-méta similaires ont été inventées, ce qui a rendu les valarrays à peu près obsolètes avant même la publication de la norme. IIRC, le (s) proposant (s) original (s) de valarray l'a abandonné à mi-chemin de la normalisation, ce qui (si c'est vrai) ne l'a pas aidé non plus.

ISTR que la principale raison pour laquelle il n'a pas été supprimé de la norme est que personne n'a pris le temps d'évaluer le problème en profondeur et d'écrire 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 corrige ou confirme cela.


Les modèles d'expression peuvent également être crédités à Vandevoorde, non?
Nikos Athanasiou

@Nikos: Pas à ma connaissance. Je pourrais toutefois avoir tord. Qu'avez-vous en faveur de cette lecture?
sbi

1
il est mentionné dans le livre "C ++ Templates - The complete guide", je pense qu'il est généralement admis qu'ils les ont tous les deux inventés indépendamment .
Nikos Athanasiou

27

Je sais que les valarrays ont du sucre syntaxique

Je dois dire que je ne pense pas std::valarraysavoir 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 les std::valarrays du langage de programmation C ++ mentionne cette API inhabituelle et le fait que, depuisstd::valarray s sont censés être hautement optimisés, tous les messages d'erreur que vous obtenez en les utilisant seront probablement non intuitifs.

Par curiosité, il y a environ un an , je dénoyautées std::valarraycontre std::vector. Je n'ai plus le code ni les résultats précis (même s'il ne devrait pas être difficile d'écrire le vôtre). En utilisant GCC , je l'ai fait obtenir un peu d' avantages de performances lors de l' utilisation std::valarraydes mathématiques simples, mais pas pour mes mises en œuvre pour calculer l' écart - type (et, bien sûr, l' écart - type est pas complexe, dans la mesure où les mathématiques va). Je soupçonne que les opérations sur chaque élément d'un grand std::vectorjeu mieux avec les caches que les opérations sur std::valarrays. ( NOTE , suite aux conseils de musiphil , j'ai réussi à obtenir des performances presque identiques de vectoret valarray).

Au final, j'ai décidé de l'utiliser std::vectoren prêtant une attention particulière à des choses comme l'allocation de mémoire et la création d'objets temporaires.


Les deux std::vectoret std::valarraystockent les données dans un bloc contigu. Cependant, ils accèdent à ces données en utilisant différents modèles et, plus important encore, l'API pour std::valarrayencourage des modèles d'accès différents de ceux de l'API pour std::vector.

Pour l'exemple de l'écart type, à une étape particulière, j'ai dû 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::sliceou std::gslice. Cela fait plus de cinq ans maintenant.

Car std::vector, j'ai fait quelque chose du genre:

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 des lambdas C ++ 11.

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

Quand j'ai écrit cette réponse, je soupçonnais que soustraire la valeur des éléments de deux std::valarrays (dernière ligne de l' std::valarrayexemple) serait moins compatible avec le cache que la ligne correspondante dans l' std::vectorexemple (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' std::vectorexemple et a des performances presque identiques. En fin de compte, la question est de savoir quelle API vous préférez.


Je ne vois aucune raison pour laquelle a std::vectorjouerait mieux avec les caches que a std::valarray; ils allouent tous deux un seul bloc de mémoire contigu pour leurs éléments.
musiphil

1
@musiphil Ma réponse est trop longue pour un commentaire, j'ai donc mis à jour la réponse.
Max Lybbert

1
Pour votre valarrayexemple ci-dessus, vous n'avez pas eu à construire d' temp valarrayobjet, mais vous auriez pu simplement le faire std::valarray<double> differences_from_mean = original_values - mean;, puis le comportement du cache devrait être similaire à celui de l' vectorexemple. (Au fait, si meanc'est vraiment int, pas double, vous pourriez avoir besoin static_cast<double>(mean).)
musiphil

Merci pour la suggestion de nettoyer le fichier valarray. J'aurai besoin de voir si cela améliore les performances. Quant à l' meanêtre int: c'était une erreur. J'ai initialement écrit l'exemple en utilisant ints, puis j'ai réalisé que le meanserait alors très loin de la vraie moyenne à cause de la troncature. Mais j'ai manqué quelques changements nécessaires lors de ma première série de modifications.
Max Lybbert

@musiphil Vous avez raison; cette modification a amené l'exemple de code à des performances presque identiques.
Max Lybbert

23

valarray était censé laisser une certaine bonté de traitement vectoriel FORTRAN déteindre sur C ++. D'une manière ou d'une autre, le support nécessaire du compilateur n'a jamais vraiment eu lieu.

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

Cependant, Intel semble maintenant revoir valarray dans ses récentes versions de compilateur (par exemple, voir la diapositive 9 ); c'est un développement intéressant étant donné que leur jeu d'instructions SIMD SSE 4 voies est sur le point d'être rejoint par des instructions AVX 8 voies et Larrabee 16 voies et dans l'intérêt de la portabilité, il sera probablement beaucoup mieux de coder avec une abstraction comme valarray que (disons) intrinsèques.


16

J'ai trouvé un bon usage pour valarray. Il s'agit d'utiliser valarray comme les 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);

entrez la description de l'image ici

Nous pouvons implémenter 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 d'un 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()

2
J'avais littéralement les mêmes pensées que vous lorsque j'ai découvert le valarray aujourd'hui au travail. Je pense qu'à partir de maintenant, pour les problèmes de traitement mathématique en c ++, j'utiliserai valarray car le code semble beaucoup plus simple à comprendre d'un point de vue mathématique.
Zachary Kraus

8

La norme C ++ 11 dit:

Les classes du tableau valarray sont définies pour être exemptes de certaines formes d'alias, permettant ainsi d'optimiser les opérations sur ces classes.

Voir C ++ 11 26.6.1-2.


Puisque je suppose que la norme définit les formulaires, pouvez-vous les citer? De plus, sont-ils mis en œuvre à l'aide d'astuces de codage ou sont-ils des exceptions basées sur le compilateur aux règles d'alias ailleurs dans le langage?
underscore_d

2

Avec, std::valarrayvous pouvez utiliser la notation mathématique standard comme prête à l'emploi v1 = a*v2 + v3. Cela n'est possible avec les vecteurs que si vous définissez vos propres opérateurs.


0

std :: valarray est destiné aux tâches numériques lourdes, telles que Computational Fluid Dynamics ou Computational Structure Dynamics, dans lesquelles vous avez des tableaux avec des millions, parfois des dizaines de millions d'éléments, et vous les parcourez dans une boucle avec également des millions de pas de temps. Peut-être qu'aujourd'hui std :: vector a une performance comparable mais, il y a 15 ans, valarray était presque obligatoire si vous vouliez écrire un solveur numérique efficace.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.