【问题标题】:How much of the compiler should we know?我们应该知道多少编译器?
【发布时间】:2010-09-25 23:40:27
【问题描述】:

为了写出更好的代码,是否值得深入了解编译器的作用?

多少才够?我不是一个有点洗涤器的人,但我在想知道编译器是如何运行的会让我成为一个更好的程序员。我错了吗?

如果是,您会推荐哪些资源?

【问题讨论】:

    标签: compiler-construction


    【解决方案1】:

    知道编译器将如何优化您的代码可能不会有什么坏处,但不要为编译器编写,而是为人们阅读而编写

    以针对编译器更优化的方式编写代码可能会使人们更难以阅读它,而现在编译器可能知道更好地为您优化代码。。 p>

    【讨论】:

    • 有些语言(Javascript、PHP)并没有给你太多优化的方式,所以有必要在可读性和效率之间找到一个折衷方案。
    【解决方案2】:

    在根本没有任何有效性证明的情况下,我对了解我的代码会发生什么感觉更好,因为我只知道一点编译器和一点汇编。通过阅读Jack Crenshaw's Let's Build a Compiler,您可以学到很多东西。

    如果您感兴趣,可以研究更复杂的编译器方法。


    编辑: 同样值得注意的是,许多不需要“编译器”的问题仍然最好由编译器方法解决。解析任何适度复杂的命令语言都是一个编译器问题,即使您不是在编写可执行文件。


    Edit2:许多常见的文本对编译器问题采取了相当抽象的数学方法,起初可能会令人生畏或困惑。 Crenshaw 教程采用了一种“开始敲出代码”的方法,这是由作者更微妙的理解所启发的。很好的介绍,但如果你是认真的,应该跟进更正式的研究。

    【讨论】:

    • 谢谢!我正在研究 Creenshaw 的教程,确实是一个很好的起点!
    【解决方案3】:

    我教过编程语言和高级编译器。以下是我认为了解编译器功能的两个最有用的理由:

    1. 如果您不知道编译器在做什么,您可能会无意中编写出比您预期的要昂贵得多的代码。如果您在不知情的情况下分配内存,则尤其如此。一个经典的例子是在一个循环中连接字符串,例如

      answer = ""

      对于 i = 1 到 n 做

        answer = answer .. strings[i]    -- .. is string concatenation
      

      这段代码是二次的,进行二次分配和复制。坏消息。

    2. 了解编译器的另一个重要原因是,一个问题通常需要一点语言。如果您对编译器有所了解(解释器在这里也一样好,可能更好),那么您可以构建一种小语言。如果您可以选择语言的外观,最好让其他人为您构建语言。 Lua 是一种特别擅长被其他程序用作组件的语言。

    Crenshaw 的教程还不错。另一本好书是 P. J. Brown 关于交互式编译器和解释器的书。它早已绝版,但您可能会在图书馆找到它。

    我会避免阅读大量关于编译器的本科生教科书。 Michael Scott 的Programming-Language Pragmatics 可能对编译器感兴趣的人更有价值。

    【讨论】:

      【解决方案4】:

      我认为它肯定会以一种微妙的方式让你成为一个更好的程序员。

      大致了解它的工作原理将帮助您更加了解您正在编写的代码。我见过很多有经验的开发人员在学习一门新语言时很难理解一些基本概念。如果您大致了解编译器的工作原理以及(可能更重要)代码的执行方式,您将更好地理解这些概念。我说的是堆与堆栈、指针等。

      如果您需要编写代码来分析或翻译某些文本,它也可能会派上用场。我曾经写过一个程序来将一些 sql 条件转换为另一种自定义格式,并为它编写一个小解析器是最简单和最优雅的方法(或者我认为:))

      此外,对编译器的深入了解可能会帮助您专门针对它进行优化,但这可能非常困难,并不总是推荐,正如 coobird 所说。

      【讨论】:

        【解决方案5】:

        我认为每个程序员都应该对编译器如何将高级代码转换为机器指令、可执行的优化、内存的工作原理以及代码在硬件上的执行方式有基本的了解。我认为了解这一点会有所帮助,这样您可以更好地了解程序的性能,并且可以帮助您做出更好的实施选择。

        您是否能够实际编写机器代码或确切了解您的系统使用的虚拟内存架构可能并不重要,但我认为这些概念的基本概念很重要。

        编辑

        例如:C 编译器以行主要格式将数据存储在数组中,因此您应该迭代多维数组,首先改变最高维度(最右边的索引),然后继续到最低维度(最左边的索引)。 Fortran 正好相反,以列主要格式存储数组。这意味着在 Fortran 中,您应该首先更改最低维度,然后再更改最高维度。这将提高代码的缓存命中率,并显着提高大型多维数组的性能。

        【讨论】:

        • 现在两种迭代之间没有性能差异。不知道它为什么或如何发生,但如果你真的尝试它,任何一次迭代都会在现实中花费相同的时间。所以数组示例实际上不再有效。
        • 可能由优化器在 AST 重写级别修复...只是猜测。
        • 这是一个可以通过高级编译器修复的问题,但并非所有编译器都这样做。更有理由了解编译器是如何工作的以及它可以做什么。
        • @Robert -- 我老了,还能说什么。
        • 我记得循环展开通常对性能有好处,那时我们还没有缓存。
        【解决方案6】:

        您对有效使用调试器有兴趣吗?好的。你对编写可靠或高效的代码有兴趣吗?那么是的。

        就我个人而言,我更关心后端而不是前端。我建议为 ARM 而不是 x86 进行编译,在这种情况下,您不必学习汇编程序(我建议您编写自己的反汇编程序),因此如果您使用 gcc,它有一个反汇编程序,您可以看到您的高级代码所做的更改最终结果,以及您可以使用编译器选项进行多少更改。对于大多数高级语言程序员来说,这是一次大开眼界的经历,他们意识到相同的代码可能会根据所使用的编译器和命令行选项产生截然不同的结果。

        对于编译器的中间部分,我推荐 lcc 和 sdcc。你可能想也可能不想买 lcc 书:

        [http://www.cs.princeton.edu/software/lcc/][1]

        不过,您不需要,来源在网络上(有多种形式)。和 sdcc(小型设备 c 编译器,最初为 8051 和其他 8 位微控制器创建)一样。我的建议是进入编译器与后端的接口,你会发现你的代码已经变成了一系列原子部分,有时像反向抛光一样。 a = b + 7;最终可能会加载常量整数 7. 从内存中将变量 b 读取到下一个可用寄存器中。将 7 加上寄存器 b 并保存在下一个可用寄存器中。将寄存器中的值存储到内存中a的位置。

        您也可以使用 gcc 来做到这一点,但您最终可能会意识到 gcc 并没有您想象的那么好。由于语言的数量和后端的数量以及混合的手数等,它庞大而复杂。不过,它确实可以通过,并且确实适用于有专家维护的语言和平台。 gcc 可以教给你的,其他人无法做到的是,前端的各种语言会归结为通用的中间语言,后端会变成针对每个平台的特定指令。

        最后是前端。在大多数情况下,人们使用 bison/yacc,这是一个工具,您可以为高级语言创建描述,该工具可以根据您的描述解析用户输入,如果您愿意,可以将其转换为这种中间语言。

        如果您计划将自己的爱好或职业与编写软件有关,我会说您必须进行此练习一次,如果不是多次。你的代码的整体质量、代码的可靠性、代码的性能以及编写代码的效率都会受到这些知识的影响。

        我会小心“不要为编译器编写,而是为人们阅读而编写”的说法。那里有很多糟糕的代码,因为这种语句被滥用了。为可维护性编写代码会导致必须维护的错误代码。可维护性与可靠性和性能是相互排斥的。我个人会比任何大学毕业生都可以维护的糟糕代码具有可靠性和性能。

        随着时间的推移,您将学会不要太努力地为编译器编写代码。只是不要浪费您的代码,不要使用该语言的 gee whiz 功能。如果你不得不做额外的研究来找出一些编译器特性,你可以确定世界上大多数人都不理解它,包括应该在编译器中实现它的人。因此,您可以预期该功能不会在编译器中始终如一地工作,因此您应该首先使用它。这也意味着不要尝试为一个特定的编译器编写代码,不要太依赖 gcc 及其功能,尝试 sdcc 和 lcc 以及 microsoft 和 borland 和 kiel 等。使您的代码干净、简单、可读且可移植。

        归根结底,如果您认真编写软件,那么您绝对需要了解编译器的工作原理。 gcc、sdcc、lcc(和 vbcc,如果你能找到的话)都是免费的、开源的,并且提供了可以提高你的编码技能的学习体验。

        【讨论】:

          【解决方案7】:

          我认为编译器所做的事情在这里很重要(它创建了一个具有 x,y,z 特征的解释)这被转化为了解您所针对的平台。

          它执行该任务的方式无关紧要(当然,除非您正在编写编译器)

          关于编译器,我们应该知道的最重要的事情是它显示的错误消息。

          :) 看起来很明显,但我对我遇到的甚至没有看编译器输出的开发人员数量感到惊讶。

          【讨论】:

          • 我不同意不需要知道编译器做了什么。 1000 次中有 999 次你是对的。但是,如果您对幕后发生的事情一无所知,那么最后一个是真正的熊。
          【解决方案8】:

          不要学习编译器,要学习它们解决的问题。

          【讨论】:

            【解决方案9】:

            我认为了解编译器的工作原理并不像不断提高自己的编程知识那样有必要。现在,学习编写编译器(或其背后的原理)恰好是扩展知识的好方法。

            如果您有兴趣,我建议您购买 Dragon Book,也称为Compilers: Principles, Techniques and Tools。第一次可能会有点沉重,但它肯定会让你思考。如果您没有完全通过或卡在某些部分,我建议您将其搁置一段时间然后再返回 - 第二次通过会容易得多。

            【讨论】:

              【解决方案10】:

              作为一名真正全面的开发人员,我认为您应该对编译器及其工作原理有相当多的了解,甚至可以尝试编写一个简单的编译器。

              然而,对于特定的编译器,大多数开发人员只需知道一件事就可以逃脱:编译器切换到输出汇编语言而不是二进制代码。检查输出的汇编语言将准确地告诉您编译器正在做什么来优化您的代码,并可以帮助您弄清楚如何重新编写代码以使其更好地工作。
              另外,它是一种有趣的“程序员练习”,可以刷新您的低级知识。

              【讨论】:

                【解决方案11】:

                在一篇博文中,Steve Yegge 断言所有程序员 should know how compilers work。他竟然说:

                温柔但坚持不懈的执行官 摘要:如果你不知道怎么做 编译器工作,那么你不知道 计算机是如何工作的。如果你不是 100% 确定你是否知道编译器 工作,那么你不知道他们如何 工作。

                在文章中,他提出了一个令人信服的论点,即需要了解编译器。他还提供了现实世界示例的列表,了解如何解析和分析将是有用的。

                【讨论】:

                • 不错。我读了那篇博客文章,这是一篇很棒的文章!您使用什么资源了解了更多关于编译器的信息? (除了大学课程)
                • 老实说,自 collage 以来,我对编译器的工作并不多。 Yegge 的帖子激励我重新开始。我读过 F# 是一个很好的解析器。所以我想我会阅读 Jack Crenshaw (compilers.iecc.com/crenshaw) 的 Let's Build a Compiler,然后尝试用 F# 编写 DSL 编译器。
                【解决方案12】:

                我认为真正真正重要的是做一个解释器:它让你对编程语言有更多的了解,这就是你使用的东西……在方案中,做一个解释器实际上并不难!但实际上我会非常鼓励阅读SICP 的部分内容以获得很大的启发)。

                关于编译器,它更复杂,因为这里的重点是为实际机器获得一些性能/执行它。作为程序员,重要的是至少要知道他们在全球范围内执行了哪些任务以及它们何时运行,而不是细节,因为现在它们已经发展成为非常复杂的系统,尤其是 JIT 等......

                【讨论】:

                  【解决方案13】:

                  至少,您应该熟悉抽象级别的语言功能。如果您不知道变量名称是否区分大小写,或者数字如何转换为布尔值,那么您甚至可能无法可靠地编写一个简单的“if”子句。

                  大多数情况下,我发现有关编译器内部工作的任何其他知识都有助于我编写更高效的代码。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-01-26
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-07-04
                    • 2018-02-04
                    相关资源
                    最近更新 更多