performance - Inconsistent execution times in Javascript -
note: have checked mike brandt's answer if have since caught dumb mistake on dead/live pixel ratio. ken gets nod advice.
i'm trying debug performance issues conway's game of life in canvas element , i'm getting strange performance issues.
i'm getting 4-12 fps , benchmarking of drawing functions indicates overall performance should able go 60 fps.
below canvas drawing code. updatebgcanvas being called @ 30fps requestanimationframe. whole thing being run , perf tested in chrome 28.0.1500.70.
(my apologies messy code, i've been hacking code smaller subunits greater granularity in performance profiler without regard coding technique)
unsurprisingly, canvas drawing functions (filldead , filllive biggest cpu hogs here's gets bizarre. filllive consumes 5-6% of cpu time (about expect fillrect benchmarking did) , filldead eats whopping 36-38% of cpu time. these identical functions except conditional test against 1 or 0.
i've tried swapping calling order in parent function , colors being used fill , filldead consistently takes 6-7 times longer call identical filllive. i'm @ loss why be.
any suggestions?
window.bgvars = { "about": "the background 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 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 buffer for(var rep=0;rep<this.cgrid.length;rep++){ //add live squares in 8 neighbor blocks var neighbors = this.calcneighbors(rep); this.cgrid2[rep] = 0; //implement gol ruleset this.liferules(rep, neighbors); } //seed random noise keep dynamic , copy display buffer this.refreshgrid(); } }
edits math functions suggested ken, copying parent object vars local vars, giving 16% perf gain in math functions, 4% overall:
"cirind": function(index, mod){ //returns modulus, array-wrapping value 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; },
to give example of can improve performance try replace filldead function modifcation:
"filldead": function(){ /// localize vars interpreter doesn't /// have walk of branches time: var cgrid = this.cgrid, ctx = this._ctx, xblocks = this.xblocks, xblocksize = this.xblocksize, yblocksize = this.yblocksize, rep = cgrid.length - 1; /// while loops faster loops while(rep--) if(cgrid[rep] == 0){ ctx.fillrect((rep%xblocks) * xblocksize, ((rep/xblocks)|0) * yblocksize, xblocksize, yblocksize); } } }, //...
how perform ? if 1 or 2 grid elements won't see difference more grid elements better performance should (cannot test don't have full code).
if see improves performance should apply other functions well. should see if can put of this.*
local vars instead in parent scope , pass them in parameters functions.
Comments
Post a Comment