为什么大多数语言都没有优化"0*...",是否有任何语言可以做到?

Mik*_*ans 6 language-agnostic syntax optimization

我正在编写一个2D曲线算法并且有一些代码可以有效地进行求和a la:

for (i=0, end=...; i<end; i++) {
  value += coefficients[i] * expensiveToCalculateValue(i);
}
Run Code Online (Sandbox Code Playgroud)

coefficients[i]某些迭代步骤的值为零.由于零次仍然为零(至少在简单的算术规则下),我认为我可以通过首先检查是否coefficients[i]为零来显着优化此代码,如果是,则仅对continue下一次迭代.添加,排序,工作出色.

但这留下了一个问题:为什么这不适合我呢?这不是一些创造性的小众乘法版本,它是简单的算术.实际上所有语言都会使二进制OR和AND操作短路,如果找到一个操作数使得该结果从该点开始不变,那么为什么算术乘以零不会同样短路?

我尝试用Java,PHP,JavaScript,Perl,Python,C++运行这段代码(为synax修改),甚至看看Prolog做了什么,但没有人意识到一旦他们看到"零次......"他们不必评估潜在的昂贵的第二(或第三,第四等)术语:

printed = 0;
function veryExpensive() {
  print "oh god this costs so much, x" + (printed++);
  return 0;
}
value = 0 * veryExpensive() * veryExpensive() * veryExpensive()
Run Code Online (Sandbox Code Playgroud)

所有这些人最终都跑veryExpensive()了三次.

现在,我明白你可以 - 如果你是那种人 - veryExpensive根据你可以依赖它执行的事实来编写你的函数来执行管理开销工作,尽管它的结果没有对算术表达做出贡献(如果你做的话)那,你可能会滥用一种语言,但是在编程生涯中,每个人都喜欢鬼鬼祟祟的忍者代码),但是你只是这样做,因为你知道语言恰好在这种情况下无法优化.如果语言为您优化了算术评估,那么您的代码表达性就不会受到严重影响.

那么:是否有一些历史先例导致当前使用的大量语言优化为"真正的OR ......"和"假的......"而不是"零时间......"?为什么我们优化二进制操作,而不是MUL 0?(如果我们很幸运,有人会有一个引人入胜的故事,告诉我们为什么现在不要短路)

更新

约翰斯基特和尼克布加利斯都提出了很好的论据,为什么用现存的语言来优化这个问题会导致问题,但是尼克的回答更加符合这个问题,所以我将他的答案标记为"正确的".也就是说,它们涵盖了同一问题的不同方面,因此真正的答案是两者的结合.

Jon*_*eet 9

所有这些都最终运行了非常昂贵的()三次.

所以他们应该这样做.

但你只是这样做,因为你知道语言碰巧有趣而不是针对这种情况进行优化.

不,这不是"有趣"没有优化的问题.这是一个优化问题而不是违反语言规范.

如果语言指定操作X * Y首先评估X然后计算Y,然后将这两个值相乘,那么它只是一个不正确的优化,以删除评估Y值是否X恰好为0.

经常有哪家运营商这样的表现,当然-特别是(在C类语言):

  • 有条件的运营商a ? b : c将仅评估要么 b 或者 c
  • x || y这只会评估yif是否x为false
  • x && y这只会评估y是否x属实

并且C#具有null-coalescing运算符x ?? y,该运算符仅计算yif是否x为null.

乘法可以像这样定义,但我怀疑:

  • 大多数乘法运算中,条件执行的分支命中是不值得的.但是你希望这个行为能够明确定义:它短路的还是不是短路的; 它不应该(IMO)未定义.
  • 它使语言更加复杂,无论是指定还是使用.你可能会想,它总是评估两个操作数的"无条件乘"操作(就像x | yx & y是非短路).

基本上,我不认为所有参与者的额外复杂性是值得的.


Nik*_*lis 2

让编译器自动添加运行时检查可能有意义,也可能没有意义。在您的特定情况下,也许检查确实提高了性能,但您的特定情况并不是判断优化的最终情况。

如果乘法非常昂贵并且微处理器没有零乘法的内部优化,并且零乘法的结果保证(例如0 * NaN != 0)为零,那么检查可能有意义。但这是很多和操作数,而且如您所知,您可以将短路。

假设您有一个准随机的数字分布,以及一些未知的零与非零数字比率。根据比率、零和非零序列的游程长度以及处理器的分支预测算法,检查实际上可能会引起问题(即管道停顿)。

仍然认为编译器应该代表您插入这样的检查吗?