是否有编译器能够提出绝对需要程序员批准的优化建议?
如果我们让人类参与循环,编译器是否可以做更多严格的语义等效优化?
有一些潜在的优化被编译器彻底抛弃,因为它们可能在语义上不等价。
然而,它们可能也很好,那么为什么不尝试检测并建议它们呢?检测可能涉及两个阶段的过程:编译时分析阶段和运行时分析阶段。
错误、警告和...建议?
编译器已经对“警告”做了类似的事情,从某种意义上说,它们在每次编译期间都会被汇集起来,并永远保留在列表中,直到您将它们解决到编译器满意为止。为什么不设置一个“建议”或“建议的优化”部分,以类似的方式运行,并且有可能提高应用程序的性能?
如果编译器要分析布尔表达式的复杂性、估计的运行时间、单个操作数为真与假的可能性等,那么它可以创建一个建议列表,例如表达式操作数的更好顺序,并提供建议作为给程序员的列表。然后,程序员可以单独处理它们,并决定忽略它们,或者让代码编辑器实现建议。
优化布尔表达式操作数顺序
考虑“短路逻辑表达式的优化”。因为操作数的顺序影响哪个操作数可能被“短路”(即不被调用),所以简单布尔表达式(即 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”的最佳操作数顺序。更昂贵的电路”。如果编译器可以在编译时、运行时或两者中确定其中任何一个的近似值,那么它可以确定特定表达式可能不是最佳的,并且它可以为程序员创建建议的更改来实现。
今天有编译器做这样的事情吗?如果没有,为什么?
Can compilers do more than strict semantically-equivalent optimizations, if we keep the human in the loop?
There are some potential optimizations that are dismissed outright by compilers, because they may not be semantically equivalent.
However, they may also be fine, so why not attempt to detect and suggest them? Detection could involve a two-stage process: a compile-time analysis stage and a run-time profiling stage.
Errors, Warnings, and... Suggestions?
Compilers already do something similar with "warnings", in the sense that they are pooled during each compilation, and sit there forever in a list until you address them to the compilers satisfaction. Why not have a "Suggestions" or "Suggested Optimizations" section that functions in a similar fashion, and has the potential improve your application's performance?
If a compiler was to analyze Boolean expressions for complexity, estimated runtime, likelihood of individual operands being true vs. false, etc., then it could create a list of suggestions, such as a better order for an expressions operands, and present the suggestions as a list to the programmer. The programmer could then address them individually, and decide to ignore them, or have the code-editor implement the suggestion.
Optimizing Boolean Expression Operand Order
Consider the "Optimization of Short-Circuited Logical Expressions". Because the order of operands affects which operand may be "short-circuited" (i.e. not called), the order of operands in simple Boolean expressions (i.e. A && B && C) is something that (I think) is left untouched by the compiler to avoid introducing unknown side-effects if any operand has side-effects.
Consider this:
char c = reader.ReadChar(); //Stream bs; const string NEWLINE;
while (!IsStringPresent( c, bs, NEWLINE ) && c != ',')
Since comparing a character is (faster/less complex), it should come first in the expression so short-circuit logic can avoid a call to IsStringPresent when a comma is encountered. It's also true, in this case, that commas (many per line) would be encountered more frequently than new line sequences.
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
Summary
Objectively, an optimal operand order could be determined for any expression "A && B" based on "Frequency that A is false vs B to trigger a short-circuit" and "Cost of computing A vs B, favoring short-circuiting of the more costly one". If a compiler could determine approximate values of either of these, at compile-time, run-time, or both, then it could determine that a particular expression may be sub-optimal, and it could create a suggested change for the programmer to implement.
Do any compilers do anything like this today? If not, why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我的观点是,仅仅因为程序员认为可以,就允许优化器短路函数调用,这会导致无法追踪的错误。每次编译时都会询问吗?我不知道;听起来像这样的事情会有很多问题。
My opinion is that allowing the optimizer to short circuit a function call just because a programmer thinks it would be ok, is a recipe for untracable bugs. Does it ask during each compile? I dunno; just sounds like there would be a lot of issues with something like this.
我的回答是“可能”,但是“这是我们真正想做的吗?”
也许您建议编译器检测到可能的同构,但不确定是否是同构,因此它询问程序员“这是同构吗”,但无论如何“如果是这样,如果我应用它,会导致你认为更快的代码吗?”
然而,我的想法更像是这样的:“嘿程序员,如果我们能做到这一点,我们会得到更快的代码,但是,语义是不同的!我们必须加强这个函数的前提条件来进行这种优化可以吗?
加强一个前置条件可能还可以,哎呀,程序员可能根本就没有写前置条件。如果程序员说“当然,没关系”,而程序员错了,那么运气好的话,测试就会发现“前置条件失败”的问题。
My answer is "probably" but "is this what we really want to do?"
Perhaps you're suggesting the compiler detects a possible isomorphism that it isn't sure is an isomorphism and so it asks the programmer "is this an isomorphism", but in any case "if so, if I apply it, will it result in faster code, do you think?"
My thought, however, is more like this: "Hey programmer, if we could do this we'd get much faster code, however, the semantics are different! We would have to strengthen the pre-condition of this function to do this optimisation. Is that OK?"
Strengthening a pre-condition may be OK, heck, the programmer probably didn't even write a pre-condition. If the programmers says "Sure, that's OK" and the programmer is wrong, then with any luck testing would reveal the problem with a "Pre-condition failure".
你问的主要是程序员的任务。在某种程度上,可以扩展诸如静态分析工具或重构工具之类的工具来完成您想要的任务。
编译器的任务是在标准规则范围内(尽可能)。对于某些(激进的)优化,编译器确实提供了特定的标志,可以传递这些标志来启用/禁用它们
例如矢量化、内联、过程间等——这些优化很难手工完成。
重新排序(在您提到的示例中)受到 C、C++ 标准的限制,因为它们可能会更改程序的语义。在操作没有副作用的情况下,可以对它们重新排序,并且编译器在指令调度期间执行此类操作(在 clang++ 和 g++ 中的 -O2 处),但我不确定有多少其他编译器这样做。
What you are asking is mainly the task of a programmer. To some extent there are tools like static-analysis-tools or refactoring-tools which could be extended to accomplish what you want.
Compilers task is to play within the rules of standard (as much as possible). For some (aggressive) optimizations compilers do provide specific flags which can be passed to enable/disable them
e.g. vectorization, inlining, inter-procedural etc. -- these optimizations are difficult to do by hand.
Reordering (in the example you mentioned) is restricted by C, C++ standard because they might change the semantics of the program. In cases when there is no side effect by the operations, it is possible to reorder them and compilers do such stuff during instruction scheduling (at -O2 in clang++ and g++) but I'm not sure how many other compilers do that.