像Ruby / Python这样的动态语言能否达到性能一样的C / C ++?


64

我想知道是否可以为像Ruby这样的动态语言构建编译器,使其具有与C / C ++类似和可比的性能?根据我对编译器的了解,以Ruby为例,由于Ruby处理反射的方式,诸如从整数到大整数的自动类型转换以及缺少静态类型等特性,使得编译Ruby代码永远不可能高效地进行编译。对于Ruby来说非常困难。

是否有可能构建一个可以将Ruby或任何其他动态语言编译为与C / C ++非常接近的二进制文件的编译器?是否有根本的原因导致JIT编译器(例如PyPy / Rubinius)最终或永远无法在性能上与C / C ++相匹配?

注意:我确实知道“性能”可能是模糊的,所以要澄清一下,我的意思是,如果您可以在C / C ++中以X的性能执行X,那么您可以在Ruby / Python中以接近Y的性能执行X吗?X是从设备驱动程序和OS代码到Web应用程序的所有内容。


1
您能否改写这个问题,以鼓励得到适当证据支持的其他答案?
拉斐尔

@Raphael我已经进行编辑。我认为我的编辑并没有从根本上改变问题的含义,但是却减少了意见的吸引力。
吉尔斯2012年

1
特别是,我认为您需要修复一个(或几个)具体的性能指标。运行?空间使用率?能源消耗?开发时间?投资回报?在我们的元数据上还要注意与该问题(或更确切地说是答案)有关的问题。
拉斐尔

这个问题是宗教战争的典型发起者。从答案中我们可以看到,尽管有一个非常文明的答案,但我们有一个。
Andrej Bauer 2013年

有动态语言允许可选的类型注释(例如:Clojure)。据我所知,与带类型注释的函数相关的性能等同于对语言进行静态键入的时间。
Pedro Morte Rolo

Answers:


68

对于所有说“是”的人,我将提出一个反驳,即答案是“不”。这些语言将永远无法匹敌静态编译语言的性能。

Kos提供了(非常有效的)观点,即动态语言在运行时具有有关系统的更多信息,可用于优化代码。

但是,还有另外一面:需要跟踪这些附加信息。在现代体系结构上,这是性能杀手。

威廉·爱德华兹(William Edwards)提供了很好的观点综述

特别是,除非像Devin提到的那样,大幅限制语言的表达能力,否则 Kos提到的优化不能在非常有限的范围内应用。当然,这是一个可行的折衷方案,但是为了便于讨论,您最终只能使用静态语言,而不是动态语言。这些语言从根本上不同于Python或Ruby,因为大多数人会理解它们。

William引用了一些有趣的IBM幻灯片

  • 每个变量都可以动态键入:需要类型检查
  • 由于类型不匹配等原因,每个语句都可能引发异常:需要进行异常检查
  • 可以在运行时添加,删除和更改每个字段和符号:需要访问检查
  • 可以在运行时更改每个对象的类型及其类层次结构:需要类层次结构检查

分析之后可以消除其中一些检查(注意:此分析需要时间-在运行时)。

此外,科斯认为动态语言甚至可以超越C ++性能。JIT确实可以分析程序的行为并进行适当的优化。

但是C ++编译器可以做同样的事情!现代编译器提供所谓的配置文件引导的优化,如果给它们适当的输入,它们可以对程序运行时行为进行建模,并应用与JIT相同的优化。

当然,所有这些都取决于实际训练数据的存在,此外,如果使用模式在运行中发生变化,则程序将无法适应其运行时特性。理论上,准时制可以解决这个问题。我很想看看这种做法的实际情况,因为为了进行优化,JIT将不得不不断收集使用情况数据,这再次降低了执行速度。

总之,与静态分析和优化相比,我不认为运行时热点优化从长远来看超过跟踪运行时信息的开销


2
@Raphael这是编译器的“缺点”。特别是,javac是否曾进行过基于配置文件的优化?据我所知。通常,使JITted语言的编译器很好地进行优化是没有意义的,因为JIT可以处理它(至少,这样,更多的语言将从中受益)。因此(javac据我所知),就我所知,从未在优化器上投入太多精力(对于.NET语言,这绝对是正确的)。
康拉德·鲁道夫2012年

