Google Closure Compiler是否会降低性能?

cal*_*lum 26 javascript optimization google-closure-compiler

我正在撰写Google Chrome扩展程序.由于JavaScript文件是从磁盘加载的,因此它们的大小几乎不重要.

无论如何,我一直在使用Google Closure Compiler,因为显然它可以进行性能优化以及减少代码大小.

但是我在Closure Compiler的输出顶部注意到了这一点:

var i = true, m = null, r = false;
Run Code Online (Sandbox Code Playgroud)

这一点显然是为了减少文件大小(整个脚本中true/ null/的所有后续使用false都可以用单个字符替换).

但肯定会有轻微的性能影响吗?读取文字true关键字比按名称查找变量并找到它的值必须更快true...?

这种性能打击值得担心吗?谷歌闭包编译器还有什么可以实际上减慢执行速度吗?

Inc*_*ito 42

答案可能是.

让我们看看关闭团队对此的看法.

来自FAQ:

编译器是否在我的应用程序的执行速度和下载代码大小之间进行权衡?

是.任何优化编译器都需要权衡.某些大小优化确实会引入较小的速度开销.但是,Closure Compiler的开发人员一直小心不要引入显着的额外运行时.一些编译器的优化甚至会降低运行时间(请参阅下一个问题).

编译器是否针对速度进行优化?

在大多数情况下,较小的代码是更快的代码,因为下载时间通常是Web应用程序中最重要的速度因素.减少冗余的优化也可以加快代码的运行时间.

我断然挑战他们在这里做出的第一个假设.使用的vars名称的大小并不会直接影响各种JavaScript引擎如何处理代码 - 事实上,JS引擎并不关心你是否调用变量supercalifragilisticexpialidociousx(但我作为程序员确实这样做).如果你担心交付,下载时间是最重要的部分 - 一个缓慢运行的脚本可能是由我怀疑该工具根本无法解释的数百万件事造成的.

要真实地理解为什么你的问题可能,首先你需要问的是"是什么让JavaScript快或慢?"

当然,我们遇到了一个问题,"我们在谈论什么JavaScript引擎?"

我们有:

  • 喀拉喀邦(歌剧院)
  • 脉轮(IE9 +)
  • SpiderMonkey(Mozilla/FireFox)
  • SquirrelFish(Apple的webkit)
  • V8(Chrome)
  • Futhark(歌剧院)
  • JScript(9之前的所有IE版本)
  • JavaScriptCore(Konqueror,Safari)
  • 我跳过了一些.

这里有人真的认为他们都一样吗?特别是JScript和V8?不,不!

再说一遍,当google关闭编译代码时,哪个引擎可以构建东西?你感觉幸运吗?

好吧,因为我们永远不会涵盖所有这些基础,所以我们可以尝试在这里看一下"老"与"新"代码.

以下是我所见过的JS Engines最佳演示文稿之一的快速摘要.

较旧的JS引擎

  • 代码被直接解释和编译为字节代码
  • 没有优化:你得到你得到的
  • 由于字体松散,代码很难快速运行

新的JS引擎

  • 引入Just-In-Time(JIT)编译器以实现快速执行
  • 为真正快速的代码引入类型优化JIT编译器(考虑接近C代码速度)

这里的关键区别是新引擎引入了JIT编译器.

从本质上讲,JIT将优化您的代码执行,以便它可以更快地运行,但如果它不喜欢的事情发生,它会转身并再次变慢.

你可以通过这样的两个函数来做这些事情:

var FunctionForIntegersOnly = function(int1, int2){
    return int1 + int2;
}

var FunctionForStringsOnly = function(str1, str2){
    return str1 + str2;
}

alert(FunctionForIntegersOnly(1, 2) + FunctionForStringsOnly("a", "b"));
Run Code Online (Sandbox Code Playgroud)

通过谷歌关闭运行实际上简化了整个代码到:

alert("3ab");
Run Code Online (Sandbox Code Playgroud)

并且通过书中的每个指标都会更快.这里真正发生的是它简化了我的一个非常简单的例子,因为它做了一些部分执行.这是你需要小心的地方.

让我们说我们的代码中有一个y-combinator,编译器把它变成这样的东西:

(function(a) {
 return function(b) {
    return a(a)(b)
  }
})(function(a) {
  return function(b) {
    if(b > 0) {
      return console.log(b), a(a)(b - 1)
    }
  }
})(5);
Run Code Online (Sandbox Code Playgroud)

不是真的更快,只是缩小了代码.

JIT通常会看到实际上你的代码只需要对该函数输入两个字符串输入,并返回一个字符串(或第一个函数的整数),这将它放入特定于类型的JIT中,这使得它非常快.现在,如果谷歌闭包做了一些奇怪的事情,比如将具有几乎相同签名的那些函数转换成一个函数(对于非平凡的代码),如果编译器做了JIT不喜欢的事情,你可能会失去JIT速度.

那么,我们学到了什么?

  • 您可能拥有JIT优化代码,但编译器会将您的代码重新组织为其他代码
  • 旧浏览器没有JIT但仍然运行您的代码
  • Closure编译的JS通过为简单函数部分执行代码来调用较少的函数调用.

所以你会怎么做?

  • 编写小型和对点函数,编译器将能够更好地处理它们
  • 如果您对JIT非常了解,手动优化代码并使用该知识,那么闭包编译器可能不值得使用.
  • 如果您希望代码在旧版浏览器上运行得更快,那么它就是一个很好的工具
  • 权衡取舍通常是值得的,但只要小心检查,不要盲目地相信它.

通常,您的代码更快.您可能会介绍各种JIT编译器不喜欢的东西,但如果您的代码使用较小的函数并且正确的原型面向对象设计,它们将很少见.如果你考虑编译器正在做的全部范围(更短的下载和更快的执行),那么奇怪的事情var i = true, m = null, r = false;可能是值得的,虽然编译器的运行速度较慢,但​​总寿命更快.

同样值得注意的是,Web应用程序执行中最常见的瓶颈是文档对象模型,如果您的代码很慢,我建议您在那里付出更多努力.

  • 很好的答案,但我不禁担心JIT编译器在内联和部分评估"常量"值时会遇到更多麻烦,如果它在一个变量中,可以想象可以在以后重新分配. (3认同)
  • @incognito:引用:"我断然挑战他们在这里做出的第一个假设.变量名称的大小......"我们没有做出这样的假设.你在哪里知道Closure Compiler将变量名称大小与运行时性能等同起来?但是,应用程序的启动时间与下载代码所花费的时间高度相关,并且(遗憾地)每个字节都很重要.这个事实在过去十年中变得更糟,因为移动平台已经变得非常重要,因为它具有高延迟/低带宽/低可靠性连接,低级处理器和微型浏览器缓存. (3认同)
  • 我应该澄清,除了奇怪的开始,一般评估是合理的,但我要补充说,很难"调到JIT",因为有不少于4个主要引擎(每个主要浏览器一个,不考虑即将到来的引擎)每个都有不同的特点 (2认同)

Thi*_*iff 10

看起来在现代浏览器中使用文字truenull变量几乎在所有情况下都没有区别(如零;它们完全相同).在极少数情况下,变量实际上更快.

因此,节省的额外字节值得,并且不需要任何费用.

truevs变量(http://jsperf.com/true-vs-variable):

真与变量

nullvs变量(http://jsperf.com/null-vs-variable):

null vs variable

  • 然后,这证明我们没有衡量我们认为我们正在测量的东西.某处出了点问题,时间不应该被信任. (3认同)