Skip to content

aipooljs — 物件池

This content is not available in your language yet.

import { Aside } from ‘@astrojs/starlight/components’;

aipooljs 解決的問題很具體:遊戲熱路徑(子彈、粒子、特效)每幀都在大量建立與銷毀相同形狀的物件,讓 GC 不斷追著跑。object pool 用一個固定大小的預配置緩衝區取代這些配置,讓 acquire() / release() 都是 O(1),不產生 GC 壓力。

Terminal window
pnpm add aipooljs
import { createPool } from "aipooljs";
// 1. 預配置固定大小的緩衝區
const bullets = createPool({
create: () => ({ x: 0, y: 0, vx: 0, vy: 0, active: false }),
reset: (b) => {
// reset 必須用賦值,不要用 delete(會讓 V8 降格 hidden class)
b.x = 0;
b.y = 0;
b.vx = 0;
b.vy = 0;
b.active = false;
},
size: 256,
});
// 2. 熱路徑:acquire — O(1),不配置
function fireBullet(x: number, y: number, vx: number, vy: number) {
const b = bullets.acquire();
b.x = x;
b.y = y;
b.vx = vx;
b.vy = vy;
b.active = true;
return b;
}
// 3. 子彈消失時 release — O(1),reset() 先跑再歸還
function recycleBullet(b: ReturnType<typeof fireBullet>) {
bullets.release(b);
}

固定大小,溢出即丟錯acquire() 在池空時會丟出 PoolError,強迫你調整上游產生速率,而不是讓池暗自擴張讓最壞情況難以預測。若確實需要彈性,可傳入 onOverflow: 'grow'

雙重釋放偵測release() 同一個物件兩次是 object pool 的經典 bug,aipooljsSet 追蹤存活物件,第二次 release 直接丟出 PoolError

reset 不用 delete:刪除屬性會讓 V8 將物件降格成 dictionary mode,破壞 hidden class 最佳化,讓整個熱路徑從 monomorphic 變成 megamorphic。JSDoc 有明確標注這個規則。

createPool({
create, // () => T — 初始建立物件
reset, // (obj: T) => void — 歸還時重設
size, // number — 池子大小(必填)
onOverflow, // 'throw'(預設)| 'grow' | (pool) => T
})
  • 子彈/粒子池:最典型的用法,搭配 aiecsjs 的 entity 管理
  • aiquadtreejs 搭配:pool 管理物件生命週期,quadtree 管理空間查詢
  • DOM 節點回收<li> 列表項目、畫布繪圖物件等可以用同樣模式管理