68 votes

Une implémentation C++ pourrait-elle, en théorie, paralléliser l'évaluation de deux arguments de fonction ?

Étant donné l'appel de fonction suivant :

f(g(), h())

puisque l'ordre d'évaluation des arguments de la fonction n'est pas spécifié (c'est toujours le cas en C++11 pour autant que je sache), une implémentation pourrait théoriquement exécuter g() y h() en parallèle ?

Une telle mise en parallèle ne pourrait se faire que si g y h sont connus pour être assez triviaux (dans le cas le plus évident, ils n'accèdent qu'aux données locales de leur corps) afin de ne pas introduire de problèmes de concurrence mais, au-delà de cette restriction, je ne vois rien qui l'interdise.

Alors, la norme l'autorise-t-elle ? Même si ce n'est que par la règle du " si " ?

(En <a href="https://stackoverflow.com/a/9116081/560648">cette réponse </a>Mankarse affirme le contraire ; cependant, il ne cite pas la norme, et ma lecture de l'étude de l <code>[expr.call]</code> n'a pas révélé de formulation évidente).

42voto

Mankarse Points 22800

L'exigence provient de [intro.execution]/15 :

... Lors de l'appel d'une fonction ... Chaque évaluation dans la fonction appelante (y compris les autres appels de fonction) qui n'est pas spécifiquement séquencée avant ou après l'exécution du corps de la fonction appelée est séquencé de manière indéterminée par rapport à l'exécution de la fonction appelée [Note de bas de page : En d'autres termes, les exécutions de fonctions ne s'imbriquent pas les unes dans les autres. ].

Ainsi, toute exécution du corps de g() doit être séquencée de manière indéterminée avec (c'est-à-dire sans chevauchement avec) l'évaluation de h() (parce que h() est une expression dans la fonction appelante).

Le point critique ici est que g() y h() sont tous deux des appels de fonction.

(Bien sûr, la règle du "si" signifie que la possibilité ne peut être entièrement exclue, mais elle ne devrait jamais se produire d'une manière qui pourrait affecter le comportement observable d'un programme. Tout au plus, une telle implémentation ne ferait que modifier les caractéristiques de performance du code).

16voto

Dietmar Kühl Points 70604

Tant que vous ne pouvez pas le dire, ce que le compilateur fait pour évaluer ces fonctions est entièrement à sa discrétion. Il est clair que l'évaluation des fonctions ne doit pas impliquer d'accès à des données partagées et mutables, car cela introduirait des courses de données. Le principe directeur de base est la règle du "comme si" et les opérations fondamentales observables, c'est-à-dire l'accès à des données partagées et mutables, ne sont pas autorisées. volatile les données, les opérations d'E/S, l'accès aux données atomiques, etc. La section concernée est 1.9 [intro.execution].

3voto

Nicol Bolas Points 133791

Non, sauf si le compilateur savait exactement ce que g() , h() et tout ce qu'ils appellent le fait.

Les deux expressions sont des appels de fonction, qui peuvent avoir des effets secondaires inconnus. Par conséquent, leur parallélisation pourrait provoquer un data-race sur ces effets secondaires. Puisque la norme C++ ne permet pas à l'évaluation des arguments de provoquer une course aux données sur les effets secondaires des expressions, le compilateur ne peut les paralléliser que s'il connaît qu'une telle course aux données n'est pas possible.

Cela signifie qu'il faut parcourir chaque fonction et regarder exactement ce qu'elle fait et/ou appelle, puis suivre ceux fonctions, etc. Dans le cas général, ce n'est pas faisable.

1voto

nevsan Points 657

Réponse facile : lorsque les fonctions sont séquencé Même si c'est de manière indéterminée, il n'y a aucune possibilité de condition de course entre les deux, ce qui n'est pas vrai s'ils sont parallélisés. Même une paire de fonctions "triviales" d'une ligne pourrait le faire.

void g()
{
    *p = *p + 1;
}

void h()
{
    *p = *p - 1;
}

Si p est un nom partagé par g y h puis un appel séquentiel de g y h dans n'importe quel ordre aura pour résultat la valeur pointée par p ne change pas. S'ils sont parallélisés, la lecture de *p et son attribution pourrait être intercalée arbitrairement entre les deux :

  1. g lit *p et trouve la valeur 1.
  2. f lit *p et trouve également la valeur 1.
  3. g écrit 2 à *p .
  4. f en utilisant toujours la valeur 1 qu'il a lue auparavant, il écrira 0 à *p .

Ainsi, le comportement est différent lorsqu'ils sont parallélisés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X