1
@Raphael关键字是:也许吧。它没有显示任何一种方式。这就是我想说的。在前面的段落中,我已经给出了假设(但没有证据)。
康拉德·鲁道夫2012年

1
@Ben我不否认这很复杂。这仅仅是一种直觉。毕竟,在运行时跟踪所有这些信息都是有代价的。您对IO的观点,我不相信。如果这是可以预测的(=典型用例),则PGO可以预测。如果是虚假的,我也不相信JIT可以优化它。有时可能是出于偶然。但是可靠吗?…
康拉德·鲁道夫2012年

2
@Konrad:你的直觉是错误的。这与运行时的变化无关,而与编译时不可预测性有关。JIT与静态优化的最佳结合点不是当程序的性能在运行时因“太快”而无法进行性能分析时,而是当程序的性能在每次单独运行时都易于优化时,但在每次运行时都存在很大差异运行。静态优化器通常只需要针对一组条件进行优化,而JIT则针对该运行中发生的条件分别优化每个运行。
2012年

3
注意:此注释线程正在开发为迷你聊天室。如果您想继续此讨论,请聊天。评论应用于改善原始帖子。请在此处中断此对话。谢谢。
罗伯特·卡塔诺

20

如果您可以在C / C ++中以X的性能执行X,那么您可以在Ruby / Python中以接近Y的性能执行X吗?

是。以PyPy为例。它是Python代码的集合,在解释方面与C的表现非常接近(不是所有的都那么接近,但也不是那么遥远)。它通过对源代码执行完整的程序分析来为每个变量分配一个静态类型(有关详细信息,请参见AnnotatorRtyper文档),然后在为您提供C的相同类型信息后,便可以执行相同的操作。各种优化。至少在理论上。

当然,要权衡的是RPython只能接受Python代码的一个子集,并且通常,即使解除了该限制,也只有Python代码的一个子集可以做得很好:可以被分析并具有静态类型的子集。

如果对Python的限制足够多,则可以构建可以利用受限子集并将其编译为有效代码的优化器。实际上,这并不是一个有趣的好处,众所周知。但是首先使用Python(或Ruby)的全部目的是我们想要使用有趣的功能,这些功能可能无法很好地进行分析并导致良好的性能!所以有趣的问题实际上是...

另外,JIT编译器(例如PyPy / Rubinius)在性能上是否会匹配C / C ++?

没事

我的意思是:当然,也许随着代码运行的积累,您可以获得足够的键入信息和足够的热点来编译所有代码,直到机器代码为止。对于某些代码,也许我们可以获得比C更好的性能。我不认为这有很大争议。但是它仍然必须“热身”,性能仍然难以预测,对于某些需要一致且可预测的高性能的任务,它的性能不如C或C ++。

Java的现有性能数据比Python或Ruby具有更多的类型信息,并且比Python或Ruby具有更好的JIT编译器,但仍无法与C / C ++匹配。但是,它在同一球场。


1
“当然,需要权衡的是,只有一部分Python代码可以被接受,或者更确切地说,只有一部分Python代码可以做得很好:可以被分析并具有静态类型的子集。” 这不太准确。RPython编译器完全只接受子集。使用RPython编译为相当有效的C语言,仅因为保证了Python难编译的部分永远不会出现在RPython程序中,才可以正常工作。不仅仅是如果它们发生,它们将不会被优化。它是处理所有Python的已编译解释器。
2012年

1
关于JIT:我已经看到一个以上的基准测试,其中Java超过了几种C(++)风格。只有带有boost的C ++似乎可以可靠地保持领先地位。在那种情况下,我想知道每个开发人员时间的性能,但这是另一个主题。
拉斐尔

@Ben:一旦有了RPython,创建一个编译器/解释器就很简单了,当RPython编译器失败时,它会回退到使用CPython解释器,因此“只有一部分Python代码可以做得很好:...”是完全准确的。
Lie Ryan

