【问题标题】:V8 Engine Voodoo: Why is this faster / slower?V8 Engine Voodoo:为什么这更快/更慢?
【发布时间】:2013-06-27 12:56:12
【问题描述】:

我目前正在开发一个图像编辑器,偶然发现了这种关于 V8 中像素操作和/或函数调用的奇怪行为。

http://jsperf.com/canvas-pixelwise-manipulation-performance

有两个测试用例。两个测试用例都应该处理内存中画布的图像数据以增加亮度。所以他们必须遍历每个像素并操作每个像素的 4 个颜色值。

案例一

案例 1 执行“总共 1 个函数调用”,这意味着它将上下文和 imageData 传递给一个函数,该函数然后迭代像素并操作数据。多合一功能

案例2

案例 2 执行“1 个函数调用 per pixel”,这意味着它遍历像素并为每个像素调用一个方法,然后操作给定像素的 imageData。这会导致(在这种情况下)250000 个额外的函数调用。

我的期望

我希望案例 1 比案例 2 快得多,因为案例 2 执行了 250000 次额外的函数调用。

结果

在 Chrome 中,情况正好相反。如果我执行 250000 个额外的函数调用,它比一个处理所有图像操作的单个函数调用要快。

我的问题:为什么?

【问题讨论】:

  • V8 将为案例 2 内联您的函数。我认为函数内联的类型推断更严格,因此可以将这段代码“重写”为高性能整数运算。

标签: javascript performance v8


【解决方案1】:

这两种代码都不会操纵任何画布,并且在基准循环中定义函数实际上没有任何意义。 你想要的是永远不会重新创建的静态函数,这样一旦 JIT 优化了它们,它们就会保持优化。你不 想要衡量创建函数的开销,因为真正的应用程序只会定义一次函数。

修复基准代码后,它们应该以相同的速度运行,因为manipulatePixel 函数将获得 内联。

http://jsperf.com/canvas-pixelwise-manipulation-performance/4

我还创建了另一个 jsperf,在其中我有目的地操纵 V8 启发式*而不是内联 manipulatePixel 功能:

http://jsperf.com/canvas-pixelwise-manipulation-performance/5

如您所见,它现在慢了 50%。两个 jsperfs 之间的唯一区别是 manipulatePixel 函数中的巨大注释。


*V8 将raw textual size of the function(包括 cmets)视为内联决策的启发式。

【讨论】:

    【解决方案2】:

    我不太熟悉 V8 的优化魔法,但我想说案例 2 为 V8 引擎留下了更多空间来重写代码。
    尽管乍一看,案例 1 应该表现更好,但它并没有给 V8 留下太多发挥其魔力的空间。
    虽然只有 1 个函数,但创建了一个调用对象,在该函数对象的范围内,声明了几个变量并正在处理一个巨大的对象。
    但是,第二种情况可能只是转换为循环,甚至是字节移位,从而消除了对函数对象和作用域的需要。
    除了省略范围/函数之外,您的变量(参数)也不需要复制,因此不会留下讨厌的对象引用来导致任何开销。

    除了要复制的变量和引用之外,还需要考虑范围扫描:从函数内调用的Math.abs(略微)比在全局范围内慢。我不知道这是否属实,但我有一种偷偷摸摸的怀疑,即在更高范围内声明的屏蔽变量也可能会影响性能。
    您还在 one-function-approach 中使用了widthheight,在我看来它们好像是隐含的全局变量。这会导致对循环的每次迭代进行范围扫描,这可能会导致比这些参数和Math.* 调用更多的阻力......

    【讨论】:

      猜你喜欢
      • 2010-09-22
      • 1970-01-01
      • 2011-04-22
      • 2013-11-28
      • 1970-01-01
      • 2012-06-29
      • 1970-01-01
      • 1970-01-01
      • 2011-06-02
      相关资源
      最近更新 更多