【发布时间】:2012-01-10 23:21:08
【问题描述】:
如果我们让人类参与循环,编译器能否做的不仅仅是严格的语义等价优化?
有一些潜在的优化被编译器直接忽略了,因为它们可能在语义上不等效。
但是,它们可能也很好,那么为什么不尝试检测并建议它们呢?检测可能涉及两个阶段的过程:编译时分析阶段和运行时分析阶段。
错误、警告和...建议?
编译器已经对“警告”进行了类似的处理,因为它们在每次编译期间都被汇集在一起,并且永远存在于列表中,直到您将它们解决到编译器满意为止。为什么不设置功能类似的“建议”或“建议的优化”部分,并有可能提高您的应用程序的性能?
如果编译器要分析布尔表达式的复杂性、估计的运行时间、单个操作数是真还是假的可能性等,那么它可以创建一个建议列表,例如表达式操作数的更好顺序,以及将建议作为列表呈现给程序员。然后,程序员可以单独处理它们,并决定忽略它们,或者让代码编辑器实施建议。
优化布尔表达式操作数顺序
考虑“短路逻辑表达式的优化”。因为操作数的顺序会影响哪个操作数可能被“短路”(即未调用),所以简单布尔表达式(即 A && B && C)中的操作数顺序是(我认为)编译器不会改变的东西避免在任何操作数有副作用时引入未知的副作用。
考虑一下:
char c = reader.ReadChar(); //Stream bs; const string NEWLINE;
while (!IsStringPresent( c, bs, NEWLINE ) && c != ',')
由于比较一个字符是(更快/不太复杂),它应该放在表达式的第一位,这样短路逻辑可以避免在遇到逗号时调用 IsStringPresent。同样,在这种情况下,逗号(每行很多)会比换行符更频繁地出现。
char c = reader.ReadChar(); //Stream bs; const string NEWLINE;
if (c != ',' && !IsStringPresent( c, bs, NEWLINE )) //faster for short-circuit; plus ',' is encountered more often than newline
总结
客观地,可以根据“A 与 B 为假以触发短路的频率”和“计算 A 与 B 的成本,有利于短路”来确定任何表达式“A && B”的最佳操作数顺序成本更高的那个”。如果编译器可以在编译时、运行时或两者中确定其中任何一个的近似值,那么它可以确定一个特定的表达式可能是次优的,并且它可以为程序员创建一个建议的更改来实现.
今天有编译器做这样的事情吗?如果不是,为什么?
【问题讨论】:
-
对于您的短路示例,编译器的问题是 IsStringPresent 可能有副作用,不这样做是不正确的。所以是的,编译器不得不问。它必须写下假设列表,并且您必须对其进行审查。一旦该列表变长,它将成为错误的来源:假设发生了变化,您在列表中没有注意到它,这是错误的,现在优化变坏了。如果您必须将答案写成一堆击键,为什么不自己编写对操作数重新排序的击键呢?
-
关于一般的短路:编译器可以做的是根据它的最后一次编译来决定被调用的函数是否有副作用。然后它可以根据最后的编译结果和您建议的操作成本对操作数重新排序。
-
首先,正是因为短路,所以要避免包括副作用。编译器通过避免更改操作数的顺序来保证安全。如果程序员像经常建议的那样安全行事,在我们的短路表达式中不包含隐藏的“必需的副作用”,那么以逻辑等效的方式重新排序操作数应该是安全的。如果是这样的话,我就是从那里来的......那么我们应该优化这个顺序(实际上必须由人类决定),我认为编译器可以帮助我们而不会让它变得非常复杂。
-
如果编译器知道短路表达式中被调用的函数确实没有副作用,则不需要huerstics。它可以以任何顺序评估操作数,因此它应该首先选择最便宜的操作数。问题是,编译器是怎么知道的? a)您断言(可能是错误的),b)编译器检查被调用的函数,c)您声明该函数是无副作用的(FORTRAN“纯”)并且编译器强制执行它。
-
编译器不会尝试检测副作用,这是人类的工作。它会考虑像 (A && B && C && D) 这样的表达式,查看哪些 OP 是最好的“短路器”(即最有可能是 False),并查看评估每个 OP 的成本(简单比较与昂贵的函数调用)在编译时的复杂性、堆栈大小等方面。如果表达式看起来不是最佳的,那么它可以分析实际的运行时评估成本,并最终提出最佳顺序。说“嘿,(B && D && A && C)可能是一个更快的订购!”
标签: compilation compiler-optimization compiler-theory logical-operators short-circuiting