Может ли реализация C++ теоретически распараллелить оценку двух аргументов функции?


учитывая следующий вызов функции:

f(g(), h())

поскольку порядок оценки аргументов функции не определен (все еще имеет место в C++11, насколько мне известно), может ли реализация теоретически выполнить g() и h() параллельно?

такая параллелизация могла только пнуть в were g и h известно, что это довольно тривиально (в наиболее очевидном случае, доступ только к данным, локальным для их тел), чтобы не вводить проблемы параллелизма, но, за пределами этого ограничения я не вижу ничего, что могло бы его запретить.

Итак, стандарт позволяет это? Даже если только по правилу "как-будто"?

(In ответ, Манкарс утверждает обратное; однако он не цитирует стандарт, и мое прочтение [expr.call] не выявил никаких очевидных формулировок.)

4 68

4 ответа:

требование исходит из [intro.execution]/15:

... При вызове функции ... Каждое вычисление в вызывающей функции (включая другие вызовы функций), которое не было специально упорядочено до или после выполнения тела вызываемой функции, равно неопределенная последовательность в отношении выполнения вызываемой функции [сноска:другими словами, выполнение функций не чередуется с каждым другой.].

так что любое исполнение тела g() должен быть неопределенно упорядочен с (То есть не перекрывается с) оценкой h() (поскольку h() - это выражение в вызывающей функции).

критическая точка здесь заключается в том, что g() и h() как вызовы функций.

(конечно, правило as-if означает, что возможность не может быть полностью исключена, но это никогда не должно происходить таким образом, чтобы это могло повлиять наблюдаемое поведение программы. В лучшем случае такая реализация просто изменит характеристики производительности кода.)

пока вы не можете сказать, что бы компилятор ни делал для оценки этих функций, это полностью зависит от компилятора. Очевидно, что оценка функций не может включать в себя какой-либо доступ к общим изменяемым данным, поскольку это приведет к гонкам данных. Основным руководящим принципом является правило"как бы" и основные наблюдаемые операции, т. е. доступ к volatile данных, операции ввода-вывода, доступ к атомной данных и т. д. Соответствующий раздел 1.9 [интро.исполнение.]

нет, если компилятор точно знал, что g(),h() и ничего им вызов.

два выражения, вызовы функций, которые могут иметь неизвестные побочные эффекты. Поэтому их распараллеливание может вызвать гонку данных по этим побочным эффектам. Поскольку стандарт C++ не позволяет вычислению аргументов вызывать гонку данных по любым побочным эффектам выражений, компилятор может распараллелить их только в том случае, если он знает что нет такой гонки данных вероятный.

это означает ходить через каждую функцию и смотреть на то, что они делают и / или вызывают, а затем отслеживать через те функции, etc. В общем случае это неосуществимо.

простой ответ: когда функции sequenced, даже если неопределенно, нет никакой возможности для состояния гонки между ними, что неверно, если они распараллелены. Даже пара однострочных "тривиальных" функций может это сделать.

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


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

если p - это имя общей g и h, то последовательный вызов g и h в любом порядке приведет к значению, на которое указывает p не меняется. Если они распараллелено, чтение *p и назначение его можно было бы чередовать произвольно между двумя:

  1. g читает *p и находит значение 1.
  2. f читает *p а также находит значение 1.
  3. g пишет 2 к *p.
  4. f, все еще используя значение 1, которое он прочитал раньше, будет писать 0 в *p.

таким образом, поведение отличается, когда они распараллеленный.