|
| 1 | +# Object Pooling for ViewportPattern: Summary |
| 2 | + |
| 3 | +This document summarizes the investigation into using an object pool to optimize `ViewportPattern` allocations and reduce Garbage Collection (GC) overhead. |
| 4 | + |
| 5 | +## Problem |
| 6 | + |
| 7 | +- Profiling has indicated significant GC pauses related to the frequent allocation and deallocation of `ViewportPattern` objects. |
| 8 | +- The `combinePatterns` function, in particular, has been identified as a hotspot for these allocations. |
| 9 | + |
| 10 | +## Proposed Solution: Object Pooling |
| 11 | + |
| 12 | +The proposed solution is to implement an object pool for `ViewportPattern` objects. This involves reusing `ViewportPattern` instances rather than creating new ones for each operation. |
| 13 | + |
| 14 | +## Structural Analysis |
| 15 | + |
| 16 | +- The `ViewportPattern` type is defined as `Pattern<ViewportNumber>`, which consists of an `anchor: Point<ViewportNumber>` and a `target: Point<ViewportNumber>`. |
| 17 | +- `Point<N>` is a two-element array `[x: N, y: N]`. |
| 18 | +- This simple and fixed structure makes `ViewportPattern` (and its constituent `Point`s) well-suited for object pooling. |
| 19 | + |
| 20 | +## Necessary Code Modifications |
| 21 | + |
| 22 | +1. **Create `ViewportPatternPool` Class:** |
| 23 | + |
| 24 | + - Implement a class (e.g., in `src/object-pool.ts`) with: |
| 25 | + - `acquire(): ViewportPattern`: Retrieves an available `ViewportPattern` from the pool or creates a new one (up to a configurable limit). |
| 26 | + - `release(pattern: ViewportPattern)`: Returns a `ViewportPattern` to the pool, making it available for reuse. |
| 27 | + - The pool would internally manage a list of `ViewportPattern` instances. Pooled patterns would have their `anchor` and `target` `Point` arrays pre-allocated. |
| 28 | + |
| 29 | +2. **Modify Core Functions to Mutate Output Parameters:** |
| 30 | + |
| 31 | + - Update functions that currently return new `ViewportPattern` or `Point` instances to accept an optional `out` parameter. If provided, these functions will mutate the `out` object/array instead of allocating a new one. |
| 32 | + - **Point Manipulation Functions (e.g., in `src/functions.ts`):** |
| 33 | + - `mapPointToViewportSpace(point, screenSize, outPoint: ViewportPoint)` |
| 34 | + - `resolveRelativePointPosition(relativePoint, pattern, outPoint: Point<N>)` (or adapt `resolveRelativePointPositionInPlace`) |
| 35 | + - **Pattern Manipulation Functions (e.g., in `src/functions.ts`):** |
| 36 | + - `mapPatternToViewportSpace(pattern, screenSize, outPattern: ViewportPattern)` |
| 37 | + - `combinePatterns(parent, child, outPattern: Pattern<ParentNumber>)` |
| 38 | + - These functions would then modify `outPattern.anchor`, `outPattern.target`, `outPoint[0]`, `outPoint[1]` directly. |
| 39 | + |
| 40 | +3. **Integrate Pool into `streamDrawablePatterns` (in `src/draw/stream-drawable-patterns.ts`):** |
| 41 | + |
| 42 | + - Instantiate or import a global `ViewportPatternPool`. |
| 43 | + - **Acquire:** Before calling `mapPatternToViewportSpace` (for initial screens) or `combinePatterns` (in the generation loop), acquire a `ViewportPattern` from the pool. |
| 44 | + ```typescript |
| 45 | + // const newViewportPattern = combinePatterns(entry.currentPattern, pattern); // OLD |
| 46 | + const pooledPattern = globalViewportPatternPool.acquire() |
| 47 | + const newViewportPattern = combinePatterns(entry.currentPattern, pattern, pooledPattern) |
| 48 | + ``` |
| 49 | + - **Release:** After a `ViewportPattern` is no longer needed (i.e., after its `QueueEntry` is processed and it has been used to generate children patterns, or if `isValidPattern` returns false and the pattern is discarded), release it back to the pool. |
| 50 | + |
| 51 | + ```typescript |
| 52 | + // yield entry; // OLD |
| 53 | + // ... |
| 54 | + // // After entry.currentPattern is fully processed: |
| 55 | + // globalViewportPatternPool.release(entry.currentPattern); |
| 56 | +
|
| 57 | + // If a newly combined pattern is not valid: |
| 58 | + if (isValidPattern(newViewportPattern, viewportBoundaries)) { |
| 59 | + // ... push to queue ... |
| 60 | + } else { |
| 61 | + globalViewportPatternPool.release(newViewportPattern) // Release if not used |
| 62 | + } |
| 63 | + ``` |
| 64 | + |
| 65 | + - Care must be taken to ensure patterns are released exactly once and only when they are no longer in use. |
| 66 | + |
| 67 | +## Trade-offs |
| 68 | + |
| 69 | +- **Benefits:** |
| 70 | + - Reduced GC pressure, leading to fewer and shorter GC pauses. |
| 71 | + - Potentially improved overall performance and smoother rendering, especially in the pattern generation loop. |
| 72 | +- **Costs:** |
| 73 | + - **Increased Code Complexity:** Introduces manual memory management aspects (acquire/release). |
| 74 | + - **New Bug Class:** Risk of bugs related to incorrect pool usage (e.g., double-release, use-after-release, pool exhaustion if releases are missed). |
| 75 | + - **Function Signature Changes:** Core utility functions will have modified signatures. |
| 76 | + |
| 77 | +## Conclusion |
| 78 | + |
| 79 | +Pooling `ViewportPattern` objects is a viable strategy for addressing the identified GC performance issues. While it introduces complexity, the potential performance gains in the critical pattern generation path could be significant. Careful implementation of the pool and the acquire/release logic is paramount. |
0 commit comments