| 166 | * Circular buffer avoids allocations during benchmark. |
| 167 | */ |
| 168 | const createRollingStat = (windowSize: number): RollingStat => { |
| 169 | const buf = new Float64Array(Math.max(1, windowSize | 0)); |
| 170 | let idx = 0; |
| 171 | let count = 0; |
| 172 | let sum = 0; |
| 173 | |
| 174 | return { |
| 175 | push(value) { |
| 176 | const v = Number.isFinite(value) ? value : 0; |
| 177 | if (count < buf.length) { |
| 178 | buf[idx] = v; |
| 179 | sum += v; |
| 180 | count++; |
| 181 | idx = (idx + 1) % buf.length; |
| 182 | return; |
| 183 | } |
| 184 | |
| 185 | const prev = buf[idx]!; |
| 186 | buf[idx] = v; |
| 187 | sum += v - prev; |
| 188 | idx = (idx + 1) % buf.length; |
| 189 | }, |
| 190 | mean() { |
| 191 | if (count === 0) return 0; |
| 192 | return sum / count; |
| 193 | }, |
| 194 | }; |
| 195 | }; |
| 196 | |
| 197 | /** |
| 198 | * Main setup: initializes GPUContext, createRenderCoordinator (low-level API), |