Bei folgendem Funktionsaufruf:
f(g(), h())
Könnte eine Implementierung theoretisch g()
und h()
parallel ausgeführt werden, da die Reihenfolge der Auswertung von Funktionsargumenten nicht angegeben ist (soweit mir bekannt ist dies in C ++ 11 immer noch der Fall) ?
Eine solche Parallelisierung konnte nur eingesetzt werden, g
und es war h
bekannt, dass sie ziemlich trivial war (im offensichtlichsten Fall nur auf lokale Daten zuzugreifen), um keine Parallelitätsprobleme zu verursachen, aber über diese Einschränkung hinaus kann ich nichts sehen, was dies verbietet .
Erlaubt der Standard dies? Auch wenn nur nach der Als-ob-Regel?
(In dieser Antwort behauptet Mankarse etwas anderes; er zitiert jedoch nicht den Standard, und mein Durchlesen von [expr.call]
hat keinen offensichtlichen Wortlaut ergeben.)
quelle
g
undh
in diesem Fall könnte der Compiler wahrscheinlich beweisen, dass es sowieso einer sequenziellen Bewertung entspricht), und dann hätte es nicht wirklich viel Vorteil: Das Erstellen eines Threads wäre wahrscheinlich teurer als eine der Funktionen zu bewerten. - Bei größeren Funktionen, bei denen die Leistung im Prinzip erheblich gesteigert werden könnte, ist es schwierig, die Kontrolle über die mögliche Explosion rekursiver Thread-Spawns zu behalten. Sogar Haskell tut dies nicht, da es überall freie Bewertungsreihenfolge und leichte Gewinde gibt.pure
für Funktionen hätten, der der Implementierung mitteilt, dass diese Funktion nicht auf den (möglicherweise) gemeinsam genutzten Status zugreift (auch bekannt als hat sie keine Nebenwirkungen), könnte dies in Zukunft möglich werden.Antworten:
Die Anforderung kommt von
[intro.execution]/15
:Daher muss jede Ausführung des Körpers von
g()
unbestimmt mit der Auswertung vonh()
(weil diesh()
ein Ausdruck in der aufrufenden Funktion ist) sequenziert werden (dh nicht überlappen ).Der kritische Punkt hier ist, dass
g()
undh()
beide Funktionsaufrufe sind.(Natürlich bedeutet die Als-ob-Regel, dass die Möglichkeit nicht vollständig ausgeschlossen werden kann, aber sie sollte niemals auf eine Weise erfolgen, die das beobachtbare Verhalten eines Programms beeinflussen könnte. Eine solche Implementierung würde höchstens die Leistungsmerkmale von ändern der Code.)
quelle
g()
vsf()
undh()
vs giltf()
, aber in der Tat ist der Nettoeffekt, dass dasg()
und dash()
nicht verschachtelt werden können.g()
undh()
Argumente nimmt. Wenn dies der Fall gewesen wäre, würde die Bewertung dieser Argumente (in Vorbereitung auf die Ausführung vonf
oderh
) nicht unbestimmt miteinander sequenziert (so dass sie verschachtelt werden könnten), obwohl sie in Bezug auf den Körper des anderen unbestimmt sequenziert würden vonf
undh
.Solange Sie nicht sagen können, liegt es ganz beim Compiler, was der Compiler tut, um diese Funktionen zu bewerten. Es ist klar, dass die Bewertung der Funktionen keinen Zugriff auf gemeinsam genutzte, veränderbare Daten beinhalten kann, da dies zu Datenrennen führen würde. Das grundlegende Leitprinzip ist die "als ob" -Regel und die grundlegenden beobachtbaren Operationen, dh Zugriff auf
volatile
Daten, E / A-Operationen, Zugriff auf Atomdaten usw. Der relevante Abschnitt ist 1.9 [Einführung].quelle
Nicht , wenn der Compiler wußte genau , was
g()
,h()
und alles , was sie nennen tut.Die beiden Ausdrücke sind Funktionsaufrufe, die unbekannte Nebenwirkungen haben können. Eine Parallelisierung kann daher zu einem Datenwettlauf bei diesen Nebenwirkungen führen. Da der C ++ - Standard nicht zulässt, dass die Argumentauswertung einen Datenwettlauf mit Nebenwirkungen der Ausdrücke verursacht, kann der Compiler diese nur parallelisieren, wenn er weiß, dass ein solcher Datenwettlauf nicht möglich ist.
Das bedeutet, dass Sie durch jede Funktion gehen und genau sehen, was sie tun und / oder aufrufen, und dann diese Funktionen usw. nachverfolgen. Im allgemeinen Fall ist dies nicht möglich.
quelle
s/were g and h fairly trivial/were g and h known to be fairly trivial/
in m Frage an.Einfache Antwort: Wenn die Funktionen sequenziert werden , auch wenn sie unbestimmt sind, gibt es keine Möglichkeit für eine Race-Bedingung zwischen den beiden, was nicht zutrifft, wenn sie parallelisiert sind. Sogar ein Paar einzeiliger "trivialer" Funktionen könnte dies tun.
void g() { *p = *p + 1; } void h() { *p = *p - 1; }
Wenn
p
ein Name vong
und geteilt wirdh
, führt ein sequentieller Aufruf vong
undh
in beliebiger Reihenfolge dazu, dass der Wert angezeigt wird, auf denp
nicht geändert wird . Wenn sie parallelisiert sind, kann das Lesen*p
und Zuweisen willkürlich zwischen den beiden verschachtelt werden:g
liest*p
und findet den Wert 1.f
liest*p
und findet auch den Wert 1.g
schreibt 2 an*p
.f
Wenn Sie immer noch den zuvor gelesenen Wert 1 verwenden, wird 0 in geschrieben*p
.Daher ist das Verhalten anders, wenn sie parallelisiert werden.
quelle