注意:如果可以,我会检查Mike Brandt的答案,因为他在死/实时像素比率上遇到了我的愚蠢错误。但肯得到了一般好建议的点头。
我正在尝试使用Canvas元素中的Conway生活游戏来调试一些性能问题,并且我遇到了一些非常奇怪的性能问题。
我得到大约4-12 FPS并且绘图功能的基准测试表明整体性能应该能够达到60 FPS。
下面是Canvas绘图代码。 updateBgCanvas由RequestAnimationFrame在大约30FPS处调用。整个过程正在Chrome 28.0.1500.70中运行和性能测试。
(对于凌乱的代码道歉,我一直在将代码破解成更小的子单元,以便在性能分析器中获得更大的粒度,而不必考虑良好的编码技术)
不出所料,Canvas绘图函数(fillDead和fillLive是最大的CPU生成器,但是这里有奇怪的地方.fillLive消耗了5-6%的CPU时间(大约是我对fillRect基准测试所期望的)和fillDead占用了大约36-38%的CPU时间。除了针对1或0的条件测试外,它们是相同的函数。
我尝试在父函数中交换调用顺序,并且用于填充和fillDead的颜色始终比几乎相同的fillLive长6到7到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%的性能增益,总体上约为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 :(得分:0)
为了举例说明如何提高性能,请尝试使用此修改替换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.*
放入本地变量而不是父作用域中,并将它们作为参数传递给函数。