【问题标题】:LLVM and the future of optimizationLLVM 和优化的未来
【发布时间】:2011-10-04 01:27:29
【问题描述】:

我意识到 LLVM 还有很长的路要走,但理论上,GCC/ICC/etc 中的优化可以做到。个别语言是否适用于 LLVM 字节码?如果是这样,这是否意味着任何编译为 LLVM 字节码的语言都有可能同样快?或者语言特定的优化(在 LLVM 字节码阶段之前)是否总是在优化任何特定程序中发挥重要作用。

我对编译器或优化知之甚少(仅够危险),所以如果这个问题没有明确定义,我深表歉意。

【问题讨论】:

  • llvm 实际上做得很好,从版本 28 开始,它生成的代码比 gcc 4.x 更好/更快(对于我测试过的应用程序)。那是将它用作交叉编译器而不是运行时虚拟机。 llvm 作为交叉编译器的优化组合的数量相对于 gcc 可以提供的功能呈指数增长。您可以优化任何单个文件或以任何组合组合字节码并优化该组合。从 C/C++ 优化到字节码或等到字节码链接后等。
  • 我的问题实际上是围绕在优化编译器(C、C++)和尚未引起兴趣或人力的语言(haskell、 d、ocaml 等)来真正进行所需的优化级别,使它们达到与 C 和 C++ 相同的级别。希望 LLVM 能让所有这些语言具有相似的性能,这使得选择一种语言成为一个更开放的选择(对于我的特定领域,即数值模拟和建模,并且需要速度)。
  • 我认为将语言转换为内部语言、字节码或 icode 或其他语言的概念,然后应用并非真正特定于语言的优化列表应该是可行的,然后,当从内部代码转到目标时,您会在那里进行更多优化,这些优化又与原始语言无关。如果这些其他语言可以为 llvm 或 gcc 构建前端,那么它们应该能够利用现有的优化器。
  • @dwelch:这是我最初的想法,但是,看起来,为了获得最佳性能,您需要进行特定于语言的优化(查看 Dietrich 的答案),然后是 LLVM字节码特定的优化。因此,虽然语言的总体性能会有所提高(因为会有一组“llvm 优化”,每种语言都可以利用),但背后有人力的语言最终会更快,因为有更多的人和更多的兴趣在编写语言特定的优化。
  • 绝对同意这一点。你需要人才和人力。而且您需要需求/兴趣,如果没有人愿意使用它,那么您就会失去早期采用者和 beta 测试人员,最终人才/人力会失去兴趣并继续前进。无论好坏,这都是 C/C++ 继续前进的原因。

标签: optimization gcc compiler-construction llvm compiler-optimization


【解决方案1】:

在这里,我找到了一份报告,其中包含 GCC 与 LLVM 差异的简单概述: Clang/LLVM Maturity Evaluation Report by Dominic Fandrey