9
@Raphael多次显示,编写良好的 C ++代码胜过Java。这是“写得很好”的部分,但是在C ++中要难得多,因此在许多工作台上,您会看到Java优于C ++的结果。因此,C ++更加昂贵,但是当需要严格的内存控制和粗砂时,您会使用C / C ++。特别是C只是ac / p汇编程序。
TC1 2012年

7
将其他语言的最大性能与C / C ++之类的语言进行比较是徒劳的,因为您可以直接内联汇编作为语言规范的一部分。机器可以运行以任何语言编写的程序的任何事情,最坏的情况是,您可以通过跟踪执行的指令编写汇编程序来进行复制。正如@Raphael在较早的评论中所建议的那样,一个更有趣的度量标准是每个开发工作的性能(工时,代码行等)。
Patrick87 2012年

18

简短的答案是:我们不知道,请在100年后再问一次。(那时我们可能仍然不知道;也许我们永远不会知道。)

从理论上讲,这是可能的。拿走曾经编写的所有程序,手动将它们翻译为最有效的机器代码,然后编写一个解释器,将源代码映射到机器代码。这是可能的,因为仅编写了有限数量的程序(并且随着编写更多程序,请保持手动翻译)。当然,从实际意义上讲,这也是完全愚蠢的。

再说一次,从理论上讲,高级语言也许能够达到机器代码的性能,但它们不会超越机器语言。这仍然是非常理论上的,因为实际上,我们很少诉诸于编写机器代码。此参数不适用于比较高级语言:并不意味着C必须比Python更高效,只有机器代码的性能不能比Python差。

从另一方面来看,从纯粹的实验角度来看,我们可以看到,大多数情况下,解释性高级语言的性能要比编译后的低级语言差。我们倾向于以非常高级的语言和对时间要求严格的内部循环在汇编中编写对时间不敏感的代码,介于两者之间的是C和Python。尽管我没有任何统计数据可以支持这一点,但我认为在大多数情况下这确实是最好的决定。

但是,在一些毫无争议的实例中,高级语言击败了人们实际上会写的代码:特殊用途的编程环境。Matlab和Mathematica之类的程序通常在解决某些数学问题上比单纯的凡人所能写的要好得多。库函数可能是用C或C ++编写的(这有助于“低级语言更有效”阵营),但是如果我正在编写Mathematica代码,那不关我的事,库是一个黑匣子。

从理论上讲,Python能否比C达到或接近最佳性能?如上所示,是的,但是我们离今天还很遥远。再说一次,在过去的几十年中,编译器取得了很大的进步,而且这种进步并没有放缓。

高级语言倾向于使更多的事情自动化,因此它们需要执行更多的工作,因此效率较低。另一方面,它们往往具有更多的语义信息,因此可以更容易地发现优化(如果您正在编写Haskell编译器,则不必担心其他线程会在您的鼻子下修改变量)。比较苹果和橙子不同编程语言的几种努力之一是“ 计算机语言基准测试”(以前称为枪战)。Fortran倾向于在数字任务上大放异彩。但是在处理结构化数据高速线程交换时,F#和Scala表现出色。不要将这些结果当作福音:它们所衡量的很多东西是每种语言的测试程序作者的水平。

支持高级语言的一个论点是,现代系统上的性能与执行的指令数量并没有那么紧密的联系,随着时间的推移,这种联系就不那么紧密了。低级语言很适合简单的顺序机器。如果高级语言执行两倍的指令,但设法更智能地使用高速缓存,那么它会造成一半的高速缓存未命中,那么它可能会成为赢家。

在服务器和台式机平台上,CPU几乎达到了一个稳定的水平,在此期间它们无法获得更快的速度(移动平台也可以到达那里)。这适合于易于利用并行性的语言。许多处理器将大部分时间都花在等待I / O响应上。与I / O量相比,花在计算上的时间没什么大不了的,而允许程序员最小化通信的语言是一个优势。

总而言之,尽管高级语言从惩罚开始,但它们还有更多的改进空间。他们能接近多远?100年后再问一次。

