Skip to content

Commit

Permalink
optimize pool implementation (for performance)
Browse files Browse the repository at this point in the history
remove previous inUse array, and replaced by a counter to keep track of "used" instances
  • Loading branch information
obiot committed Jul 27, 2024
1 parent 5b54a31 commit d9b6946
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 19 deletions.
22 changes: 15 additions & 7 deletions packages/melonjs/src/system/pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ test("can acquire objects from the pool", () => {
expect(object1).toBeInstanceOf(GameObject);
expect(object2).toBeInstanceOf(GameObject);
expect(object1).not.toBe(object2);
expect(pool.size()).toBe(2);
expect(pool.size()).toBe(0);
expect(pool.used()).toBe(2);
});

test("can release objects from the pool", () => {
Expand All @@ -23,27 +24,34 @@ test("can release objects from the pool", () => {
});
const object1 = pool.get();
pool.release(object1);
pool.get();
expect(pool.size()).toBe(1);
expect(pool.used()).toBe(0);
pool.get();
expect(pool.size()).toBe(0);
expect(pool.used()).toBe(1);
});

test("can manually purge", () => {
const pool = createPool(() => {
return { instance: new GameObject() };
});
const object1 = pool.get();
expect(pool.used()).toBe(1);
const object2 = pool.get();
const object3 = pool.get();
expect(pool.size()).toBe(3);
expect(pool.size()).toBe(0);
expect(pool.used()).toBe(3);
pool.release(object1);
pool.purge();
expect(pool.size()).toBe(2);
expect(pool.size()).toBe(1);
expect(pool.used()).toBe(2);
pool.release(object2);
pool.purge();
expect(pool.size()).toBe(1);
expect(pool.size()).toBe(0);
expect(pool.used()).toBe(0);
pool.release(object3);
expect(pool.size()).toBe(1);
expect(pool.size()).toBe(1);
pool.purge();
expect(pool.size()).toBe(0);
});

test("can pass arguments", () => {
Expand Down
41 changes: 29 additions & 12 deletions packages/melonjs/src/system/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface Pool<T, A extends unknown[]> {
release(object: T): void;
purge(): void;
size(): number;
used(): number;
}

type Reset<A extends unknown[]> = ((...args: A) => void) | undefined;
Expand All @@ -16,38 +17,54 @@ export const createPool = <T, A extends unknown[]>(
options: (...args: A) => CreatePoolOptions<T, A>,
): Pool<T, A> => {
const available: T[] = [];
const inUse: T[] = [];
const instanceResetMethods = new Map<T, Reset<A>>();
let inUse: number = 0;

return {
release: (object: T) => {
const index = inUse.indexOf(object);
if (index >= 0) {
const [instance] = inUse.splice(index, 1);
available.push(instance);
} else {
throw new Error("Trying to release an instance that is not in use.");
}
/**
* release an object back to the pool
* @param instance The object to release.
*/
release: (instance: T) => {
available.push(instance);
inUse--;
},
/**
* get an instance from the pool
* @param args The arguments for creating the instance.
*/
get: (...args) => {
const object = available.pop();
if (object) {
inUse.push(object);
const reset = instanceResetMethods.get(object);
reset?.(...args);
inUse++;
return object;
} else {
const { instance, reset } = options(...args);
instanceResetMethods.set(instance, reset);
inUse.push(instance);
inUse++;
return instance;
}
},
/**
* purge the pool
*/
purge: () => {
available.length = 0;
inUse = 0;
},
/**
* get the current size of the pool (how many objects are available)
*/
size: () => {
return available.length + inUse.length;
return available.length;
},
/**
* get the number of objects currently in use
*/
used: () => {
return inUse;
},
};
};

0 comments on commit d9b6946

Please sign in to comment.