perf: optimize circular buffer for numbers
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Fri, 24 May 2024 19:39:51 +0000 (21:39 +0200)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Fri, 24 May 2024 19:39:51 +0000 (21:39 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
src/circular-buffer.ts
src/pools/worker-node.ts
src/pools/worker.ts
tests/circular-buffer.test.mjs
tests/pools/utils.test.mjs

index 2975ef7797389b2cb370c0184de493c81fe85ed7..a56cd09d551e67cc865ac4e40950cb6988b76362 100644 (file)
@@ -4,17 +4,15 @@
 export const defaultBufferSize = 2048
 
 /**
- * Circular buffer.
+ * Circular buffer designed for positive numbers.
  *
- * @typeParam T - Type of buffer data.
  * @internal
  */
-export class CircularBuffer<T> {
+export class CircularBuffer {
   private readIdx: number
   private writeIdx: number
-  private items: Array<T | undefined>
+  private items: Float32Array
   private readonly maxArrayIdx: number
-  /* Buffer number of elements */
   public size: number
 
   /**
@@ -27,7 +25,7 @@ export class CircularBuffer<T> {
     this.writeIdx = 0
     this.maxArrayIdx = size - 1
     this.size = 0
-    this.items = new Array<T | undefined>(size)
+    this.items = new Float32Array(size).fill(-1)
   }
 
   /**
@@ -49,12 +47,12 @@ export class CircularBuffer<T> {
   }
 
   /**
-   * Puts data into buffer.
+   * Puts number into buffer.
    *
-   * @param data - Data to put into buffer.
+   * @param number - Number to put into buffer.
    */
-  public put (data: T): void {
-    this.items[this.writeIdx] = data
+  public put (number: number): void {
+    this.items[this.writeIdx] = number
     this.writeIdx = this.writeIdx === this.maxArrayIdx ? 0 : this.writeIdx + 1
     if (this.size < this.items.length) {
       ++this.size
@@ -62,28 +60,28 @@ export class CircularBuffer<T> {
   }
 
   /**
-   * Gets data from buffer.
+   * Gets number from buffer.
    *
-   * @returns Data from buffer.
+   * @returns Number from buffer.
    */
-  public get (): T | undefined {
+  public get (): number | undefined {
     const data = this.items[this.readIdx]
-    if (data == null) {
+    if (data === -1) {
       return
     }
-    this.items[this.readIdx] = undefined
+    this.items[this.readIdx] = -1
     this.readIdx = this.readIdx === this.maxArrayIdx ? 0 : this.readIdx + 1
     --this.size
     return data
   }
 
   /**
-   * Returns buffer as array.
+   * Returns buffer as numbers' array.
    *
-   * @returns Array of buffer data.
+   * @returns Numbers' array.
    */
-  public toArray (): T[] {
-    return this.items.filter(item => item != null) as T[]
+  public toArray (): number[] {
+    return Array.from(this.items.filter(item => item !== -1))
   }
 
   private checkSize (size: number): void {
index d87a79c099c5dab34126e284ca2f14f4be72b3a1..079557010565cc7a7e129d0f314caf4762eb7700 100644 (file)
@@ -240,17 +240,17 @@ export class WorkerNode<Worker extends IWorker, Data = unknown>
         failed: 0
       },
       runTime: {
-        history: new CircularBuffer<number>(MeasurementHistorySize)
+        history: new CircularBuffer(MeasurementHistorySize)
       },
       waitTime: {
-        history: new CircularBuffer<number>(MeasurementHistorySize)
+        history: new CircularBuffer(MeasurementHistorySize)
       },
       elu: {
         idle: {
-          history: new CircularBuffer<number>(MeasurementHistorySize)
+          history: new CircularBuffer(MeasurementHistorySize)
         },
         active: {
-          history: new CircularBuffer<number>(MeasurementHistorySize)
+          history: new CircularBuffer(MeasurementHistorySize)
         }
       }
     }
@@ -283,17 +283,17 @@ export class WorkerNode<Worker extends IWorker, Data = unknown>
         failed: 0
       },
       runTime: {
-        history: new CircularBuffer<number>(MeasurementHistorySize)
+        history: new CircularBuffer(MeasurementHistorySize)
       },
       waitTime: {
-        history: new CircularBuffer<number>(MeasurementHistorySize)
+        history: new CircularBuffer(MeasurementHistorySize)
       },
       elu: {
         idle: {
-          history: new CircularBuffer<number>(MeasurementHistorySize)
+          history: new CircularBuffer(MeasurementHistorySize)
         },
         active: {
-          history: new CircularBuffer<number>(MeasurementHistorySize)
+          history: new CircularBuffer(MeasurementHistorySize)
         }
       }
     }
index 7d9f3de9b24e2a8cebeed50064ff2e8ab87e71b8..b2e6ba80198480f0edfe72e45c5eee56f80d03f7 100644 (file)
@@ -86,7 +86,7 @@ export interface MeasurementStatistics {
   /**
    * Measurement history.
    */
-  readonly history: CircularBuffer<number>
+  readonly history: CircularBuffer
 }
 
 /**
index c6351eb7128ea956a817def1be81ccd63d7a03c6..2d275237413c9f91741cf3f09170a40e9446c1bd 100644 (file)
@@ -10,7 +10,7 @@ describe('Circular buffer test suite', t => {
     expect(circularBuffer.writeIdx).toBe(0)
     expect(circularBuffer.maxArrayIdx).toBe(defaultBufferSize - 1)
     expect(circularBuffer.size).toBe(0)
-    expect(circularBuffer.items).toBeInstanceOf(Array)
+    expect(circularBuffer.items).toBeInstanceOf(Float32Array)
     expect(circularBuffer.items.length).toBe(defaultBufferSize)
   })
 
@@ -18,7 +18,7 @@ describe('Circular buffer test suite', t => {
     const size = 1000
     const circularBuffer = new CircularBuffer(size)
     expect(circularBuffer.maxArrayIdx).toBe(size - 1)
-    expect(circularBuffer.items).toBeInstanceOf(Array)
+    expect(circularBuffer.items).toBeInstanceOf(Float32Array)
     expect(circularBuffer.items.length).toBe(size)
   })
 
@@ -41,27 +41,29 @@ describe('Circular buffer test suite', t => {
   it('Verify that circular buffer put() works as intended', () => {
     const circularBuffer = new CircularBuffer(4)
     circularBuffer.put(1)
-    expect(circularBuffer.items).toMatchObject([1])
+    expect(circularBuffer.items).toStrictEqual(
+      new Float32Array([1, -1, -1, -1])
+    )
     expect(circularBuffer.writeIdx).toBe(1)
     expect(circularBuffer.size).toBe(1)
     circularBuffer.put(2)
-    expect(circularBuffer.items).toMatchObject([1, 2])
+    expect(circularBuffer.items).toStrictEqual(new Float32Array([1, 2, -1, -1]))
     expect(circularBuffer.writeIdx).toBe(2)
     expect(circularBuffer.size).toBe(2)
     circularBuffer.put(3)
-    expect(circularBuffer.items).toMatchObject([1, 2, 3])
+    expect(circularBuffer.items).toStrictEqual(new Float32Array([1, 2, 3, -1]))
     expect(circularBuffer.writeIdx).toBe(3)
     expect(circularBuffer.size).toBe(3)
     circularBuffer.put(4)
-    expect(circularBuffer.items).toMatchObject([1, 2, 3, 4])
+    expect(circularBuffer.items).toStrictEqual(new Float32Array([1, 2, 3, 4]))
     expect(circularBuffer.writeIdx).toBe(0)
     expect(circularBuffer.size).toBe(4)
     circularBuffer.put(5)
-    expect(circularBuffer.items).toMatchObject([5, 2, 3, 4])
+    expect(circularBuffer.items).toStrictEqual(new Float32Array([5, 2, 3, 4]))
     expect(circularBuffer.writeIdx).toBe(1)
     expect(circularBuffer.size).toBe(4)
     circularBuffer.put(6)
-    expect(circularBuffer.items).toMatchObject([5, 6, 3, 4])
+    expect(circularBuffer.items).toStrictEqual(new Float32Array([5, 6, 3, 4]))
     expect(circularBuffer.writeIdx).toBe(2)
     expect(circularBuffer.size).toBe(4)
   })
index 3d9634ff50256ea0cac40b80351ba688a5c66a64..85a00fba2471b151add354c0a954a61a7f0208f4 100644 (file)
@@ -36,79 +36,73 @@ describe('Pool utils test suite', () => {
   })
 
   it('Verify updateMeasurementStatistics() behavior', () => {
-    const circularBuffer = new CircularBuffer(MeasurementHistorySize)
+    // const circularBuffer = new CircularBuffer(MeasurementHistorySize)
     const measurementStatistics = {
-      history: circularBuffer
+      history: new CircularBuffer(MeasurementHistorySize)
     }
     updateMeasurementStatistics(
       measurementStatistics,
       { aggregate: true, average: false, median: false },
       0.01
     )
-    expect(measurementStatistics).toStrictEqual({
+    expect(measurementStatistics).toMatchObject({
       aggregate: 0.01,
       maximum: 0.01,
-      minimum: 0.01,
-      history: circularBuffer
+      minimum: 0.01
     })
     updateMeasurementStatistics(
       measurementStatistics,
       { aggregate: true, average: false, median: false },
       0.02
     )
-    expect(measurementStatistics).toStrictEqual({
+    expect(measurementStatistics).toMatchObject({
       aggregate: 0.03,
       maximum: 0.02,
-      minimum: 0.01,
-      history: circularBuffer
+      minimum: 0.01
     })
     updateMeasurementStatistics(
       measurementStatistics,
       { aggregate: true, average: true, median: false },
       0.001
     )
-    expect(measurementStatistics).toStrictEqual({
+    expect(measurementStatistics).toMatchObject({
       aggregate: 0.031,
       maximum: 0.02,
       minimum: 0.001,
-      average: 0.001,
-      history: circularBuffer
+      average: 0.0010000000474974513
     })
     updateMeasurementStatistics(
       measurementStatistics,
       { aggregate: true, average: true, median: false },
       0.003
     )
-    expect(measurementStatistics).toStrictEqual({
+    expect(measurementStatistics).toMatchObject({
       aggregate: 0.034,
       maximum: 0.02,
       minimum: 0.001,
-      average: 0.002,
-      history: circularBuffer
+      average: 0.0020000000367872417
     })
     updateMeasurementStatistics(
       measurementStatistics,
       { aggregate: true, average: false, median: true },
       0.006
     )
-    expect(measurementStatistics).toStrictEqual({
+    expect(measurementStatistics).toMatchObject({
       aggregate: 0.04,
       maximum: 0.02,
       minimum: 0.001,
-      median: 0.003,
-      history: circularBuffer
+      median: 0.003000000026077032
     })
     updateMeasurementStatistics(
       measurementStatistics,
       { aggregate: true, average: true, median: false },
       0.01
     )
-    expect(measurementStatistics).toStrictEqual({
+    expect(measurementStatistics).toMatchObject({
       aggregate: 0.05,
       maximum: 0.02,
       minimum: 0.001,
-      average: 0.005,
-      history: circularBuffer
+      average: 0.004999999975552782
     })
   })