2013-07-11 51 views
1

注意:如果可以的话,我会检查Mike Brandt的答案,因为他在死/活像素比率上发现了我的愚蠢错误。但肯得到一般好的建议。Javascript中执行时间不一致

我试图在Canvas元素中调试Conway的生活游戏中的一些性能问题,我遇到了一些非常奇怪的性能问题。

我得到约4-12 FPS和绘图功能的基准测试表明,整体性能应该能够达到60 FPS。

以下是Canvas绘图代码。 updateBgCanvas被RequestAnimationFrame以大约30FPS的速度调用。整个事情正在Chrome 28.0.1500.70中运行并进行全面测试。

(为乱码我的道歉,我一直在黑客的代码为较小的亚单位获得的性能分析器更大的粒度没有更多考虑到良好的编码技术)

不出所料,画布绘制功能( fillDead和fillLive是最大的CPU猪,但在这里它变得奇怪。fillLive占用CPU时间的5-6%(大约是我所期望的fillRect基准测试的结果),fillDead占用了CPU的36-38%除了1或0的条件测试外,这些功能是相同的。

我试过在父函数和正在使用的颜色中调用调用顺序对于填充和填充,DeadDead始终需要的时间比几乎相同的fillLive长6-7倍。我完全不知道为什么会这样。

有什么建议吗?

window.bgVars = { 
    "about": "The background is the famous Conway Game of Life", 
    "_Canvas": {}, 
    "_Ctx": {}, 
    "xBlockSize": 5, 
    "yBlockSize": 5, 
    "xBlocks": 0, 
    "yBlocks": 0, 
    "bornVals": [3], 
    "stayAliveVals": [2, 3], 
    "cGrid": [], 
    "cGrid2": [], 
    "cL": 0, 
    "initBgVars" : function(iCanvas, iCtx){ 
     console.log(this.xBlockSize); 
     this._Canvas = iCanvas; 
     this._Ctx = iCtx; 
     this.cGrid = []; 
     this.cGrid2 = []; 
     this.xBlocks = Math.round(myCanvas.width/this.xBlockSize) + 1; 
     this.yBlocks = Math.round(myCanvas.height/this.yBlockSize) + 1; 
     for(var rep=0;rep<(this.xBlocks * this.yBlocks);rep++){ 
      this.cGrid.push(Math.round(Math.random()*0.8)); 
     } 
     this.cGrid2.length = this.cGrid.length; 
    }, 
    "cirInd": function(index){ 
     //returns modulus, array-wrapping value to implement circular array 
     if(index<0){index+=this.cGrid.length;} 
     return index%this.cGrid.length; 
    }, 
    "calcNeighbors": function(rep){ 
     var foo = this.xBlocks; 
     var neighbors = this.cGrid[this.cirInd(rep-foo-1)] + this.cGrid[this.cirInd(rep-foo)] + this.cGrid[this.cirInd(rep-foo+1)] + this.cGrid[this.cirInd(rep-1)] + this.cGrid[this.cirInd(rep+1)] + this.cGrid[this.cirInd(rep+foo-1)] + this.cGrid[this.cirInd(rep+foo)] + this.cGrid[this.cirInd(rep+foo+1)]; 
     return neighbors; 
    }, 
    "refreshGrid": function(){ 
     for(var rep=0;rep<this.cGrid.length;rep++){ 
      if(Math.random()<0.0002){this.cGrid2[rep] = 1;} 
      this.cGrid[rep] = this.cGrid2[rep]; 
     } 
    }, 
    "lifeRules": function(rep, neighbors){ 
      if(this.cGrid[rep] == 1){ //stay alive rules 
       for(var rep2=0;rep2<this.stayAliveVals.length;rep2++){ 
       if(neighbors==this.stayAliveVals[rep2]){this.cGrid2[rep] = 1;} 
       } 
      } 
      if(this.cGrid[rep] == 0){ //'born' rules 
       for(var rep2=0;rep2<this.bornVals.length;rep2++){ 
       if(neighbors==this.bornVals[rep2]){this.cGrid2[rep] = 1;} 
       } 
      }   
    }, 
    "fillDead": function(){ 
     for(var rep=0;rep<this.cGrid.length;rep++){ 
      if(this.cGrid[rep] == 0){ 
       this._Ctx.fillRect((rep%this.xBlocks)*this.xBlockSize, Math.floor(rep/this.xBlocks)*this.yBlockSize, this.xBlockSize, this.yBlockSize); 
      } 
     }   
    }, 
    "fillLive": function(){ 
     for(var rep=0;rep<this.cGrid.length;rep++){ 
      if(this.cGrid[rep] == 1){ 
       this._Ctx.fillRect((rep%this.xBlocks)*this.xBlockSize, Math.floor(rep/this.xBlocks)*this.yBlockSize, this.xBlockSize, this.yBlockSize); 
      } 
     }   
    }, 
    "updateBgCanvas": function(){ 
     //fill live squares 
     this._Ctx.fillStyle = 'rgb(130, 0, 0)'; 
     this.fillLive(); 
     //fill dead squares 
     this._Ctx.fillStyle = 'rgb(100, 0, 0)'; 
     this.fillDead(); 
     //calculate next generation to buffer 
     for(var rep=0;rep<this.cGrid.length;rep++){ 
      //add up the live squares in the 8 neighbor blocks 
      var neighbors = this.calcNeighbors(rep); 
      this.cGrid2[rep] = 0; 
      //implement GoL ruleset 
      this.lifeRules(rep, neighbors); 
     } 
     //seed with random noise to keep dynamic and copy to display buffer 
     this.refreshGrid(); 
    } 
    } 

编辑由Ken建议的数学函数,复制父对象瓦尔到本地变量,得到约16%PERF增益在数学函数,约4%的整体:

 "cirInd": function(index, mod){ 
     //returns modulus, array-wrapping value to implement circular array 
     if(index<0){index+=mod;} 
     return index%mod; 
    }, 
    "calcNeighbors": function(rep){ 
     var foo = this.xBlocks; 
     var grid = this.cGrid; 
     var mod = grid.length; 
     var neighbors = grid[this.cirInd(rep-foo-1, mod)] + grid[this.cirInd(rep-foo, mod)] + grid[this.cirInd(rep-foo+1, mod)] + grid[this.cirInd(rep-1, mod)] + grid[this.cirInd(rep+1, mod)] + grid[this.cirInd(rep+foo-1, mod)] + grid[this.cirInd(rep+foo, mod)] + grid[this.cirInd(rep+foo+1, mod)]; 
     return neighbors; 
    }, 
+0

你是否已经统计了在每种情况下实际执行条件代码的次数(即cGrid是否包含明显比0更多的0)?我认为只有当cGrid具有大致相等的1和0的数字时,你才会期望两个函数的性能相同。 –

+0

这是可能的。在分析时间窗口期间,我正在观察死/活像素比例,它看起来非常平均,但它可能只是一种视错觉。我会做一个快速检查.. – DanHeidel

+0

是的,我的眼睛很脏,脏兮兮的骗子。死像素比例仅为4:1,但似乎可能是原因。谢谢! – DanHeidel

回答

0

举个例子你可以做什么来改善性能的尝试与此modifcation更换fillDead功能:

"fillDead": function(){ 

    /// localize to vars so interpreter doesn't 
    /// have to walk of the branches all the time: 
    var cGrid = this.cGrid, ctx = this._Ctx, 
     xBlocks = this.xBlocks, xBlockSize = this.xBlockSize, 
     yBlockSize = this.yBlockSize, 
     rep = cGrid.length - 1; 

    /// while loops are generally faster than for loops 
    while(rep--) 
     if(cGrid[rep] == 0){ 
     ctx.fillRect((rep%xBlocks) * xBlockSize, ((rep/xBlocks)|0) * yBlockSize, xBlockSize, yBlockSize); 
     } 
    } 
}, 
//... 