最后说明:通常,比较不是在可以用语言A 编写的最高效程序与在语言B相同的编写程序之间进行比较,也不是用每种语言编写的最高效程序之间进行比较,而是在可以编写的最高效程序之间进行比较。由人类在一定时间内使用每种语言编写。这引入了即使在原则上也无法进行数学分析的元素。实际上,这通常意味着,最佳性能是在为满足性能目标而需要编写的低级代码与为发布日期而有时间编写的低级代码之间的折衷。


我认为高层次和低层次之间的区别是错误的。C ++可以(非常)高级。但是,现代的高级C ++的性能(不一定)不会比低级的C ++差-恰恰相反。C ++及其库经过精心设计,可提供高级抽象,而不会降低性能。Haskell示例也是如此:其高级抽象通常可以启用而不是阻止优化。在这方面,动态语言和静态语言之间的原始区别更有意义。
康拉德·鲁道夫2012年

@KonradRudolph是的,在低级/高级之间有些随意。但是动态和静态语言也不能捕获所有内容。JIT可以消除很多差异。本质上,该问题的已知理论答案是琐碎且无用的,而实际答案是“取决于”。
吉尔斯2012年

那么,我认为问题就变成了“ JIT能够变得多么好,如果它们超越了静态编译,那么静态编译语言也可以从中受益吗?”至少一旦我考虑了JIT,我便会理解这个问题。是的,我同意您的评估,但可以肯定的是,我们可以得到一些超出“取决于”范围的知情猜测。;-)
Konrad Rudolph

@KonradRudolph如果我想猜测,我会询问Software Engineering
吉尔斯2012年

1
不幸的是,语言大战是量化基准的一个可疑来源:他们不接受所有程序,仅接受那些被认为是典型语言的程序。这是一个棘手且非常主观的要求。这意味着您不能假设点射式实现实际上是没有用的(实际上,某些实现显然拒绝了其他更好的选择)。另一方面; 这些都是微基准测试,有些实现使用了不寻常的技术,您通常不会在更普通的情况下考虑使用这些技术来赢得性能。所以:这是一个游戏,而不是非常可靠的数据源。
Eamon Nerbonne'4

10

C ++的语句之间的基本区别x = a + b和Python的说法x = a + b是,C / C ++编译器可以从这句话告诉(和它有现成的关于类型的一些额外的信息xab)恰恰需要执行什么样的机器代码。告诉Python语句将要执行的操作是什么,您需要解决Halting问题。

在C语言中,该语句基本上将编译为几种类型的机器加法中的一种(C编译器知道哪一种)。在C ++中,它可能会以这种方式进行编译,或者可能会编译为调用静态已知函数,或者(最坏的情况)可能是必须编译为虚拟方法查找和调用,但是即使这样,其机器代码开销也很小。但是,更重要的是,C ++编译器可以从涉及的静态已知类型中判断出它是否可以发出单个快速加法运算或是否需要使用较慢的选项之一。

在Python中,编译器理论上可以做到几乎是很好的,如果它知道abint秒。还有一些额外的装箱开销,但是如果静态地知道类型,您也可能会摆脱它(同时仍然提供接口,即整数是具有方法的对象,超类的层次结构等)。问题是Python的编译器无法知道这一点,因为类是在运行时定义的,可以在运行时进行修改,甚至执行定义和导入的模块也可以在运行时解析(甚至执行哪个import语句取决于只能在运行时知道的事情)。因此,Python编译器将必须知道已执行了哪些代码(即解决停止问题),才能知道其正在编译的语句将执行什么操作。

因此,即使使用理论上可能进行的最复杂的分析,您也无法提前知道给定Python语句将要做什么。这意味着,即使实施了复杂的Python编译器,在几乎所有情况下,它仍必须发出遵循Python字典查找协议的机器代码来确定对象的类和查找方法(遍历类层次结构的MRO,它也可以在运行时动态更改,因此很难编译成简单的虚拟方法表),并且基本上可以完成(慢速)解释器的工作。这就是为什么实际上没有任何复杂的针对动态语言的优化编译器的原因。创建一个不仅困难,最大可能的回报是“

