在SO上阅读这个引人入胜(和最高投票的问题),Why is it faster to process a sorted array than an unsorted array?让我对编译器代码的正确性感到疑惑。
例如,答案指出:
英特尔编译器11做了一些奇迹。它interchanges the two loops ......
编译器程序员如何知道何时可以交换循环?
而且,一般来说,他们是否使用数学证明来证明结论?
编译器程序员如何知道他们的编译器会生成正确的代码?他们如何测试他们的结论?他们是否必须编写运行编译器的测试套件,并检查 生成的代码 是否正确?
答案 0 :(得分:4)
编译器程序员如何知道何时可以交换循环?
编译器对代码运行一系列检查,以确定是否可以安全地交换循环。例如,如果代码没有完全内联,则可能无法交换循环。如果代码修改了volatile变量,它将不会交换循环。如果代码存储在先前循环迭代中计算的值,则编译器将不会交换循环。如果他们可以确定它是安全的,因为没有触发这些条件,编译可以交换循环。
而且,一般来说,他们是否使用数学证明来证明结论?
没有。他们只是进行优化和一组保守测试,以确保优化是安全的。随着时间的推移,他们开发了更多优化和更复杂的算法,以检测优化何时安全,即使在不太明显的情况下也是如此。
编译器程序员如何知道他们的编译器会生成正确的代码?
他们尽力而为。偶尔他们会犯错误。人们提交错误报告,并修复它。
他们如何测试他们的结论?他们是否必须编写运行编译器的测试套件,并检查生成的代码是否正确?
他们绝对使用测试套件。当在GCC中检测到错误时,会在测试套件中专门添加一个测试,以确保错误得到修复而不会重新引入。
答案 1 :(得分:2)
编译器程序员如何知道何时可以交换循环?
当修改不会根据语言标准改变程序的行为时,更改不会违反标准本身。
例如,C和C ++标准在一些地方说,函数参数和子表达式的评估顺序是未指定的。这使编译器可以自由地生成代码,以便按照它认为合适的任何顺序对它们进行评估。如果你的程序依赖于特定的顺序,它就不符合标准,你无权责怪编译器“破坏”它。
编译器可能并且经常使用代码分析,逻辑和数学与所有这些定理来优化代码。
在实践中,测试显示编译器是否做得对。
答案 2 :(得分:1)
编译是一个复杂的过程。该程序结构为图形,编译器根据开发人员提出的规则,尽力“优化”该图。
然而,无法保证所生成的代码接近“最佳”。已经对所谓的“超级优化器”进行了研究,试图使用自动验证引擎生成真正优化的代码......也就是说,他们可以回答诸如“有没有办法编译这个算法以便它需要少于X的问题”周期。 Denali是我读过的一个超级优化者。对于某些架构而言,该技术比其他架构更容易。缺点是这些超级优化者可能需要数小时甚至数天来编制一个对大多数人来说无法接受的简单例程。
答案 3 :(得分:1)
使用自己的语言编写的任何编译器的主要健全性测试之一是让它自己编译,然后使用生成的新编译器再次编译自己。两个结果编译器应该是相同的,模数时间戳。
答案 4 :(得分:1)
<强>摘要强>
好的,我同意一些答案,但我认为人们低估了严谨和保守的编译器在转换和优化中的作用。长话短说 - 虽然最佳代码生成确实是一种黑色艺术,99%的时间使用启发式而不是证明(有人谈到Denali超级优化器)正确的代码生成事实上,它是数学声音,它的设计就是这样。存在错误的事实是由于编译器代码库的巨大复杂性。作为一个副节点,自动定理证明器可能有错误,即使它的每个部分都是数学 sound ,因此编译器有错误的事实并不意味着它们是重写规则的随机集合
现在,依次回答您的问题:
编译器程序员如何知道何时可以交换循环?
有一个称为依赖性分析的编译器理论的子域,它处理这样的问题。关于这个主题有整个books,但对于您的特定问题:编译器将自动找出循环的迭代空间(如果边界是可计算的,请参阅 NB < / strong>在底部),它将找出循环指令之间的依赖关系(存在不同类型的依赖关系),它将计算距离和方向向量循环,然后弄清楚它是否合法进行交换。关于此的Wikipedia文章只提供了不同类型的依赖关系,但是链接到其他子主题,并引用了我提到的那本书。
而且,一般来说,他们是否使用数学证明来证明结论?
否,不是一般的。还取决于证据的含义 - 你的意思是使用定理证明吗?例如,Denali论文使用SAT求解器。 Microsoft Research @ Cambridge的Dafny编译器和语言通过源级语言注释来验证您的代码。编译器验证代码的正确性,虽然使用注释是非常困难的(我在Dafny为大学项目提供了一些项目并且从经验中说话)。此外,通过&#34;不是一般的&#34;我的意思是不在gcc
,llvm
家庭,我会怀疑(但我还没有检查过)英特尔icc
。而且,我们在这里谈论的是关于类似C的源到机器级别的编译,没什么更好的。
编译器程序员如何知道他们的编译器会生成正确的代码?他们如何测试他们的结论?他们是否必须编写运行编译器的测试套件,并检查生成的代码是否正确?
其他答案广泛涵盖了这一点,所以没有更多的话要说。我想我可以补充一点,因为编译器在复杂性方面真的是 BEASTS ,所以广泛地测试它们是至关重要的。此外,我想重申,即使在数学上合理的代码片段中(如果我们假设我们已经正式定义了这样的概念),基础架构中可能存在处理该代码的错误,因此测试始终很重要。
<强>判决强>
编译器非常复杂。他们需要快速启用无缝编辑 - 编译 - 调试开发人员循环,以及正确。编译器&#39;他们生成的代码的正确性是在本地数学上的声音&#34; - 编译器执行的每个转换都必须保留原始程序的语义。速度的改进是由启发式算法驱动的,并且这种算法是黑色艺术技术的良好示例,因为它们没有证明所得到的代码会更快。他们也没有给出正确的证据证明它将正确,但每次转换都旨在产生正确的语义保留代码。最后,编译器中最热门的趋势(我猜这些天与计算机科学中的其他地方一样)是使用machine learning
进行优化,因此我们将有更多的旋钮来调整:) yay
<强> NB 强>
编译器中几乎所有内容&#34;后端&#34;即代码生成器,是NP完全或不可判定的。特别是,确定循环交换是否合法是不可判定的,因为循环边界可以是任意表达式(比如函数调用)。但是,如果界限是可计算的(比如整数或简单的东西)那么依赖性分析就会出现这种情况。