给出以下函数调用:
f(g(), h())
因为函数参数的评估顺序是未指定的(据我所知,在C ++ 11中仍然是这种情况),理论上可以并行执行g()
和h()
吗?
这样的并行化只能在g
和h
已知相当微不足道(在最明显的情况下,仅访问其身体本地的数据),以便不引入并发问题但除了这个限制,我看不到任何禁止它的东西。
那么,标准是否允许它?即使只是按照as-if规则?
(在this answer中,Mankarse声称不然;但是,他没有引用标准,而我对[expr.call]
的通读没有发现任何明显的措辞。)
答案 0 :(得分:42)
要求来自[intro.execution]/15
:
...调用函数时...调用函数中的每个评估(包括其他函数调用)在执行被调用函数体之前或之后没有特别排序不确定地排序< / strong>关于被调用函数的执行[脚注:换句话说,函数执行不会相互交错。]。
因此g()
的主体的任何执行都必须与h()
的评估不确定地排序(即不重叠)(因为h()
是调用函数中的表达式)。
这里的关键点是g()
和h()
都是函数调用。
(当然,as-if规则意味着不能完全排除这种可能性,但它绝不应该以可能影响程序的可观察行为的方式发生。最多,这样的实现只会改变代码的性能特征。)
答案 1 :(得分:16)
只要您无法分辨,无论编译器如何评估这些函数完全取决于编译器。显然,对功能的评估不能涉及对共享的可变数据的任何访问,因为这会引入数据竞争。基本的指导原则是“似乎” - 规则和基本的可观察操作,即访问volatile
数据,I / O操作,访问原子数据等。相关部分是1.9 [intro.execution ]
答案 2 :(得分:3)
除非编译器确切知道g()
,h()
以及他们调用的任何内容。
这两个表达式是函数调用,可能有未知的副作用。因此,对它们进行并行化可能会导致这些副作用的数据竞争。由于C ++标准不允许参数评估导致表达式的任何副作用的数据争用,因此编译器只能在知道没有这样的数据争用的情况下并行化它们。
这意味着走过每个功能,看看他们做了什么和/或打电话,然后跟踪那些功能等。在一般情况下,这是不可行的。
答案 3 :(得分:1)
简单回答:当函数排序时,即使不确定,两者之间也不存在竞争条件,如果它们是并行化的则不是这样。即使是一对一行“琐碎”的功能也可以做到。
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
。因此,并行化时行为是不同的。