从理论上讲,C ++实现能否并行化两个函数参数的求值?


68

给定以下函数调用:

f(g(), h())

由于未指定函数参数的求值顺序(据我所知,仍然是C ++ 11中的情况),理论上可以执行g()h()并行执行吗?

这种并行化只能踢都gh已知相当琐碎(最明显的情况下,仅访问数据的本地,以自己的身体),以免引入并发问题,但是,除此之外,限制我什么都看不到,禁止它。

那么,标准允许吗?即使仅按常规规则进行?

(在此答案中,Mankarse主张相反;但是,他没有引用该标准,而且我的通读[expr.call]并未显示任何明显的措辞。)


1
我认为这与任何其他优化都一样。只要遵守“假设”规则即可。
Mysticial

我认为,从理论上讲,它可以并行执行g()和h()。我不确定该标准是禁止还是允许,但是我的想法是应该这样做
Aniket Inge 2012年

14
它可以启动火箭让月球人告诉它函数调用的结果并返回。
Johannes Schaub-litb 2012年

1
正如你所说,这将踢只对相当琐碎gh(在这种情况下,编译也许可以证明它是相当于有些人的序贯评价反正),然后就不会真正有多大的好处:创建一个线程可能会更贵而不是评估任何一个功能。—如果具有更大的功能,则原则上可以极大地提高性能,将很难保持对递归线程生成可能爆发的控制。甚至Haskell都不这样做,因为它在任何地方都具有评估顺序的自由度,并且具有轻量级线程。
大约

1
如果我们有一个pure函数说明符,告诉实现函数所说的函数不访问(可能)共享状态(又称没有副作用),那么将来可能会实现。
Xeo 2012年

Answers:


42

要求来自[intro.execution]/15

...调用函数时...在执行被调用函数的主体之前或之后,未按其他方式专门排序的调用函数中的每个评估(包括其他函数调用)都将不确定地按顺序执行称为函数[注:换句话说,函数执行不会相互交织。]。

因此,的主体的任何执行都g()必须不确定地与h()(由于h()在调用函数中是一个表达式)的求值顺序(即,不与之求值)重叠。

这里的关键点是,g()并且h()都是函数调用。

(当然,按条件规则意味着不能完全排除这种可能性,但绝不应以可能影响程序可观察行为的方式发生这种可能性。最多,这种实现只会改变程序的性能特征。编码。)


就是那个。认为这仅适用于g()vsf()h()vs f(),很容易被吓倒,但实际上,最终的结果是不能将g()和和h()交错。
Lightness Races in Orbit

我觉得我原来的混乱是在阅读了“不定”更加强烈地比“排序”,在不定测序。确实,“排序”是关键术语,因为我认为交错调用之间不会有“序列”。
Lightness Races in Orbit

这个答案是正确的,但值得注意的是,这仅仅是如此,因为既不的电话g()h()带任何参数。如果已经存在,则对这些参数的评估(为执行f或做准备h不会不确定地相互排序(因此它们可以被交错),尽管它们相对于另一个主体的主体可以不确定地排序。的fh
Marc van Leeuwen

16

只要您不知道,编译器为评估这些功能所做的一切完全取决于编译器。显然,对功能的评估不能涉及对共享的可变数据的任何访问,因为这会引入数据竞争。基本指导原则是“好像”规则和可观察的基本操作,即访问volatile数据,I / O操作,访问原子数据等。相关部分为1.9 [intro.execution]。


我猜不清楚。我承认不能在一般情况下应用。但是,在功能本身的行为并不阻止这种可能性的情况下……
轨道上的“竞速比赛” 2012年

4
我以为很清楚:如果编译器可以说出它不会违反其约束,那么它可以做任何想做的事情。换种说法:只要您不知道(除了可能需要花费时间),它就可以并行执行这些功能,因此可以随意执行。
DietmarKühl2012年

第一次我对您的回答略有不同。在五分钟的宽限期窗口中,它是否发生了变化?还是我会发疯?
Lightness Races in Orbit

1
@LightnessRacesinOrbit:好吧,我第一次发布后不久就发生了变化,但并没有实质性改变:我将“ relecant”固定为“ relevant”,但我不认为这会改变语义;)
DietmarKühl2012年

好点子。规范定义了语义,而不是实现,因此,如果并行化没有语义差异,则编译器可以做到。
dhasenan

3

除非编译器确切知道,和它们调用的内容g(),否则不然h()

这两个表达式是函数调用,可能会有未知的副作用。因此,将它们并行化可能会导致这些副作用的数据争夺。由于C ++标准不允许参数求值引起表达式的任何副作用,因此,编译器仅在知道不可能进行此类数据竞争的情况下才能并行化它们。

这意味着要遍历每个函数并仔细查看它们的作用和/或调用,然后跟踪这些函数,等等。在一般情况下,这是不可行的。


我承认它不能在所有情况下都能做到。我想s/were g and h fairly trivial/were g and h known to be fairly trivial/在一个问题上。
Lightness Races in Orbit

1

容易回答:对函数进行排序时,即使不确定地对它们进行排序,也不可能在两者之间出现竞争状态,如果将它们并行化,那是不正确的。甚至一对单行“琐碎”功能也可以做到这一点。

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


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

如果pg和共享的名称h,则按任意顺序依次调用gh将导致所指向的值p不变。如果将它们并行化,*p则可以在两者之间任意交错读取和分配它:

  1. g读取*p并找到值1。
  2. f读取*p并找到值1。
  3. g将2写入*p
  4. f,仍然使用之前读取的值1,会将0写入*p

因此,并行化时的行为是不同的。


可以,但是我以前不清楚这两个调用是否要不确定地排序(而不是未排序),因此提出了问题。:)
轻轨赛将于2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.