Javascript中不一致的执行时间
注:如果可以的话,我会检查Mike Brandt的答案,因为他发现了我在死/活像素比上的愚蠢错误。但肯得到了普遍良好的建议点头 我正在尝试调试Conway在Canvas元素中的生活游戏的一些性能问题,我遇到了一些非常奇怪的性能问题 我得到了大约4-12 FPS,绘图函数的基准测试表明总体性能应该能够达到60 FPS 下面是画布绘制代码。RequestAnimationFrame正在以大约30FPS的速度调用updateBgCanvas。整个系统正在Chrome 28.0.1500.70中运行和性能测试 (对于混乱的代码,我深表歉意,我一直在将代码分成更小的子单元,以便在性能分析器中获得更大的粒度,而没有太多考虑好的编码技术) 毫不奇怪,画布绘制函数(fillDead和fillLive是最大的CPU消耗,但这里是它变得奇怪的地方。fillLive消耗了5-6%的CPU时间(大约是我所做的fillRect基准测试的预期值)fillDead占用了惊人的36-38%的CPU时间。除了针对1或0的条件测试之外,这些都是相同的函数 我尝试过在父函数中交换调用顺序,填充和fillDead所用的颜色始终比几乎相同的fillLive调用时间长6-7倍。我完全不知道为什么会这样 有什么建议吗Javascript中不一致的执行时间,javascript,performance,canvas,Javascript,Performance,Canvas,注:如果可以的话,我会检查Mike Brandt的答案,因为他发现了我在死/活像素比上的愚蠢错误。但肯得到了普遍良好的建议点头 我正在尝试调试Conway在Canvas元素中的生活游戏的一些性能问题,我遇到了一些非常奇怪的性能问题 我得到了大约4-12 FPS,绘图函数的基准测试表明总体性能应该能够达到60 FPS 下面是画布绘制代码。RequestAnimationFrame正在以大约30FPS的速度调用updateBgCanvas。整个系统正在Chrome 28.0.1500.70中运行和性
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();
}
}
window.bgVars={
“关于”:“背景是著名的康威生活游戏”,
“_Canvas”:{},
“_Ctx”:{},
“xBlockSize”:5,
“yBlockSize”:5,
“Xblock”:0,
“yBlocks”:0,
“bornVals”:[3],
“固定资产”:[2,3],
“cGrid”:[],
“cGrid2”:[],
“cL”:0,
“initBgVars”:函数(iCanvas、iCtx){
console.log(这个.xBlockSize);
这个。_Canvas=iCanvas;
这个。_Ctx=iCtx;
this.cGrid=[];
this.cGrid2=[];
this.xBlocks=Math.round(myCanvas.width/this.xBlockSize)+1;
this.yBlocks=Math.round(myCanvas.height/this.yBlockSize)+1;
对于(var rep=0;rep要举例说明如何提高性能,请尝试使用此修改替换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.*
放在父作用域中的局部变量中,并将它们作为参数传递给函数。您计算过在eac中的条件中实际执行代码的次数吗h情况(即cGrid是否包含显著多于1的0)?我认为只有当cGrid的1和0的数量大致相等时,两个函数的性能才会相同。这是可能的。我在分析时间窗口期间观察了死/活像素比率,它看起来很均匀,但可能只是一种错觉。我会做一个快速检查……是的,我的眼睛是肮脏的说谎者。死/活像素比例仅为4:1,但这似乎是可能的原因。谢谢!你应该将fillDead和fillAlive结合起来,也许还可以更新bGCanvas:目前你在数组中浏览了三次;也可以使用vars来保存这个查找,这都是一个函数,我将其分解成子例程来细化分析。由于活像素和死像素的数量之间存在如此大的差异,我只是重写它来进行死颜色泛光填充,只绘制“活”像素。我也一直在尝试使用vars来降低查找成本,但没有看到太多的效果从中获得性能提升。关于使用局部变量的好建议。我曾假设,由于它是对局部对象的引用,查找时间将是最小的,但我在数学部分获得了16%的性能提升。(总体性能提升仅为4%左右,但仍然是一种获得更高速度的廉价方法。)至于vs while,JSPerf的最新结果似乎表明,现在的for实际上比while稍微快一点,但差别非常小。@DanHeidel是的,JS引擎在不断改进。过去快/慢的东西现在在性能上变得非常相等。至于参考时间,它确实是f但是解释器仍然必须检查分支上的所有属性。如果有两个分支(this.branch.*),它也必须在那里执行,这在时间上是指数级的(不要考虑可能的解释器优化)@DanHeidel还记得,对象本身在当前状态下有很多分支(许多分支可能是不必要的)。如果您将操作拆分为许多小的内部函数(如果在您的场景中可能的话)如果在内部连续调用,性能也会有所提高。谢谢你的提示!我已经习惯了嵌入式系统中的C语言,浏览器中所有的自动抽象和幕后工作都需要一些时间来适应。
"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);
}
}
},
//...