访问本地变量不会提高性能

****澄清**:我不是在寻找最快的代码或优化。 我想了解为什么一些似乎没有被优化或优化的代码实际上总体上运行得更快。

短版

为什么这个代码:

var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;

比这个更高性能?

var index = Math.floor(ref_index) * 4;

长版

本周,Impact js的作者发表了一篇关于渲染问题的文章:

http://www.phoboslab.org/log/2012/09/drawing-pixels-is-hard

在文章中有一个函数的来源,通过访问画布中的像素来缩放图像。 我想提出一些传统的方法来优化这种代码,以便在加载时缩小缩放比例。 但经过测试后,我的结果大部分时间都是最原始的功能。

猜测这是正在进行一些智能优化的JavaScript引擎,我尝试了解更多事情,所以我做了一堆测试。 但是我的结果很混乱,我需要一些帮助来了解发生了什么。

我在这里有一个测试页面:

http://www.mx981.com/stuff/resize_bench/test.html

jsPerf:http://jsperf.com/local-variable-due-to-the-scope-lookup

要开始测试,请单击图片,结果将显示在控制台中。

有三种不同的版本:

原始代码:

for( var y = 0; y < heightScaled; y++ ) {
    for( var x = 0; x < widthScaled; x++ ) {
        var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
        var indexScaled = (y * widthScaled + x) * 4;
        scaledPixels.data[ indexScaled ] = origPixels.data[ index ];
        scaledPixels.data[ indexScaled+1 ] = origPixels.data[ index+1 ];
        scaledPixels.data[ indexScaled+2 ] = origPixels.data[ index+2 ];
        scaledPixels.data[ indexScaled+3 ] = origPixels.data[ index+3 ];
    }
}

jsPerf:http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance

我尝试优化它的一个尝试:

var ref_index = 0;
var ref_indexScaled = 0
var ref_step = 1 / scale;
for( var y = 0; y < heightScaled; y++ ) {
    for( var x = 0; x < widthScaled; x++ ) {
        var index = Math.floor(ref_index) * 4;
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index ];
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+1 ];
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+2 ];
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+3 ];

        ref_index+= ref_step;
    }
}

jsPerf:http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance

相同的优化代码,但每次重新计算索引变量(混合)

var ref_index = 0;
var ref_indexScaled = 0
var ref_step = 1 / scale;
for( var y = 0; y < heightScaled; y++ ) {
    for( var x = 0; x < widthScaled; x++ ) {
        var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index ];
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+1 ];
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+2 ];
        scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+3 ];

        ref_index+= ref_step;
    }
}

jsPerf:http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance

最后两个唯一的区别是计算“指数”变量。 而令我惊讶的是,大多数浏览器中的优化版本都比较慢(除了歌剧)。

