2014-04-03 37 views
1

我正在嘗試構建一個在瀏覽器中運行的Javascript遊戲工具包。我已經遇到過度垃圾收集可能導致的可怕的100ms +暫停。這往往會破壞用戶體驗。Javascript對象池是爲了避免垃圾回收值得嗎?

正如我已經讀過的,對此的補救措施是避免首先創建垃圾,例如通過對象的合併和重用。我把一個簡單的應用程序,以測試出來的概念:http://jsfiddle.net/gk6Gn/

載體類被包含在源,並且被非常簡單地定義:

function Vector2() 
{ 
    this.x = 0; 

    this.y = 0; 
} 

Vector2.prototype.addUnpooled = function (other) 
{ 
    var v = new Vector2(); 

    v.x = this.x + other.x; 

    v.y = this.y + other.y; 

    return v; 
}; 

Vector2.prototype.addPooled = function (other) 
{ 
    var v = acquireVector2(); 

    v.x = this.x + other.x; 

    v.y = this.y + other.y; 

    return v; 
}; 

我使用requestAnimationFrame來計算大約每60倍幀第二。每一幀,我都經歷了N次迭代。在每次迭代中,我創建並添加兩個向量,導致第三個向量。我緩慢上升,迭代的次數,直到性能下降低於每秒59幀,並認爲我的每幀的最大迭代:

function drawFrame(time) 
{ 
    window.requestAnimationFrame(drawFrame); 

    //testClassic(); 

    testPooled(); 

    framesSinceLastReport++; 

    var timeSinceLastReport = time - lastReportTime; 

    if (timeSinceLastReport >= 1000) 
    { 
     var framesPerSecond = Math.floor(framesSinceLastReport/(timeSinceLastReport/10000))/10; 

     output.innerHTML = framesPerSecond + ' fps @ ' + iterationsPerFrame + ' iter/frame'; 

     framesSinceLastReport = 0; 

     lastReportTime = time; 

     if (framesPerSecond >= 59) iterationsPerFrame = Math.floor(iterationsPerFrame * 1.2); 
    } 
} 

drawFrame(); 

要比較蘋果蘋果,我成立既是一個「經典」方法,即我只是新建矢量對象並將它們留給垃圾收集器,以及「集中」方法,我使用非收縮數組來存儲矢量對象以供重用。在這個例子中,池永遠不會超過三個矢量:

function testClassic() 
{ 
    for (var i = 0; i < iterationsPerFrame; i++) 
    { 
     var a = new Vector2(); 

     a.x = 2; 

     a.y = 3; 

     var b = new Vector2(); 

     b.x = 1; 

     b.y = 4; 

     var r = a.addUnpooled(b); 

     if (r.x != 2 + 1 || r.y != 3 + 4) throw 'Vector addition failed.'; 
    } 
} 

function testPooled() 
{ 
    for (var i = 0; i < iterationsPerFrame; i++) 
    { 
     var a = acquireVector2(); 

     a.x = 2; 

     a.y = 3; 

     var b = acquireVector2(); 

     b.x = 1; 

     b.y = 4; 

     var r = a.addPooled(b); 

     if (r.x != 2 + 1 || r.y != 3 + 4) throw 'Vector addition failed.'; 

     releaseVector2(a); 

     releaseVector2(b); 

     releaseVector2(r); 
    } 
} 

對於我的合併測試,這裏有我的獲取和釋放功能:

var vector2Pool = []; 

vector2Pool.topIndex = -1; 

function acquireVector2() 
{ 
    if (vector2Pool.topIndex >= 0) 
    { 
     var object = vector2Pool[vector2Pool.topIndex]; 

     vector2Pool[vector2Pool.topIndex] = null; 

     vector2Pool.topIndex--; 

     Vector2.apply(object); 

     return object; 
    } 
    else 
    { 
     return new Vector2(); 
    } 
} 

function releaseVector2(vector2) 
{ 
    vector2Pool.topIndex++; 

    vector2Pool[vector2Pool.topIndex] = vector2; 
} 

這一切都在期望的瀏覽器上運行,但性能測試結果,我看到的是完全給人留下深刻印象:

PC 

    Chrome 33.0.1750.154 m 

    unpooled 1077153 iter/frame 

    pooled 100677 iter/frame 

    Firefox 27.0.1 

    unpooled 100677 iter/frame 

    pooled 33718 iter/frame 

    Internet Explorer 9.0.8112.16421 

    unpooled 83898 iter/frame 

    pooled 83898 iter/frame 

iPhone 5, iOS 7.1 

    Safari Mobile 

    unpooled 208761 iter/frame 

    pooled 144974 iter/frame 

    Chrome 

    unpooled 11294 iter/frame 

    pooled 3784 iter/frame 

iPad with Retina, iOS 7.1 

    Safari Mobile 

    unpooled 208761 iter/frame 

    pooled 144974 iter/frame 

在任何情況下,我會看到從池更好的性能,而且在許多情況下性能急劇惡化。對於性能差距爲10比1的Chrome尤其如此。

我已經在網上看到其他一些文章,展示了這種技術的性能提升。我的測試有缺陷嗎?

或者我可能錯過了這種方法的重點?例如。預先性能(最高達90%!)是否更好,以防止GC在隨機時間中斷超過16ms幀?

回答

0

不知道你是否仍然在意這一點,但我相信使用Vector2.apply(object);正在實例化另一個必須由垃圾回收清理的矢量對象實例。所以實質上你並沒有通過使用該方法來清空池中的矢量對象來保存任何對象清理。

所以,簡單地手動空出: http://jsfiddle.net/gk6Gn/1/

+0

我並不看好這種行爲......但也許試試看? –