请注意,这不是基于什么代码干什么的,它是基于什么样的代码可以做的事情。即使是一系列简单的整数算术运算的Python代码也必须进行编译,就好像它正在调用任意的类运算一样。静态语言对代码可能执行的操作有更大的限制,因此,它们的编译器可以做出更多的假设。

JIT编译器通过等待运行时进行编译/优化来获得收益。这样一来,他们就可以发出适用于代码正在执行的代码而不是代码可能正在执行的代码。因此,JIT编译器对动态语言的潜在收益要比对静态语言大得多。对于更多的静态语言,优化人员想知道的很多事情可以提前知道,因此您不妨对其进行优化,而为JIT编译器做的工作更少。

有多种用于动态语言的JIT编译器,它们声称可以达到与经过编译和优化的C / C ++相当的执行速度。对于任何一种语言,JIT编译器甚至都存在无法通过提前编译器完成的优化,因此从理论上讲,对于某些程序,JIT编译(对于某些程序)可能胜过最佳的静态编译器。但是正如Devin正确指出的那样,JIT编译的属性(只有“热点”是快速的,并且只有在预热期之后),这意味着JIT编译的动态语言不太可能适用于所有可能的应用程序,即使它们变得通常比静态编译语言快或快。


1
现在是两票无评论的反对票。我欢迎提出有关如何改善此答案的建议!
2012年

我没有投票,但是您对“需要解决暂停问题”的看法不正确。在许多情况下,已经证明可以将动态语言中的代码编译为最佳目标代码,而据我所知,这些演示都没有提供解决暂停问题的方法:-)
mikera

@mikera对不起,但不,您不正确。没有人曾经为完全通用的 Python或其他动态语言实现过编译器(就我们理解GCC是编译器而言)。每个这样的系统仅适用于语言的子集或仅适用于某些程序,或者有时发出的代码基本上是包含硬编码程序的解释器。如果愿意,我将为您编写一个Python程序,其中包含以下行:foo = x + y在该行中,预测加法运算符在编译时的行为取决于解决暂停问题。
2013年

我是正确的,我认为您没有抓住重点。我说“在许多情况下”。我没有说“在所有可能的情况下”。在现实世界中,是否可以构建与暂停问题相关的人为设计的示例在很大程度上无关紧要。FWIW,您也可以为C ++构建一个类似的示例,因此无论如何您都不会证明任何事情。无论如何,我并不是来这里争​​论的,只是建议您改善答案。要么接受,要么离开它。
mikera

@mikea我认为您可能会错过重点。为了进行x + y高效的机器添加操作进行编译,您需要在编译时知道是否需要。所有时间,而不仅仅是某些时间。对于动态语言,即使合理的启发式方法在大多数情况下都是正确的,但对于现实的程序而言,这几乎是不可能的。编译需要编译时保证。因此,通过谈论“在许多情况下”,您实际上根本无法解决我的问题。
2013年

9

只是快速的指针概述了动态语言的最坏情况:

Perl解析不可计算

结果,(完整的)Perl 永远不会被静态编译。


通常,这取决于往常。我有信心,如果您尝试使用静态编译的语言模拟动态功能,那么构思良好的解释器或(部分)编译的变体可能会接近或削弱静态编译语言的性能。

要记住的另一点是,动态语言解决了 C之外的另一个问题。对于动态汇编程序而言,C不仅是不错的语法,而动态语言则提供了丰富的抽象。运行时性能通常不是主要的考虑因素:例如,上市时间取决于开发人员能否在短时间内编写复杂的高质量系统。无需重新编译即可扩展的功能(例如使用插件)是另一个受欢迎的功能。在这种情况下,您更喜欢哪种语言?


5

为了对这个问题提供更客观的科学答案,我提出以下观点。动态语言需要解释器或运行时在运行时做出决策。该解释器或运行时是一个计算机程序,因此是用某种静态或动态编程语言编写的。

