我正在嘗試構建一個在瀏覽器中運行的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幀?
我並不看好這種行爲......但也許試試看? –