1

注: デッド/ライブ ピクセル比に関する私のばかげた間違いを見つけたので、できれば Mike Brandt の回答を確認したはずです。しかし、ケンは一般的に良いアドバイスにうなずきます。

Canvas 要素でコンウェイの人生ゲームのパフォーマンスの問題をデバッグしようとしていますが、非常に奇妙なパフォーマンスの問題が発生しています。

私は約 4 ~ 12 FPS を取得しており、描画機能のベンチマークでは、全体的なパフォーマンスが 60 FPS まで上がるはずであることを示しています。

以下は Canvas 描画コードです。updateBgCanvas は RequestAnimationFrame によって約 30FPS で呼び出されています。全体が Chrome 28.0.1500.70 で実行され、パフォーマンスがテストされています。

(乱雑なコードで申し訳ありません。コードを小さなサブユニットにハッキングして、優れたコーディング手法をあまり考慮せずに、パフォーマンス プロファイラーの粒度を高めています)

当然のことながら、Canvas の描画関数 (fillDead と fillLive は最大の CPU ホグですが、ここからが奇妙になります。fillLive は CPU 時間の 5 ~ 6% を消費します (私が行った fillRect ベンチマークから予想されるものとほぼ同じです)。 CPU 時間の 36 ~ 38% これらは、1 または 0 に対する条件付きテストを除いて同一の関数です。

親関数で呼び出し順序を交換しようとしましたが、fill と fillDead に使用されている色は、ほぼ同じ 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();
     }
  }

ケンによって提案された数学関数の編集、親オブジェクト変数をローカル変数にコピーすることで、数学関数のパフォーマンスが約 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;
     },
4

1 に答える 1