Подтвердить что ты не робот

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

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

f(g(), h())

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

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

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

этот ответ, Mankarse утверждает иначе, однако он не цитирует стандарт, и мой прочтение [expr.call] не имеет показала любую очевидную формулировку.)

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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


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

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

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

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