将如何执行?如果只有1或2个网格元素,你不会看到太多的差别,但是网格元素越多,性能应该越好(不能测试,因为我没有完整的代码)。

如果你看到它提高了性能,你也应该将它应用到其他函数中。您还应该看看您是否可以将其中一些this.*放在本地变量中,而不是放在父范围中,并将它们作为参数传递给函数。

+0

使用本地变量的好建议。我曾假设,因为它是对本地对象的引用,所以查找时间会很短,但是在数学部分中,我获得了16%的性能提升。 (只有大约4%的总体性能提升,但仍然是获得更多速度的一种便宜方法)(最后编辑如下) 至于vs vs while,JSPerf的最新结果似乎表明,实际上它比虽然这些日子但差异很小 – DanHeidel

+0

@DanHeidel是的,JS引擎在不断地改进,过去很快/很慢的事情现在变得非常平等了,至于参考时间,它是封闭的,但解释者仍然必须经过分支上的所有属性,如果你有两个分支(this.branch。*),它也必须在那里做,并且时间是指数级的(远离可能的解释器优化) – K3N

+0

@DanHeidel还记得对象是自己在当前状态有很多分支(许多可能不必要)。如果你将操作分成许多小内部函数(如果可能的话在你的场景中),这些函数会在内部连续调用,并且性能也会有所提升。 – K3N

相关问题