个人测试结果(不是jsPerf测试):

  • 歌剧

    Original:  8668ms
    Optimized:  932ms
    Hybrid:    8696ms
    
  • Original:  139ms
    Optimized: 145ms
    Hybrid:    136ms
    
  • 苹果浏览器

    Original:  433ms
    Optimized: 853ms
    Hybrid:    451ms
    
  • 火狐

    Original:  343ms
    Optimized: 422ms
    Hybrid:    350ms
    
  • 在挖掘之后,似乎通常的良好做法是由于范围查找而主要访问局部变量。 由于优化后的版本只能调用一个局部变量,因此除了涉及各种操作之外,混合代码调用多个变量和对象的速度应该更快。

    那么为什么“优化”版本更慢?

    我认为这可能是因为某些JavaScript引擎没有优化Optimized版本,因为它不够热,但是在chrome中使用--trace-opt之后,似乎所有版本都由V8正确编译。

    在这一点上,我有点无知,想知道有人会知道发生了什么?

    我在这个页面还做了更多的测试用例:

    http://www.mx981.com/stuff/resize_bench/index.html


    尽管听起来很愚蠢,但Math.whatever()调用可能会对JS引擎进行优化和内联很棘手。 只要有可能,就更喜欢算术运算(不是函数调用)来获得相同的结果。

    将以下第四项测试添加到http://www.mx981.com/stuff/resize_bench/test.html

    // Test 4
    console.log('- p01 -');
    start = new Date().getTime();
    for (i=0; i<nbloop; i++) {
      var index = 0;
      var ref_indexScaled = 0
      var ref_step=1/scale;
    
    
      for( var y = 0; y < heightScaled; y++ ) {
        for( var x = 0; x < widthScaled; x++ ) {
          var z= index<<2;
          scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ z++ ];
          scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ z++ ];
          scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ z++ ];
          scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ z++ ];
    
          index+= ref_step;
        }
      }
    }
    end = new Date().getTime();
    console.log((end-start)+'ms');
    

    在Opera Next中产生以下数字:

  • 原始 - 2311ms
  • 重构 - 112ms
  • 混合动力 - 2371毫秒
  • p01 - 112ms

  • 使用一些基本技巧可以高度优化性能:

  • 在循环中运行多个循环时,请使用:

    while(i--){/ *这里有一些代码* /}

  • ...其中我是一个大于0的值。

  • 高速缓存变量/本地化变量以最小化计算。 对于更大的计算,这意味着将部分计算放在正确的抽象层。

  • 重新使用变量(重新初始化开销可能会成为大量数据处理的问题)。 注意:这是一个糟糕的编程设计原则,但它是一个很好的性能原则!

  • 减少财产的深度。 使用object.property会使性能与仅包含“object_propertyvalue”的var相关。

  • 使用这些原则可以获得更好的性能。 现在从一个高层次来看,你从这个函数中获得了这个函数,但它有几个方面存在缺陷。 所以要真正优化完整的功能,而不仅仅是你说的那一行:

    function resize_Test5( img, scale ) {
        // Takes an image and a scaling factor and returns the scaled image
    
        // The original image is drawn into an offscreen canvas of the same size
        // and copied, pixel by pixel into another offscreen canvas with the 
        // new size.
    
        var widthScaled = img.width * scale;
        var heightScaled = img.height * scale;
    
        var orig = document.createElement('canvas');
        orig.width = img.width;
        orig.height = img.height;
        var origCtx = orig.getContext('2d');
        origCtx.drawImage(img, 0, 0);
        var origPixels = origCtx.getImageData(0, 0, img.width, img.height);
    
        var scaled = document.createElement('canvas');
        scaled.width = widthScaled;
        scaled.height = heightScaled;
        var scaledCtx = scaled.getContext('2d');
        var scaledPixels = scaledCtx.getImageData( 0, 0, widthScaled, heightScaled );
    
        // optimization start
        var old_list = origPixels.data;
        var image_width = img.width;
        var h = heightScaled;
        var w = widthScaled;
        var index_old;
        var index_new;
        var h_scale;
        var new_list = [];
        var pre_index_new;
    
        while(h--){
            h_scale = Math.floor(h / scale) * image_width;
            pre_index_new = h * widthScaled;
            while(w--){
                index_old = (h_scale + Math.floor(w / scale)) * 4;
                index_new = (pre_index_new + w) * 4;
                new_list[ index_new ]     = old_list[ index_old ];
                new_list[ index_new + 1 ] = old_list[ index_old + 1 ];
                new_list[ index_new + 2 ] = old_list[ index_old + 2 ];
                new_list[ index_new + 3 ] = old_list[ index_old + 3 ];
            }
        }
        scaledPixels.data = new_list;
        // optimization stop
    
        scaledCtx.putImageData( scaledPixels, 0, 0 );
        return scaled;
    }
    
    链接地址: http://www.djcxy.com/p/64157.html

    上一篇: Accessing local variable doesn't improve performance

    下一篇: for loop vs forEach performance in javascript and credibility of jsperf results