@holoscript/core 1.0.0-alpha.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -9
- package/src/HoloScript2DParser.js +227 -0
- package/src/HoloScript2DParser.ts +5 -0
- package/src/HoloScriptCodeParser.js +1102 -0
- package/src/HoloScriptCodeParser.ts +145 -20
- package/src/HoloScriptDebugger.js +458 -0
- package/src/HoloScriptParser.js +338 -0
- package/src/HoloScriptPlusParser.js +371 -0
- package/src/HoloScriptPlusParser.ts +543 -0
- package/src/HoloScriptRuntime.js +1399 -0
- package/src/HoloScriptRuntime.test.js +351 -0
- package/src/HoloScriptRuntime.ts +257 -3
- package/src/HoloScriptTypeChecker.js +356 -0
- package/src/__tests__/GraphicsServices.test.js +357 -0
- package/src/__tests__/GraphicsServices.test.ts +427 -0
- package/src/__tests__/HoloScriptPlusParser.test.js +317 -0
- package/src/__tests__/HoloScriptPlusParser.test.ts +392 -0
- package/src/__tests__/integration.test.js +336 -0
- package/src/__tests__/performance.bench.js +218 -0
- package/src/__tests__/type-checker.test.js +60 -0
- package/src/__tests__/type-checker.test.ts +73 -0
- package/src/index.js +217 -0
- package/src/index.ts +158 -18
- package/src/interop/Interoperability.js +413 -0
- package/src/interop/Interoperability.ts +494 -0
- package/src/logger.js +42 -0
- package/src/parser/EnhancedParser.js +205 -0
- package/src/parser/EnhancedParser.ts +251 -0
- package/src/parser/HoloScriptPlusParser.js +928 -0
- package/src/parser/HoloScriptPlusParser.ts +1089 -0
- package/src/runtime/HoloScriptPlusRuntime.js +674 -0
- package/src/runtime/HoloScriptPlusRuntime.ts +861 -0
- package/src/runtime/PerformanceTelemetry.js +323 -0
- package/src/runtime/PerformanceTelemetry.ts +467 -0
- package/src/runtime/RuntimeOptimization.js +361 -0
- package/src/runtime/RuntimeOptimization.ts +416 -0
- package/src/services/HololandGraphicsPipelineService.js +506 -0
- package/src/services/HololandGraphicsPipelineService.ts +662 -0
- package/src/services/PlatformPerformanceOptimizer.js +356 -0
- package/src/services/PlatformPerformanceOptimizer.ts +503 -0
- package/src/state/ReactiveState.js +427 -0
- package/src/state/ReactiveState.ts +572 -0
- package/src/tools/DeveloperExperience.js +376 -0
- package/src/tools/DeveloperExperience.ts +438 -0
- package/src/traits/AIDriverTrait.js +322 -0
- package/src/traits/AIDriverTrait.test.js +329 -0
- package/src/traits/AIDriverTrait.test.ts +357 -0
- package/src/traits/AIDriverTrait.ts +474 -0
- package/src/traits/LightingTrait.js +313 -0
- package/src/traits/LightingTrait.test.js +410 -0
- package/src/traits/LightingTrait.test.ts +462 -0
- package/src/traits/LightingTrait.ts +505 -0
- package/src/traits/MaterialTrait.js +194 -0
- package/src/traits/MaterialTrait.test.js +286 -0
- package/src/traits/MaterialTrait.test.ts +329 -0
- package/src/traits/MaterialTrait.ts +324 -0
- package/src/traits/RenderingTrait.js +356 -0
- package/src/traits/RenderingTrait.test.js +363 -0
- package/src/traits/RenderingTrait.test.ts +427 -0
- package/src/traits/RenderingTrait.ts +555 -0
- package/src/traits/VRTraitSystem.js +740 -0
- package/src/traits/VRTraitSystem.ts +1040 -0
- package/src/traits/VoiceInputTrait.js +284 -0
- package/src/traits/VoiceInputTrait.test.js +226 -0
- package/src/traits/VoiceInputTrait.test.ts +252 -0
- package/src/traits/VoiceInputTrait.ts +401 -0
- package/src/types/AdvancedTypeSystem.js +226 -0
- package/src/types/AdvancedTypeSystem.ts +494 -0
- package/src/types/HoloScriptPlus.d.ts +853 -0
- package/src/types.js +6 -0
- package/src/types.ts +96 -1
- package/tsconfig.json +1 -1
- package/tsup.config.d.ts +2 -0
- package/tsup.config.js +18 -0
- package/LICENSE +0 -21
- package/dist/chunk-3X2EGU7Z.cjs +0 -52
- package/dist/chunk-3X2EGU7Z.cjs.map +0 -1
- package/dist/chunk-723TPVHD.js +0 -1074
- package/dist/chunk-723TPVHD.js.map +0 -1
- package/dist/chunk-EOKNAVDO.cjs +0 -424
- package/dist/chunk-EOKNAVDO.cjs.map +0 -1
- package/dist/chunk-HQZ3HUMY.js +0 -1087
- package/dist/chunk-HQZ3HUMY.js.map +0 -1
- package/dist/chunk-KWYIVRIH.js +0 -344
- package/dist/chunk-KWYIVRIH.js.map +0 -1
- package/dist/chunk-LKH4ZAN6.js +0 -421
- package/dist/chunk-LKH4ZAN6.js.map +0 -1
- package/dist/chunk-SATNCODL.js +0 -45
- package/dist/chunk-SATNCODL.js.map +0 -1
- package/dist/chunk-VMZN4EVR.cjs +0 -347
- package/dist/chunk-VMZN4EVR.cjs.map +0 -1
- package/dist/chunk-VV3UUUYP.cjs +0 -1089
- package/dist/chunk-VV3UUUYP.cjs.map +0 -1
- package/dist/chunk-XRYTSQHZ.cjs +0 -1076
- package/dist/chunk-XRYTSQHZ.cjs.map +0 -1
- package/dist/debugger.cjs +0 -19
- package/dist/debugger.cjs.map +0 -1
- package/dist/debugger.d.cts +0 -171
- package/dist/debugger.d.ts +0 -171
- package/dist/debugger.js +0 -6
- package/dist/debugger.js.map +0 -1
- package/dist/index.cjs +0 -755
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -169
- package/dist/index.d.ts +0 -169
- package/dist/index.js +0 -699
- package/dist/index.js.map +0 -1
- package/dist/parser.cjs +0 -13
- package/dist/parser.cjs.map +0 -1
- package/dist/parser.d.cts +0 -154
- package/dist/parser.d.ts +0 -154
- package/dist/parser.js +0 -4
- package/dist/parser.js.map +0 -1
- package/dist/runtime.cjs +0 -13
- package/dist/runtime.cjs.map +0 -1
- package/dist/runtime.d.cts +0 -147
- package/dist/runtime.d.ts +0 -147
- package/dist/runtime.js +0 -4
- package/dist/runtime.js.map +0 -1
- package/dist/type-checker.cjs +0 -16
- package/dist/type-checker.cjs.map +0 -1
- package/dist/type-checker.d.cts +0 -105
- package/dist/type-checker.d.ts +0 -105
- package/dist/type-checker.js +0 -3
- package/dist/type-checker.js.map +0 -1
- package/dist/types-WQSk1Qs2.d.cts +0 -238
- package/dist/types-WQSk1Qs2.d.ts +0 -238
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/core Runtime Optimization
|
|
3
|
+
*
|
|
4
|
+
* Object pooling, lazy evaluation, memoization, caching
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generic object pool for efficient memory reuse
|
|
9
|
+
*/
|
|
10
|
+
export class ObjectPool<T> {
|
|
11
|
+
private available: T[] = [];
|
|
12
|
+
private inUse: Set<T> = new Set();
|
|
13
|
+
private peakUsage: number = 0;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
private factory: () => T,
|
|
17
|
+
private reset: (obj: T) => void,
|
|
18
|
+
private capacity: number = 100
|
|
19
|
+
) {
|
|
20
|
+
this.preallocate(capacity);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Pre-allocate objects
|
|
25
|
+
*/
|
|
26
|
+
private preallocate(count: number): void {
|
|
27
|
+
for (let i = 0; i < count; i++) {
|
|
28
|
+
this.available.push(this.factory());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Acquire object from pool
|
|
34
|
+
*/
|
|
35
|
+
acquire(): T {
|
|
36
|
+
let obj = this.available.pop();
|
|
37
|
+
|
|
38
|
+
if (!obj) {
|
|
39
|
+
obj = this.factory();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.inUse.add(obj);
|
|
43
|
+
this.peakUsage = Math.max(this.peakUsage, this.inUse.size);
|
|
44
|
+
|
|
45
|
+
return obj;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Release object back to pool
|
|
50
|
+
*/
|
|
51
|
+
release(obj: T): void {
|
|
52
|
+
if (!this.inUse.has(obj)) {
|
|
53
|
+
console.warn('Releasing object not from this pool');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.inUse.delete(obj);
|
|
58
|
+
this.reset(obj);
|
|
59
|
+
this.available.push(obj);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Batch acquire
|
|
64
|
+
*/
|
|
65
|
+
acquireBatch(count: number): T[] {
|
|
66
|
+
const batch: T[] = [];
|
|
67
|
+
for (let i = 0; i < count; i++) {
|
|
68
|
+
batch.push(this.acquire());
|
|
69
|
+
}
|
|
70
|
+
return batch;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Batch release
|
|
75
|
+
*/
|
|
76
|
+
releaseBatch(objects: T[]): void {
|
|
77
|
+
for (const obj of objects) {
|
|
78
|
+
this.release(obj);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get pool statistics
|
|
84
|
+
*/
|
|
85
|
+
getStats() {
|
|
86
|
+
return {
|
|
87
|
+
available: this.available.length,
|
|
88
|
+
inUse: this.inUse.size,
|
|
89
|
+
peakUsage: this.peakUsage,
|
|
90
|
+
utilization: this.inUse.size / (this.inUse.size + this.available.length),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Clear pool
|
|
96
|
+
*/
|
|
97
|
+
clear(): void {
|
|
98
|
+
this.available = [];
|
|
99
|
+
this.inUse.clear();
|
|
100
|
+
this.peakUsage = 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Lazy evaluated value
|
|
106
|
+
*/
|
|
107
|
+
export class Lazy<T> {
|
|
108
|
+
private value: T | undefined;
|
|
109
|
+
private computed: boolean = false;
|
|
110
|
+
|
|
111
|
+
constructor(private compute: () => T) {}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get value (compute if needed)
|
|
115
|
+
*/
|
|
116
|
+
get(): T {
|
|
117
|
+
if (!this.computed) {
|
|
118
|
+
this.value = this.compute();
|
|
119
|
+
this.computed = true;
|
|
120
|
+
}
|
|
121
|
+
return this.value!;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Force re-computation
|
|
126
|
+
*/
|
|
127
|
+
reset(): void {
|
|
128
|
+
this.computed = false;
|
|
129
|
+
this.value = undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if computed
|
|
134
|
+
*/
|
|
135
|
+
isComputed(): boolean {
|
|
136
|
+
return this.computed;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Memoization decorator
|
|
142
|
+
*/
|
|
143
|
+
export function memoize<T extends (...args: any[]) => any>(fn: T, maxSize: number = 100): T {
|
|
144
|
+
const cache = new Map<string, any>();
|
|
145
|
+
|
|
146
|
+
return ((...args: any[]) => {
|
|
147
|
+
const key = JSON.stringify(args);
|
|
148
|
+
|
|
149
|
+
if (cache.has(key)) {
|
|
150
|
+
return cache.get(key);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = fn(...args);
|
|
154
|
+
|
|
155
|
+
if (cache.size >= maxSize) {
|
|
156
|
+
const firstKey = cache.keys().next().value;
|
|
157
|
+
cache.delete(firstKey);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
cache.set(key, result);
|
|
161
|
+
return result;
|
|
162
|
+
}) as T;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Memoized property decorator
|
|
167
|
+
*/
|
|
168
|
+
export function MemoizedProperty() {
|
|
169
|
+
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
170
|
+
const originalGetter = descriptor.get;
|
|
171
|
+
const cache = new Map<any, any>();
|
|
172
|
+
|
|
173
|
+
descriptor.get = function () {
|
|
174
|
+
if (!cache.has(this)) {
|
|
175
|
+
cache.set(this, originalGetter.call(this));
|
|
176
|
+
}
|
|
177
|
+
return cache.get(this);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return descriptor;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* LRU Cache with maximum size
|
|
186
|
+
*/
|
|
187
|
+
export class LRUCache<K, V> {
|
|
188
|
+
private cache: Map<K, V> = new Map();
|
|
189
|
+
private accessOrder: K[] = [];
|
|
190
|
+
|
|
191
|
+
constructor(private maxSize: number = 100) {}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get value from cache
|
|
195
|
+
*/
|
|
196
|
+
get(key: K): V | undefined {
|
|
197
|
+
if (this.cache.has(key)) {
|
|
198
|
+
// Move to end (most recently used)
|
|
199
|
+
const index = this.accessOrder.indexOf(key);
|
|
200
|
+
if (index > -1) {
|
|
201
|
+
this.accessOrder.splice(index, 1);
|
|
202
|
+
}
|
|
203
|
+
this.accessOrder.push(key);
|
|
204
|
+
|
|
205
|
+
return this.cache.get(key);
|
|
206
|
+
}
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Set value in cache
|
|
212
|
+
*/
|
|
213
|
+
set(key: K, value: V): void {
|
|
214
|
+
if (this.cache.has(key)) {
|
|
215
|
+
// Update existing
|
|
216
|
+
const index = this.accessOrder.indexOf(key);
|
|
217
|
+
if (index > -1) {
|
|
218
|
+
this.accessOrder.splice(index, 1);
|
|
219
|
+
}
|
|
220
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
221
|
+
// Evict LRU
|
|
222
|
+
const lruKey = this.accessOrder.shift()!;
|
|
223
|
+
this.cache.delete(lruKey);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.cache.set(key, value);
|
|
227
|
+
this.accessOrder.push(key);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Clear cache
|
|
232
|
+
*/
|
|
233
|
+
clear(): void {
|
|
234
|
+
this.cache.clear();
|
|
235
|
+
this.accessOrder = [];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get cache stats
|
|
240
|
+
*/
|
|
241
|
+
getStats() {
|
|
242
|
+
return {
|
|
243
|
+
size: this.cache.size,
|
|
244
|
+
maxSize: this.maxSize,
|
|
245
|
+
utilization: this.cache.size / this.maxSize,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Batch processing for efficient bulk operations
|
|
252
|
+
*/
|
|
253
|
+
export class Batcher<T, R> {
|
|
254
|
+
private queue: T[] = [];
|
|
255
|
+
private processingTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
256
|
+
|
|
257
|
+
constructor(
|
|
258
|
+
private processor: (batch: T[]) => Promise<R[]>,
|
|
259
|
+
private batchSize: number = 100,
|
|
260
|
+
private flushIntervalMs: number = 16 // ~1 frame at 60fps
|
|
261
|
+
) {}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Add item to batch
|
|
265
|
+
*/
|
|
266
|
+
async add(item: T): Promise<R> {
|
|
267
|
+
return new Promise((resolve) => {
|
|
268
|
+
this.queue.push(item);
|
|
269
|
+
|
|
270
|
+
if (this.queue.length >= this.batchSize) {
|
|
271
|
+
this.flush().then((results) => {
|
|
272
|
+
resolve(results[results.length - 1]);
|
|
273
|
+
});
|
|
274
|
+
} else if (!this.processingTimeout) {
|
|
275
|
+
this.processingTimeout = setTimeout(() => {
|
|
276
|
+
this.flush();
|
|
277
|
+
}, this.flushIntervalMs);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Flush batch
|
|
284
|
+
*/
|
|
285
|
+
private async flush(): Promise<R[]> {
|
|
286
|
+
if (this.processingTimeout) {
|
|
287
|
+
clearTimeout(this.processingTimeout);
|
|
288
|
+
this.processingTimeout = null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (this.queue.length === 0) {
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const batch = this.queue.splice(0, this.batchSize);
|
|
296
|
+
return await this.processor(batch);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Manually flush remaining items
|
|
301
|
+
*/
|
|
302
|
+
async flushAll(): Promise<R[]> {
|
|
303
|
+
const results: R[] = [];
|
|
304
|
+
|
|
305
|
+
while (this.queue.length > 0) {
|
|
306
|
+
const batchResults = await this.flush();
|
|
307
|
+
results.push(...batchResults);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return results;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Performance profiler with hot path tracking
|
|
316
|
+
*/
|
|
317
|
+
export class PerformanceProfiler {
|
|
318
|
+
private measurements: Map<string, { count: number; totalTime: number; minTime: number; maxTime: number }> =
|
|
319
|
+
new Map();
|
|
320
|
+
private activeTimers: Map<string, number> = new Map();
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Start timing a function
|
|
324
|
+
*/
|
|
325
|
+
startTimer(label: string): void {
|
|
326
|
+
this.activeTimers.set(label, performance.now());
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* End timing
|
|
331
|
+
*/
|
|
332
|
+
endTimer(label: string): number {
|
|
333
|
+
const startTime = this.activeTimers.get(label);
|
|
334
|
+
if (!startTime) {
|
|
335
|
+
console.warn(`No timer started for ${label}`);
|
|
336
|
+
return 0;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const duration = performance.now() - startTime;
|
|
340
|
+
this.activeTimers.delete(label);
|
|
341
|
+
|
|
342
|
+
// Update statistics
|
|
343
|
+
const stats = this.measurements.get(label) || { count: 0, totalTime: 0, minTime: Infinity, maxTime: -Infinity };
|
|
344
|
+
|
|
345
|
+
stats.count++;
|
|
346
|
+
stats.totalTime += duration;
|
|
347
|
+
stats.minTime = Math.min(stats.minTime, duration);
|
|
348
|
+
stats.maxTime = Math.max(stats.maxTime, duration);
|
|
349
|
+
|
|
350
|
+
this.measurements.set(label, stats);
|
|
351
|
+
|
|
352
|
+
return duration;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Measure function execution
|
|
357
|
+
*/
|
|
358
|
+
async measure<T>(label: string, fn: () => Promise<T>): Promise<T> {
|
|
359
|
+
this.startTimer(label);
|
|
360
|
+
try {
|
|
361
|
+
return await fn();
|
|
362
|
+
} finally {
|
|
363
|
+
this.endTimer(label);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get profiling report
|
|
369
|
+
*/
|
|
370
|
+
getReport(): string {
|
|
371
|
+
let report = '=== Performance Profile ===\n\n';
|
|
372
|
+
|
|
373
|
+
const sorted = Array.from(this.measurements.entries()).sort((a, b) => b[1].totalTime - a[1].totalTime);
|
|
374
|
+
|
|
375
|
+
for (const [label, stats] of sorted) {
|
|
376
|
+
const avgTime = stats.totalTime / stats.count;
|
|
377
|
+
report += `${label}:\n`;
|
|
378
|
+
report += ` Calls: ${stats.count}\n`;
|
|
379
|
+
report += ` Total: ${stats.totalTime.toFixed(2)}ms\n`;
|
|
380
|
+
report += ` Avg: ${avgTime.toFixed(2)}ms\n`;
|
|
381
|
+
report += ` Min/Max: ${stats.minTime.toFixed(2)}ms / ${stats.maxTime.toFixed(2)}ms\n\n`;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return report;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Reset measurements
|
|
389
|
+
*/
|
|
390
|
+
reset(): void {
|
|
391
|
+
this.measurements.clear();
|
|
392
|
+
this.activeTimers.clear();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Get hottest paths
|
|
397
|
+
*/
|
|
398
|
+
getHotPaths(topN: number = 5): Array<[string, number]> {
|
|
399
|
+
return Array.from(this.measurements.entries())
|
|
400
|
+
.sort((a, b) => b[1].totalTime - a[1].totalTime)
|
|
401
|
+
.slice(0, topN)
|
|
402
|
+
.map(([label, stats]) => [label, stats.totalTime]);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Global optimizer instance
|
|
408
|
+
*/
|
|
409
|
+
let globalProfiler: PerformanceProfiler | null = null;
|
|
410
|
+
|
|
411
|
+
export function getGlobalProfiler(): PerformanceProfiler {
|
|
412
|
+
if (!globalProfiler) {
|
|
413
|
+
globalProfiler = new PerformanceProfiler();
|
|
414
|
+
}
|
|
415
|
+
return globalProfiler;
|
|
416
|
+
}
|