如果解释器/运行时是用静态语言编写的,则可以用该静态语言编写一个程序,该程序(a)执行与其解释的动态程序相同的功能,并且(b)至少也执行同样的功能。希望这是不言而喻的,因为要提供这些索赔的严格证据将需要额外的(可能是相当大的)努力。

假设这些主张是正确的,唯一的出路就是要求解释器/运行时也必须以动态语言编写。但是,我们遇到了与以前相同的问题:如果解释器是动态的,则它需要一个解释器/运行时,它也必须以动态或静态编程语言编写。

除非您假设某个解释器的实例能够在运行时解释自身(我希望这是不言而喻的),否则击败静态语言的唯一方法是让每个解释器实例由一个单独的解释器实例进行解释;这导致无限回归(我希望这是不言而喻的荒谬)或封闭的解释器循环(我希望这也是不言而喻的荒谬)。

这样看来,即使从理论上讲,动态语言的性能通常也不会比静态语言好。当使用现实计算机的模型时,似乎更加合理。毕竟,机器只能执行机器指令序列,并且所有机器指令序列都可以静态编译。

实际上,将动态语言的性能与静态语言相匹配可能需要以静态语言重新实现解释器/运行时;但是,您完全可以做到这一点是这个论点的重点和重点。这是一个“鸡与蛋”的问题,只要您同意以上未经证实的假设(虽然我认为,大多数都是不言而喻的),我们便可以回答。我们必须对静态而非动态语言表示赞同。

根据本讨论,回答问题的另一种方法是:在存储程序中,控制=计算的数据模型是现代计算的核心,而静态和动态编译之间的区别是错误的二分法。静态编译语言必须具有在运行时生成和执行任意代码的方法。它从根本上与通用计算有关。


再读一遍,我不认为这是真的。JIT编译破坏了您的论点。一旦知道了的值,即使是最简单的代码(例如,main(args) { for ( i=0; i<1000000; i++ ) { if ( args[0] == "1" ) {...} else {...} }也可能会大大加快)args(假设它永远不会改变,我们可能会断言)。静态编译器无法创建丢弃比较的代码。(当然,在该示例中,您只是if退出循环而已。但是事情可能更令人费解。)
拉斐尔

@Raphael我认为JIT可能引起我的争议。进行JIT编译的程序(例如JVM)通常是静态编译的程序。如果静态编译的JIT程序运行脚本的速度比其他静态程序可以完成相同工作的速度快,则只需将脚本与JIT编译器“捆绑”在一起,然后将该捆绑包称为静态编译的程序即可。这至少必须与在单独的动态程序上运行的JIT一样好,这与任何关于JIT必须做得更好的论点相矛盾。
Patrick87

嗯,这就像说将Ruby脚本与其解释器捆绑在一起会为您提供一个静态编译的程序。我不同意(因为它消除了这方面语言的所有区别),但这是语义问题,而不是概念问题。从概念上讲,在运行时(JIT)适应程序可以进行优化,而在编译时(静态编译器)则无法进行优化,这就是我的观点。
拉斐尔

@Raphael没有一个有意义的区别是答案的要点:正是由于以下原因,任何将某些语言严格地分类为静态的尝试都因此而失败,正是由于这个原因:预包装(Ruby)之间没有令人信服的区别,脚本)包和一个C程序。如果(Ruby,脚本)可以使机器执行正确的指令序列来有效解决给定的问题,那么精心制作的C程序也可以。
Patrick87

但是您可以定义差异。一个变体将手头的代码原封不动地发送到处理器(C),另一个变体在运行时进行编译(Ruby,Java等)。第一种是我们所说的“静态编译”,而后者是“及时编译”(它允许依赖于数据的优化)。
拉斐尔

4

您是否可以为Ruby之类的动态语言构建编译器,使其具有与C / C ++类似和可比的性能?

我认为答案是“是”。我也相信他们甚至可以效率而言超越当前的C / C ++架构(即使有些微不足道)。

原因很简单: 运行时的信息多于编译时的信息。

