Passer trop d’arguments par référence pourrait être inefficace?

Disclamer: J’utilise Intel Comstackr 2017 et si vous voulez savoir pourquoi je fais cela, allez à la fin de la question.

J’ai ce code:

class A{ vector v; ... void foo(); void bar(); } void A::foo(){ for(int i=0; i<bigNumber;i++){ //something very expensive //call bar() many times per cycle; } } void A::bar(){ //... v.push_back(/*something*/); } 

Supposons maintenant que je veuille paralléliser foo() car cela coûte très cher. Cependant, je ne peux pas simplement utiliser #pragma omp parallel for car v.push_back() .

À ma connaissance, il existe deux alternatives:

  1. Nous utilisons #pragma omp critical
  2. Nous créons une version locale de v pour chaque thread, puis nous les joignons à la fin de la section parallèle, plus ou moins comme expliqué ici .

La solution 1. est souvent considérée comme une mauvaise solution car la situation de concurrence engendre des frais généraux conséquents.

Cependant, la solution 2. nécessite de modifier bar() de la manière suivante:

 class A{ vector v; ... void foo(); void bar(std::vector &local_v); } void A::foo(){ #pragma omp parallel { std::vector local_v; #pragma omp for for(int i=0; i<bigNumber;i++){ //something very expensive //call bar(local_v) many times per cycle; } #pragma omp critical { v.insert(v.end(), local_v.begin(), local_v.end()); } } } void A::bar(std::vector &local_v){ //... v.push_back(/*something*/); } 

Jusqu’ici tout va bien. Supposons maintenant qu’il n’ya pas seulement v , mais aussi 10 vecteurs, par exemple v1 , v2 , …, v10 , ou de toute façon 10 variables partagées. Et en plus, supposons que cette bar ne soit pas appelée directement dans foo() mais après de nombreux appels nesteds. Quelque chose comme foo() qui appelle foo1(std::vector v1, ..., std::vector v10) qui appelle foo2(std::vector v1, ..., std::vector v10) , en répétant cet appel nested plusieurs fois jusqu’à ce que le dernier appelle finalement bar(std::vector v1, ..., std::vector v10) .

Donc, cela ressemble à un cauchemar pour la maintenabilité (je dois modifier tous les en-têtes et les appels pour toutes les fonctions nestedes) … Mais plus important encore: nous convenons que passer par référence est efficace, mais c’est toujours une copie de pointeur. Comme vous pouvez le constater, de nombreux pointeurs sont copiés plusieurs fois. Est-il possible que toutes ces copies résultent en une inefficacité?

En fait, ce qui m’importe le plus ici, ce sont les performances. Par conséquent, si vous me dites “non, c’est bien parce que les compilateurs sont super intelligents et qu’ils font de la sorcellerie pour pouvoir copier un billion de références et que les performances ne chutent pas” , mais je ne sais pas si une telle sorcellerie existe ou non.

Pourquoi je fais ça: J’essaie de paralléliser ce code. En particulier, je suis en train de réécrire le while tant que for lequel il est possible de mettre en parallèle, mais si vous suivez le code, vous découvrirez que le rappel onAffineShapeFound partir d’ ici est appelé, ce qui modifie l’état des keys object partagé. Cela arrive pour beaucoup d’autres variables, mais c’est le cas “le plus profond” pour ce code.

Dans une comparaison directe entre a::Bar() et a::Bar(std::vector & v) , la différence est que la deuxième version devra augmenter la taille de la stack de 8 octets supplémentaires sur ce que la version originale doit faire. En termes de performances, il s’agit d’un effet plutôt minimal: le pointeur de stack doit être ajusté, que la fonction contienne ou non des arguments (la seule différence réelle est donc une copie du pointeur unique, qui peut même être optimisée en fonction du compilateur. ), et en termes de performances réelles de la fonction elle-même, append constamment des éléments à un std::vector sera une opération beaucoup plus coûteuse, surtout si le vecteur a besoin d’être réalloué (ce qui sera probablement le cas fréquemment, sur la taille que le vecteur doit obtenir), ce qui signifie que ces coûts dépasseront de loin les coûts de la copie du pointeur.

Donc, version courte: allez-y avec les références.