【讨论】:

    【解决方案2】:

    回到你的问题“理论上可行吗?”, 让我们想象/假设 - 只是为了讨论 - 以下:

    • 我们有字节码,甚至机器码
    • 我们知道它是使用给定的编译器 X 生成的
    • 源语言是L
    • 输入程序的大小是有限的。

    恕我直言 - 从计算机科学的角度来看,应用几乎任何东西都是资源问题。

    现在让我们尝试关注

    function machinecode_to_sourcecode( given_machinecode ){
      it = all_posibble_strings_iterator()
      while (true) {
        possible_sourcecode = it->get_string()
        machine_code = compile possible_sourcecode with X
        if (compilation succeeded)
            if(machine_code == given_machinecode)
               return possible_sourcecode;
        else
           it = it->next_string()
      }
    }
    

    所以我们尝试所有可能的字符串,作为编译器 X 的输入,以防编译成功 - 结果相等,我们有源代码。

    一旦我们有了源代码,我们就会尽可能多地获得有关程序的信息,因此我们能够应用所有可能的优化。

    所有看起来都非常昂贵,但正如你“理论上”所问的那样,我会说

    • 这需要有限的时间

    因为

    • 输入源代码长度有限

    所以

    • 迭代所有此类字符串需要有限的时间
    • 我假设编译器在有限时间内处理任何源代码(这是我们在考虑编译器时通常假设的;))。

    整个操作将花费有限的计算时间。

    【讨论】:

    • 对于任何给定的字节码,都会有无限数量的源版本产生该字节码。作为一个简单的示例,您始终可以将函数包装在其逆 func( inv( func( inv( a ) 中。因此,即使忽略这种方法的实用性,它在理论上也行不通(需要无限时间)。
    • 不一定...当您想找到任何产生给定位码的源时,可以通过在所有可能的有效源代码的空间上设置适当的搜索来实现(在这种相反的情况下,您我提出的可能是一种循环),但不幸的是,我没有确凿的证据或反例来反驳,所以请把它当作直觉(顺便说一句。that 可能你也会感兴趣)
    【解决方案3】:

    这是一个有趣的问题,但我担心你对编译器的作用缺乏一些概念。

    编译器中总会有几个优化阶段。

    • 一些优化取决于语言规则,例如在 C++ 中,您可以优化掉一些复制/移动构造函数调用。
    • 一些优化是广泛可用的(循环转换、尾调用)
    • 某些优化取决于硬件(SSE、MMX)

    LLVM 编译器应用所有 3... 但从 LLVM IR 开始,而不是从 C 或 Java 或其他任何东西开始。

    提供合适的 LLVM IR 是前端的工作。

    例如,正如@Dietrich Epp 所指出的,IR 并不真正适合函数式语言。因此,必须在表示降低到 IR 之前执行许多 Haskell 优化。

    非优化的另一个原因是特定的运行时可能随语言一起提供。 Haskell 有一个复杂的运行时,包括火花池、轻量级线程、系统调用前的抢占、工作窃取等...... IR 不适合表示这种丰富的环境,并且没有对这种组织进行任何优化。

    【讨论】:

    • “IR 并不真正适合函数式语言。”非常错误,但这是旧的,所以没有反对票:)。 julialang.org
    【解决方案4】:

    LLVM 已成为 GCC 的一个有前途的替代品(它甚至成功地编译了 Linux 内核,并带有一些补丁)。在许多情况下,它也比 GCC(编译器和生成的可执行文件)更快,并且其结构使得为任意语言编写前端变得非常容易。

    但是为了进行广泛的优化,前端也同样重要,因为它知道关于正在编译的程序的更多细节。 LLVM 堆栈无法轻易找到的东西。因此,为了生成高效的程序,前端还必须优化代码。

    【讨论】:

      【解决方案5】:

      除了 Dietrich 的出色回答之外,我认为重要的是要认识到,决定编程语言速度的不仅仅是编译器。除了给定语言可能允许/禁止的各种优化之外,还有如何您在各种编程语言中完成某些任务以及该语言允许您做什么的问题做。

      例如,优化 C 代码以最大限度地提高缓存效率(减少从内存中缓慢读取)相对容易,而这在 Haskell 中要困难得多。在 Java 中指针 hack 是不可能的。分配大量内存并手动分配的策略也是如此。

      因此,某些语言总是会因为不允许相同级别的优化而变慢。请注意,我不一定说这是一件坏事,因为随着速度的缓慢会产生极其强大的构造。

      我认为更好的看待它的方法是 LLVM 将允许将一组特定的优化应用于编译到它的所有语言。因此,虽然它将使这些语言更快,但它不会使它们同样快。

      编辑:Haskell 中的指针黑客。这么多可能性...

      【讨论】:

      • 在 Haskell 中可以进行指针攻击。你会得到很多担心的表情,代码会很丑,但你可以做到。 (是的,Haskell 确实有指针类型。)
      【解决方案6】:

      一般来说不会。

      例如, 在 Haskell 中,一个常见的优化是严格性分析,它允许编译器确定哪些变量始终处于 head-normal 形式,因此可以在不改变程序语义的情况下强制 + 内联。这对于 LLVM 是不可能的。

      说明:在 Haskell 中,函数 (Int, Int) -> Int 或多或少等同于 C 中的类型:

      typedef int (*returns_int)();
      struct pair { returns_int first, second};
      typedef struct pair *(*returns_pair)();
      
      int function(returns_pair arg);
      

      编译器可以分析function 并确定它总是评估其参数并总是提取内容,将函数转换为:

      int function(int x, int y); // note that it takes *two* arguments now
      

      这远远超出了 LLVM 的能力。也许,在未来,通过一些非常重的程序间优化......但实际上,这在可预见的未来不会发生。

      示例 2: Java VM 可以将虚拟函数调用转换为直接函数调用。但是,这不是 LLVM 可以做的——因为如果加载了另一个实现相同接口的类,则必须动态撤消这种转换。

      一般而言,当您将程序编译为 LLVM 时,您会丢失有关原始程序的大量语义信息。 LLVM 字节码能够表示任何代码,但它的类型系统相当有限——你选择的类型系统会影响你可以做的优化。

      【讨论】:

      • 因此,LLVM 最终不会在不同语言之间创造公平的竞争环境,或者至少不会完全平衡。在可预见的未来,C 和 C++ 将有更多的人力在前端进行语言特定的优化,这将使它们整体上更快的语言......还是我读错了?
      • 是和不是。我的观点是,LLVM 优化器本身永远不足以与特定语言的优化器竞争。我不打算将语言相互比较。我会犹豫对 C/C++ 编译器与其他语言背后的“人力”做出任何预测。当然,LLVM 不会改变人们是用 C 编写 Web 应用程序(他们大多不这样做)还是用 Python 编写音频编解码器(他们大多不这样做)。但是,如果您可以在 C 代码的 2 倍性能内轻松获得(例如)Haskell 代码,那么证明低级编程的合理性就变得更加困难。
      • 好的,我已经解决了,但我不知道如何解析你编写的 C。 “typedef int (*returns_int)();”是什么意思?意思是?我迷路了。
      • 也许你应该回顾一下 C typedefs?该类型的名称是returns_int,它是一个返回int 的函数。在 C 中的写法是typedef int (*returns_int)(void); 这种定义经常在库中用于回调函数。
      • 你说得对,LLVM 是优化高级语言的一个糟糕选择,因为它会丢失语义信息,但同样值得注意的是,具有所有语义的高级表示程序的信息是执行低级优化的坏地方。倾向于发生的情况是,当程序被编译时,它会通过几个逐渐降低级别的 IR 进行转换。 LLVM 对于像 Haskell 这样的高级语言仍然有用,只是不是作为优化器的第一步。
      【解决方案7】:

      我不知道有关 LLVM 使用的字节码格式的任何详细信息,但我认为您的问题的答案是否定的。

      只需考虑以下几点:动态类型与静态类型。动态类型的编程语言可能会比静态类型的语言慢,因为大多数类型检查是在运行时执行的。

      编程语言之间也可能存在其他一些可能影响性能的差异。

      【讨论】:

      • 在实践中,llvm 捕获的不仅仅是汇编,因此在许多情况下(取决于算法复杂性和可用资源),可以将对高级语言更方便的转换应用于 llvm 字节码本身。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-15
      • 1970-01-01
      • 1970-01-01
      • 2021-02-22
      相关资源
      最近更新 更多