动态类型只是一个小小的障碍:如果一个函数总是或几乎总是使用相同的参数类型执行,那么JIT优化器可以为该特定情况生成分支和机器代码。还有很多事情可以做。

参见Google的Steve Yegge的演讲《Dynamic Languages Strike Back》(我相信那里也有一个视频版本)。他提到了V8的一些具体的JIT优化技术。鼓舞人心!

我期待着未来5年的发展!


2
我喜欢乐观。
戴夫·克拉克2012年

我相信史蒂夫的讲话中有一些非常具体的批评。找到它们后,我会发布它们。
康拉德·鲁道夫2012年

1
@DaveClarke就是让我不断奔跑的原因:)
Kos

2

在我看来,显然认为这在理论上或在不久的将来是可能的人是完全错误的。关键在于,动态语言提供并强加了完全不同的编程风格。实际上,即使两个方面都相互关联,差异也是双重的:

  • 符号(无功,或各种id <->数据绑定)是无类型的。
  • 结构(数据,所有在运行时生存的东西)也未按其元素的类型进行类型化。

第二点是免费提供通用性。请注意,这里的结构是复合元素,集合,而且是类型本身,甚至是各种(!)例程(函数,动作,操作)...我们可以按元素类型来键入结构,但是由于第一点,无论如何都会在运行时进行检查。我们可以键入符号,但仍然可以根据其元素类型来进行结构化符号的a取消类型化(一个数组将被键入为一个数组,而不是一个int数组),但是即使在动态语言中,这也是不正确的(还a可以包含一个字符串)。

大号

  • Element大号大号对模型类型)
  • 所有符号都属于该类型Element,它们可以容纳任何元素大号类型的
  • 所有结构(再次包括模型例程)仅接收元素的

对我来说,很明显这只是巨大的性能损失;而且我什至没有触及其他帖子中描述的所有结果(确保程序敏感性的各种类型的运行时检查)。


+1非常有趣。你读我的答案了吗?尽管您的回答确实提供了更多细节和有趣的观点,但您和我的想法似乎相似。
Patrick87

动态语言不必非类型化。用C语言实现动态语言模型会严重限制优化的可能性;这使编译器很难识别高级优化(例如,不可变的数据),并迫使一些基本操作(例如,函数调用)通过C进行处理。您所描述的内容与字节码解释器相距不远,其中字节码解码预先评估 本机编译器的性能往往要好得多,我怀疑字节码解码能否证明这种差异是合理的。
吉尔斯

@ Patrick87:是的,我们的思路似乎非常相似(以前没有读过,对不起,我的反思来自于目前在C语言中实现dyn lang)。
spir

@吉尔斯:如果您的意思不是静态输入,我宁愿同意“ ...不必取消输入”。我想这不是人们通常认为的dyn langs。我个人认为通用性(在以上答案中给出的一般意义上)是一个功能强大得多且难以使用的功能。通过扩大对如何定义给定(看似多态)类型的思考,或者直接为实例提供更大的灵活性,我们可以轻松找到与静态类型相关的方法。
spir

1

我没有时间详细阅读所有答案...但是我很开心。

在六十年代和七十年代初期,存在着类似的争议(计算机科学的历史经常重演):是否可以编译高级语言以产生与程序员手动产生的机器代码(例如汇编代码)一样高效的代码。每个人都知道程序员比任何程序都要聪明,并且可以提出非常聪明的优化(实际上,大多数情况下都在考虑现在所谓的窥孔优化)。我当然很讽刺。

甚至有一个代码扩展的概念:一个编译器产生的代码大小与一个好的程序员产生的同一程序的代码大小之比(好像其中太多了:-)。当然,这个比例总是大于1。当时的语言是Cobol和Fortran 4,或者是知识分子的Algol 60。我相信不会考虑Lisp。

嗯,有传言说有人生产了一个编译器,有时它的扩展比为1 ...直到它成为规则,即编译后的代码比手写代码好得多(并且也更可靠)。当时人们担心代码大小(小的内存),但速度或能量消耗也是如此。我不会讲原因。

