2013-07-11 47 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

相關問題