推测条件执行 C/C++
Speculative conditional execution C/C++
于是和同事讨论了评估执行的顺序。
考虑这个条件:
if (a > b && ref++) { do something }
我们都同意,按照 C 标准,顺序是从左到右。然而,
分歧在于,尽管有求值顺序,但某些其数据最终将被求值的指令可能会被推测性地执行,然后尽管惰性求值仍保持此状态。
例如,ref
在评估 a > b
之前递增,评估仍然从左到右进行,即 a > b
和 a > b
的实际 jump greater than
指令rev > 0
按从左到右的顺序执行,并在 !true 时在第一个实例中放弃。现在这违背了惰性评估的整个概念,但是如果 ref
是一个指令流,也许是一个内联函数怎么办。开始推测性地执行一些指令会更有意义。问题是:这些指令是否已提交,即使是中途。
想法?
运算符&&
(以及运算符||
)在某种意义上是特殊的,因为它们保证从左到右为"short-cut-evaluation"。这意味着对于像 expr1 && expr2
这样的条件,expr1
将始终首先被评估,并且 - 甚至更多 - 如果 expr1
已经被评估为 expr2
则根本不会被评估错误的。这不是优化,这是语言保证的。
因此,在 if (a > b && ref++)
中,当且仅当 a > b
的计算结果为 [=20] 时,表达式 ref++
才会被计算(包括它递增 ref
的副作用) =].
a > b && ref++
不可能,因为 &&
运算符 短路 。
想想那个案例:
f = fopen("foo.txt");
if ((f != NULL) && fread(buffer,10,1,f)) { ...
执行第二部分如果 f==NULL
会是一个 bug 因为第一部分可以防止它。
相反,没有什么能阻止编译器在这里为所欲为:
a > b & ref++
&
是算术 和 所以这两个条件总是被执行。因此,即使第一个条件为假,ref
也会递增。
C 编译器被允许执行 "as-if" 规则,这意味着只要程序的 意图 得到尊重,它们就可以做任何他们喜欢的事情。
考虑 (a > b && ref++)
。这里的意图是如果 a > b
是 1
,ref
将 只增加 ,并且 必须 是观察到的行为。这是因为 &&
的右手参数仅在左手参数为 1
时才被评估。然而,生成的汇编代码可能使得 CPU 可以推测性地递增 ref
( 分支预测 ),并在适当时通过管道转储收回该递增。
于是和同事讨论了评估执行的顺序。 考虑这个条件:
if (a > b && ref++) { do something }
我们都同意,按照 C 标准,顺序是从左到右。然而, 分歧在于,尽管有求值顺序,但某些其数据最终将被求值的指令可能会被推测性地执行,然后尽管惰性求值仍保持此状态。
例如,ref
在评估 a > b
之前递增,评估仍然从左到右进行,即 a > b
和 a > b
的实际 jump greater than
指令rev > 0
按从左到右的顺序执行,并在 !true 时在第一个实例中放弃。现在这违背了惰性评估的整个概念,但是如果 ref
是一个指令流,也许是一个内联函数怎么办。开始推测性地执行一些指令会更有意义。问题是:这些指令是否已提交,即使是中途。
想法?
运算符&&
(以及运算符||
)在某种意义上是特殊的,因为它们保证从左到右为"short-cut-evaluation"。这意味着对于像 expr1 && expr2
这样的条件,expr1
将始终首先被评估,并且 - 甚至更多 - 如果 expr1
已经被评估为 expr2
则根本不会被评估错误的。这不是优化,这是语言保证的。
因此,在 if (a > b && ref++)
中,当且仅当 a > b
的计算结果为 [=20] 时,表达式 ref++
才会被计算(包括它递增 ref
的副作用) =].
a > b && ref++
不可能,因为 &&
运算符 短路 。
想想那个案例:
f = fopen("foo.txt");
if ((f != NULL) && fread(buffer,10,1,f)) { ...
执行第二部分如果 f==NULL
会是一个 bug 因为第一部分可以防止它。
相反,没有什么能阻止编译器在这里为所欲为:
a > b & ref++
&
是算术 和 所以这两个条件总是被执行。因此,即使第一个条件为假,ref
也会递增。
C 编译器被允许执行 "as-if" 规则,这意味着只要程序的 意图 得到尊重,它们就可以做任何他们喜欢的事情。
考虑 (a > b && ref++)
。这里的意图是如果 a > b
是 1
,ref
将 只增加 ,并且 必须 是观察到的行为。这是因为 &&
的右手参数仅在左手参数为 1
时才被评估。然而,生成的汇编代码可能使得 CPU 可以推测性地递增 ref
( 分支预测 ),并在适当时通过管道转储收回该递增。