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?(如果我们很幸运,有人会有一个引人入胜的故事,告诉我们为什么现在不要短路)
更新
约翰斯基特和尼克布加利斯都提出了很好的论据,为什么用现存的语言来优化这个问题会导致问题,但是尼克的回答更加符合这个问题,所以我将他的答案标记为"正确的".也就是说,它们涵盖了同一问题的不同方面,因此真正的答案是两者的结合.
所有这些都最终运行了非常昂贵的()三次.
所以他们应该这样做.
但你只是这样做,因为你知道语言碰巧有趣而不是针对这种情况进行优化.
不,这不是"有趣"没有优化的问题.这是一个优化问题而不是违反语言规范.
如果语言指定操作X * Y首先评估X然后计算Y,然后将这两个值相乘,那么它只是一个不正确的优化,以删除评估Y值是否X恰好为0.
经常有哪家运营商做这样的表现,当然-特别是(在C类语言):
a ? b : c将仅评估要么 b 或者 cx || y这只会评估yif是否x为falsex && y这只会评估y是否x属实并且C#具有null-coalescing运算符x ?? y,该运算符仅计算yif是否x为null.
乘法可以像这样定义,但我怀疑:
x | y和x & y是非短路).基本上,我不认为所有参与者的额外复杂性是值得的.
让编译器自动添加运行时检查可能有意义,也可能没有意义。在您的特定情况下,也许检查确实提高了性能,但您的特定情况并不是判断优化的最终情况。
如果乘法非常昂贵并且微处理器没有零乘法的内部优化,并且零乘法的结果保证(例如0 * NaN != 0)为零,那么检查可能有意义。但这是很多和操作数,而且如您所知,您可以将和短路。
假设您有一个准随机的数字分布,以及一些未知的零与非零数字比率。根据比率、零和非零序列的游程长度以及处理器的分支预测算法,检查实际上可能会引起问题(即管道停顿)。
您仍然认为编译器应该代表您插入这样的检查吗?