Algorithm 编译器的Big-O

Algorithm 编译器的Big-O,algorithm,compiler-construction,Algorithm,Compiler Construction,有没有人能洞察编译器典型的大O复杂性 我知道它必须是=n(其中n是程序中的行数),因为它需要至少扫描每行一次 我相信对于过程语言来说,它也必须是=n.logn,因为程序可以引入O(n)变量、函数、过程和类型等,当这些在程序中引用时,需要O(logn)来查找每个引用 除此之外,我对编译器体系结构的非正式理解已经达到了极限,我不确定前向声明、递归、函数语言和/或其他技巧是否会增加编译器的算法复杂性 因此,总而言之: 对于“典型”过程语言(C、pascal、C#等),对于高效设计的编译器(作为行数的度

有没有人能洞察编译器典型的大O复杂性

我知道它必须是
=n
(其中n是程序中的行数),因为它需要至少扫描每行一次

我相信对于过程语言来说,它也必须是
=n.logn
,因为程序可以引入O(n)变量、函数、过程和类型等,当这些在程序中引用时,需要O(logn)来查找每个引用

除此之外,我对编译器体系结构的非正式理解已经达到了极限,我不确定前向声明、递归、函数语言和/或其他技巧是否会增加编译器的算法复杂性

因此,总而言之:

  • 对于“典型”过程语言(C、pascal、C#等),对于高效设计的编译器(作为行数的度量)是否存在限制性的大O

  • 对于“典型”函数式语言(lisp、Haskell等),对于高效设计的编译器(作为行数的度量),是否存在一个限制性的big-O

  • 据我所知: 它取决于编译器在解析步骤中使用的解析器类型。
    解析器的主要类型是LL和LR,它们都有不同的复杂性。

    回到我在compilers类中能记住的东西。。。这里的一些细节可能有点离题,但总体要点应该非常正确

    事实上,大多数编译器都经历了多个阶段,因此缩小问题的范围会很有用。例如,代码通常通过标记器运行,该标记器几乎只创建对象来表示尽可能小的文本单位<代码>变量x=1将被拆分为var关键字、名称、赋值运算符和文字编号的标记,后跟语句终结器(“;”)。大括号、圆括号等都有自己的标记类型

    标记化阶段大致为O(n),尽管在关键字可以是上下文的语言中这可能会很复杂。例如,在C#中,像
    from
    yield
    这样的词可以是关键字,但也可以用作变量,这取决于它们周围的内容。因此,根据您在语言中进行了多少类似的工作,以及正在编译的特定代码,可以想象,仅此第一阶段的复杂性为O(n²)。(尽管这在实践中非常罕见。)

    在标记化之后,还有解析阶段,在这个阶段中,您尝试匹配开始/结束括号(或某些语言中的等效缩进)、语句终结器等等,并尝试理解标记。这是您需要确定给定名称是否表示特定方法、类型或变量的地方。明智地使用数据结构来跟踪在不同范围内声明了哪些名称,在大多数情况下,这项任务几乎是O(n),但也有例外

    在我看过的一个视频中,Eric Lippert说正确的C代码可以在用户击键之间的时间内编译。但是如果您想提供有意义的错误和警告消息,那么编译器必须做更多的工作


    解析后,可能会有许多额外的阶段,包括优化、转换为中间格式(如字节码)、转换为二进制代码、即时编译(以及可在该点应用的额外优化)等。所有这些都相对较快(大部分时间可能是O(n)),但这是一个如此复杂的话题,以至于即使是一种语言也很难回答这个问题,而对于一种语言类型来说,回答这个问题几乎是不可能的。

    这个问题在目前的形式下是无法回答的。编译器的复杂性当然不能用源文件中的代码行或字符来衡量。这将描述解析器或词法分析器的复杂性,但编译器的任何其他部分都不会触及该文件

    解析之后,所有内容都将以各种AST的形式以更结构化的方式表示源文件。编译器将拥有大量中间语言,每种语言都有自己的AST。不同阶段的复杂性取决于AST的大小,它与字符数甚至与前一个AST根本不相关

    考虑到这一点,我们可以在线性时间内将大多数语言解析为字符数,并生成一些AST。对于具有
    n
    叶子的树,类型检查等简单操作通常是
    O(n)
    。但是,我们将把这个AST转换成一种形式,与原始树上的节点相比,可能有两倍、三倍甚至指数级的节点。现在,我们再次在树上运行单遍优化,但这可能是相对于原始AST的
    O(2^n)
    ,天知道字符数是多少

    我想你会发现,对于一个编译器来说,由于某种复杂性,甚至不可能找到
    n
    应该是什么


    就像棺材上的钉子一样,编译一些语言是不可判定的,包括java、C#和Scala(事实证明,名义上的子类型+变异会导致不可判定的类型检查)。当然,C++的模板系统是图灵完备的,这使得可判定编译相当于停止问题(不可判定)。Haskell+某些扩展是不可判定的。还有很多其他我想都想不出来的。这些语言的编译器不存在最坏情况下的复杂性。

    行数不是要完成的工作量的精确指标。一段代码可以被引导到一行代码中,其复杂性与未引导的版本相当,高达数百行。为什么您认为需要O(logn)来查找对给定变量/函数/类型名称的引用?同意,使用哈希表可以在O(1)投票中查找,以关闭“太宽”的A