语言的奇怪特征,动态特征并不重要。重要的是如何使用它们,是否使用它们。以任何单位(代码大小,速度,能量等)表示的性能通常取决于程序的很小一部分。因此,提供表达能力的设施很有可能不会真正妨碍您。通过良好的编程实践,仅以一种有纪律的方式使用高级功能,以想象新的结构(那是轻率的教训)。

语言没有静态类型的事实决不意味着以该语言编写的程序不是静态类型的。另一方面,程序使用的类型系统可能还没有形式化,以使类型检查器现在不存在。

在讨论中,已经有几种参考最坏情况的分析(“暂停问题”,PERL解析)。但是最坏的情况分析几乎是无关紧要的。重要的是在大多数情况下或在有用情况下会发生什么……无论是定义,理解还是经历过。这是另一个与程序优化直接相关的故事。很久以前,这是在德克萨斯州的一所主要大学里进行的,当时有一名博士生和他的导师(后来他被选为一所国家科学院的导师)之间。我记得,这位学生坚持要研究顾问证明难以解决的分析/优化问题。很快他们就不再说话了。但是这位学生是对的:在大多数实际情况下,这个问题都可以解决,因此他撰写的论文成为参考书。

并进一步评论说Perl parsing is not computable,无论该句子意味着什么,ML都存在类似的问题,这是一种非常形式化的语言。Type checking complexity in ML is a double exponential in the lenght of the program.这是在最坏情况下的复杂性的非常精确和正式的结果……这一点都没有关系。ML用户Afaik仍在等待实用的程序,它将爆炸类型检查器。

在许多情况下,像以前一样,人的时间和能力比计算能力稀缺。

未来的真正问题将是发展我们的语言以集成新知识,新编程形式,而不必重写所有仍在使用的旧版软件。

如果您看数学,那是一个非常大的知识体系。数百年来,用于表达它的语言,符号和概念不断发展。用新概念编写旧定理很容易。我们确实修改了主要的证明,但并不理会很多结果。

但是在编程的情况下,我们可能必须从头开始重写所有证明(程序就是证明)。可能我们真正需要的是非常高级且可发展的编程语言。优化器设计师将很乐意效仿。


0

一些注意事项:

  • 并非所有高级语言都是动态的。Haskell是非常高级的,但是是完全静态键入的。即使像Rust,Nim和D这样的系统编程语言也可以简洁高效地表达高级抽象。实际上,它们可以像动态语言一样简洁。

  • 存在针对动态语言的高度乐观的提前编译器。良好的Lisp实现达到等效C速度的一半。

  • 在这里,JIT编译可以是一个巨大的胜利。CloudFlare的Web应用程序防火墙生成由LuaJIT执行的Lua代码。LuaJIT极大地优化了实际采用的执行路径(通常是非攻击路径),其结果是代码的运行速度比静态编译器在实际工作负载下生成的代码要快得多。与具有配置文件引导的优化的静态编译器不同,LuaJIT可以在运行时适应执行路径的更改。

  • 取消优化也很关键。不需要通过JIT编译的代码来检查是否已进行了猴子修补的类,而是通过猴子修补的动作触发了运行时系统中的一个挂钩,该挂钩将丢弃依赖于旧定义的机器代码。


这个答案如何?好吧,如果您添加了参考资料,则可能是第三个项目符号。
拉斐尔

我是怀疑的说法,PGO无法在LuaJIT下典型的工作负载Web应用程序代码的性能匹配。
康拉德·鲁道夫2015年

@KonradRudolph JIT的主要优点是,随着不同路径的变热,JIT会调整代码。
黛咪(Demi)2015年

@Demetri我知道这一点。但是很难量化这是否是一个优势-参见我的回答和评论讨论。简而言之:尽管JIT可以适应用法的变化,但它还需要在运行时跟踪内容,这会产生开销。为此,盈亏平衡仅在行为频繁发生变化的地方发生。对于Web应用程序,可能只有一个(或很少有)使用模式可以实现优化,因此,由于适应性而导致的最小性能提升不会抵消连续分析的开销。
康拉德·鲁道夫2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.