Files
esengine/packages/core/src/Utils/Pool/Pool.ts

282 lines
7.6 KiB
TypeScript
Raw Normal View History

import { IPoolable, PoolStats } from './IPoolable';
/**
*
*
*/
export class Pool<T extends IPoolable> {
private static _pools = new Map<Function, Pool<any>>();
private _objects: T[] = [];
private _createFn: () => T;
private _maxSize: number;
private _stats: PoolStats;
private _objectSize: number; // 估算的单个对象大小
/**
*
* @param createFn
* @param maxSize 100
* @param estimatedObjectSize 1024
*/
constructor(createFn: () => T, maxSize: number = 100, estimatedObjectSize: number = 1024) {
this._createFn = createFn;
this._maxSize = maxSize;
this._objectSize = estimatedObjectSize;
this._stats = {
size: 0,
maxSize,
totalCreated: 0,
totalObtained: 0,
totalReleased: 0,
hitRate: 0,
estimatedMemoryUsage: 0
};
}
/**
*
* @param type
* @param maxSize
* @param estimatedObjectSize
* @returns
*/
public static getPool<T extends IPoolable>(
type: new (...args: unknown[]) => T,
maxSize: number = 100,
estimatedObjectSize: number = 1024
): Pool<T> {
let pool = this._pools.get(type);
if (!pool) {
pool = new Pool<T>(() => new type(), maxSize, estimatedObjectSize);
this._pools.set(type, pool);
}
return pool;
}
/**
*
* @returns
*/
public obtain(): T {
this._stats.totalObtained++;
if (this._objects.length > 0) {
const obj = this._objects.pop()!;
this._stats.size--;
this._updateHitRate();
this._updateMemoryUsage();
return obj;
}
// 池中没有可用对象,创建新对象
this._stats.totalCreated++;
this._updateHitRate();
return this._createFn();
}
/**
*
* @param obj
*/
public release(obj: T): void {
if (!obj) return;
this._stats.totalReleased++;
// 如果池未满,将对象放回池中
if (this._stats.size < this._maxSize) {
// 重置对象状态
obj.reset();
this._objects.push(obj);
this._stats.size++;
this._updateMemoryUsage();
}
// 如果池已满,让对象被垃圾回收
}
/**
*
* @returns
*/
public getStats(): Readonly<PoolStats> {
return { ...this._stats };
}
/**
*
*/
public clear(): void {
// 重置所有对象
for (const obj of this._objects) {
obj.reset();
}
this._objects.length = 0;
this._stats.size = 0;
this._updateMemoryUsage();
}
/**
*
* @param targetSize
*/
public compact(targetSize?: number): void {
const target = targetSize ?? Math.floor(this._objects.length / 2);
while (this._objects.length > target) {
const obj = this._objects.pop();
if (obj) {
obj.reset();
this._stats.size--;
}
}
this._updateMemoryUsage();
}
/**
*
* @param count
*/
public prewarm(count: number): void {
const actualCount = Math.min(count, this._maxSize - this._objects.length);
for (let i = 0; i < actualCount; i++) {
const obj = this._createFn();
obj.reset();
this._objects.push(obj);
this._stats.totalCreated++;
this._stats.size++;
}
this._updateMemoryUsage();
}
/**
*
* @param maxSize
*/
public setMaxSize(maxSize: number): void {
this._maxSize = maxSize;
this._stats.maxSize = maxSize;
// 如果当前池大小超过新的最大值,进行压缩
if (this._objects.length > maxSize) {
this.compact(maxSize);
}
}
/**
*
* @returns
*/
public getAvailableCount(): number {
return this._objects.length;
}
/**
*
* @returns true
*/
public isEmpty(): boolean {
return this._objects.length === 0;
}
/**
*
* @returns true
*/
public isFull(): boolean {
return this._objects.length >= this._maxSize;
}
/**
*
* @returns
*/
public static getAllPoolTypes(): Function[] {
return Array.from(this._pools.keys());
}
/**
*
* @returns
*/
public static getAllPoolStats(): Record<string, PoolStats> {
const stats: Record<string, PoolStats> = {};
for (const [type, pool] of this._pools) {
const typeName = type.name || type.toString();
stats[typeName] = pool.getStats();
}
return stats;
}
/**
*
*/
public static compactAllPools(): void {
for (const pool of this._pools.values()) {
pool.compact();
}
}
/**
*
*/
public static clearAllPools(): void {
for (const pool of this._pools.values()) {
pool.clear();
}
this._pools.clear();
}
/**
*
* @returns
*/
public static getGlobalStatsString(): string {
const stats = this.getAllPoolStats();
const lines: string[] = ['=== Object Pool Global Statistics ===', ''];
if (Object.keys(stats).length === 0) {
lines.push('No pools registered');
return lines.join('\n');
}
for (const [typeName, stat] of Object.entries(stats)) {
lines.push(`${typeName}:`);
lines.push(` Size: ${stat.size}/${stat.maxSize}`);
lines.push(` Hit Rate: ${(stat.hitRate * 100).toFixed(1)}%`);
lines.push(` Total Created: ${stat.totalCreated}`);
lines.push(` Total Obtained: ${stat.totalObtained}`);
lines.push(` Memory: ${(stat.estimatedMemoryUsage / 1024).toFixed(1)} KB`);
lines.push('');
}
return lines.join('\n');
}
/**
*
*/
private _updateHitRate(): void {
if (this._stats.totalObtained === 0) {
this._stats.hitRate = 0;
} else {
const hits = this._stats.totalObtained - this._stats.totalCreated;
this._stats.hitRate = hits / this._stats.totalObtained;
}
}
/**
* 使
*/
private _updateMemoryUsage(): void {
this._stats.estimatedMemoryUsage = this._stats.size * this._objectSize;
}
}