edgeflowjs 0.1.0 → 0.2.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/README.md +200 -66
- package/dist/backends/index.d.ts +9 -2
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +13 -13
- package/dist/backends/index.js.map +1 -1
- package/dist/backends/onnx.d.ts +11 -4
- package/dist/backends/onnx.d.ts.map +1 -1
- package/dist/backends/onnx.js +97 -78
- package/dist/backends/onnx.js.map +1 -1
- package/dist/backends/transformers-adapter.d.ts +99 -0
- package/dist/backends/transformers-adapter.d.ts.map +1 -0
- package/dist/backends/transformers-adapter.js +171 -0
- package/dist/backends/transformers-adapter.js.map +1 -0
- package/dist/backends/webgpu.d.ts +7 -5
- package/dist/backends/webgpu.d.ts.map +1 -1
- package/dist/backends/webgpu.js +7 -5
- package/dist/backends/webgpu.js.map +1 -1
- package/dist/backends/webnn.d.ts +6 -5
- package/dist/backends/webnn.d.ts.map +1 -1
- package/dist/backends/webnn.js +6 -5
- package/dist/backends/webnn.js.map +1 -1
- package/dist/core/composer.d.ts +118 -0
- package/dist/core/composer.d.ts.map +1 -0
- package/dist/core/composer.js +163 -0
- package/dist/core/composer.js.map +1 -0
- package/dist/core/device-profiler.d.ts +75 -0
- package/dist/core/device-profiler.d.ts.map +1 -0
- package/dist/core/device-profiler.js +131 -0
- package/dist/core/device-profiler.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +8 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/memory.d.ts +22 -2
- package/dist/core/memory.d.ts.map +1 -1
- package/dist/core/memory.js +49 -13
- package/dist/core/memory.js.map +1 -1
- package/dist/core/plugin.d.ts +100 -0
- package/dist/core/plugin.d.ts.map +1 -0
- package/dist/core/plugin.js +106 -0
- package/dist/core/plugin.js.map +1 -0
- package/dist/core/runtime.d.ts +4 -0
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +18 -0
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/scheduler.d.ts +17 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +101 -3
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/types.d.ts +14 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker.d.ts +202 -0
- package/dist/core/worker.d.ts.map +1 -0
- package/dist/core/worker.js +477 -0
- package/dist/core/worker.js.map +1 -0
- package/dist/edgeflow.browser.js +9770 -4383
- package/dist/edgeflow.browser.js.map +4 -4
- package/dist/edgeflow.browser.min.js +435 -5
- package/dist/edgeflow.browser.min.js.map +4 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -10
- package/dist/index.js.map +1 -1
- package/dist/pipelines/automatic-speech-recognition.d.ts +63 -0
- package/dist/pipelines/automatic-speech-recognition.d.ts.map +1 -0
- package/dist/pipelines/automatic-speech-recognition.js +269 -0
- package/dist/pipelines/automatic-speech-recognition.js.map +1 -0
- package/dist/pipelines/base.d.ts +6 -1
- package/dist/pipelines/base.d.ts.map +1 -1
- package/dist/pipelines/base.js +12 -2
- package/dist/pipelines/base.js.map +1 -1
- package/dist/pipelines/feature-extraction.d.ts +5 -40
- package/dist/pipelines/feature-extraction.d.ts.map +1 -1
- package/dist/pipelines/feature-extraction.js +44 -63
- package/dist/pipelines/feature-extraction.js.map +1 -1
- package/dist/pipelines/image-classification.d.ts +4 -36
- package/dist/pipelines/image-classification.d.ts.map +1 -1
- package/dist/pipelines/image-classification.js +22 -60
- package/dist/pipelines/image-classification.js.map +1 -1
- package/dist/pipelines/image-segmentation.d.ts +221 -0
- package/dist/pipelines/image-segmentation.d.ts.map +1 -0
- package/dist/pipelines/image-segmentation.js +535 -0
- package/dist/pipelines/image-segmentation.js.map +1 -0
- package/dist/pipelines/index.d.ts +18 -0
- package/dist/pipelines/index.d.ts.map +1 -1
- package/dist/pipelines/index.js +51 -2
- package/dist/pipelines/index.js.map +1 -1
- package/dist/pipelines/object-detection.d.ts +44 -0
- package/dist/pipelines/object-detection.d.ts.map +1 -0
- package/dist/pipelines/object-detection.js +218 -0
- package/dist/pipelines/object-detection.js.map +1 -0
- package/dist/pipelines/question-answering.d.ts +41 -0
- package/dist/pipelines/question-answering.d.ts.map +1 -0
- package/dist/pipelines/question-answering.js +164 -0
- package/dist/pipelines/question-answering.js.map +1 -0
- package/dist/pipelines/text-classification.d.ts +3 -39
- package/dist/pipelines/text-classification.d.ts.map +1 -1
- package/dist/pipelines/text-classification.js +29 -67
- package/dist/pipelines/text-classification.js.map +1 -1
- package/dist/pipelines/text-generation.d.ts +281 -0
- package/dist/pipelines/text-generation.d.ts.map +1 -0
- package/dist/pipelines/text-generation.js +766 -0
- package/dist/pipelines/text-generation.js.map +1 -0
- package/dist/pipelines/zero-shot-classification.d.ts +45 -0
- package/dist/pipelines/zero-shot-classification.d.ts.map +1 -0
- package/dist/pipelines/zero-shot-classification.js +140 -0
- package/dist/pipelines/zero-shot-classification.js.map +1 -0
- package/dist/tools/benchmark.d.ts +92 -0
- package/dist/tools/benchmark.d.ts.map +1 -0
- package/dist/tools/benchmark.js +213 -0
- package/dist/tools/benchmark.js.map +1 -0
- package/dist/tools/debugger.d.ts +258 -0
- package/dist/tools/debugger.d.ts.map +1 -0
- package/dist/tools/debugger.js +624 -0
- package/dist/tools/debugger.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/monitor.d.ts +284 -0
- package/dist/tools/monitor.d.ts.map +1 -0
- package/dist/tools/monitor.js +921 -0
- package/dist/tools/monitor.js.map +1 -0
- package/dist/tools/quantization.d.ts +235 -0
- package/dist/tools/quantization.d.ts.map +1 -0
- package/dist/tools/quantization.js +830 -0
- package/dist/tools/quantization.js.map +1 -0
- package/dist/utils/hub.d.ts +162 -0
- package/dist/utils/hub.d.ts.map +1 -0
- package/dist/utils/hub.js +311 -0
- package/dist/utils/hub.js.map +1 -0
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/model-loader.d.ts.map +1 -1
- package/dist/utils/model-loader.js +106 -30
- package/dist/utils/model-loader.js.map +1 -1
- package/dist/utils/offline.d.ts +147 -0
- package/dist/utils/offline.d.ts.map +1 -0
- package/dist/utils/offline.js +405 -0
- package/dist/utils/offline.js.map +1 -0
- package/dist/utils/preprocessor.d.ts +82 -6
- package/dist/utils/preprocessor.d.ts.map +1 -1
- package/dist/utils/preprocessor.js +278 -21
- package/dist/utils/preprocessor.js.map +1 -1
- package/dist/utils/tokenizer.d.ts +197 -72
- package/dist/utils/tokenizer.d.ts.map +1 -1
- package/dist/utils/tokenizer.js +558 -274
- package/dist/utils/tokenizer.js.map +1 -1
- package/package.json +26 -11
|
@@ -0,0 +1,921 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgeFlow.js - Performance Monitoring Dashboard
|
|
3
|
+
*
|
|
4
|
+
* Real-time performance monitoring and metrics visualization.
|
|
5
|
+
*/
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Performance Monitor
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/**
|
|
10
|
+
* Performance monitor for edgeFlow.js
|
|
11
|
+
*/
|
|
12
|
+
export class PerformanceMonitor {
|
|
13
|
+
config;
|
|
14
|
+
samples = [];
|
|
15
|
+
isRunning = false;
|
|
16
|
+
intervalId = null;
|
|
17
|
+
alerts = [];
|
|
18
|
+
alertListeners = [];
|
|
19
|
+
sampleListeners = [];
|
|
20
|
+
// Inference tracking
|
|
21
|
+
inferenceCount = 0;
|
|
22
|
+
inferenceTimes = [];
|
|
23
|
+
queueLength = 0;
|
|
24
|
+
activeCount = 0;
|
|
25
|
+
// FPS tracking
|
|
26
|
+
frameCount = 0;
|
|
27
|
+
lastFrameTime = 0;
|
|
28
|
+
fps = 0;
|
|
29
|
+
rafId = null;
|
|
30
|
+
// Memory tracking
|
|
31
|
+
tensorMemory = 0;
|
|
32
|
+
cacheMemory = 0;
|
|
33
|
+
constructor(config = {}) {
|
|
34
|
+
this.config = {
|
|
35
|
+
enabled: config.enabled ?? true,
|
|
36
|
+
sampleInterval: config.sampleInterval ?? 1000,
|
|
37
|
+
historySize: config.historySize ?? 60,
|
|
38
|
+
monitorMemory: config.monitorMemory ?? true,
|
|
39
|
+
monitorFPS: config.monitorFPS ?? true,
|
|
40
|
+
collectors: config.collectors ?? [],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start monitoring
|
|
45
|
+
*/
|
|
46
|
+
start() {
|
|
47
|
+
if (this.isRunning)
|
|
48
|
+
return;
|
|
49
|
+
this.isRunning = true;
|
|
50
|
+
// Start sampling
|
|
51
|
+
this.intervalId = setInterval(() => {
|
|
52
|
+
this.collectSample();
|
|
53
|
+
}, this.config.sampleInterval);
|
|
54
|
+
// Start FPS monitoring
|
|
55
|
+
if (this.config.monitorFPS && typeof requestAnimationFrame !== 'undefined') {
|
|
56
|
+
this.lastFrameTime = performance.now();
|
|
57
|
+
this.frameCount = 0;
|
|
58
|
+
this.monitorFPS();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Stop monitoring
|
|
63
|
+
*/
|
|
64
|
+
stop() {
|
|
65
|
+
this.isRunning = false;
|
|
66
|
+
if (this.intervalId) {
|
|
67
|
+
clearInterval(this.intervalId);
|
|
68
|
+
this.intervalId = null;
|
|
69
|
+
}
|
|
70
|
+
if (this.rafId) {
|
|
71
|
+
cancelAnimationFrame(this.rafId);
|
|
72
|
+
this.rafId = null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Monitor FPS
|
|
77
|
+
*/
|
|
78
|
+
monitorFPS() {
|
|
79
|
+
if (!this.isRunning)
|
|
80
|
+
return;
|
|
81
|
+
this.frameCount++;
|
|
82
|
+
const now = performance.now();
|
|
83
|
+
const elapsed = now - this.lastFrameTime;
|
|
84
|
+
if (elapsed >= 1000) {
|
|
85
|
+
this.fps = Math.round((this.frameCount * 1000) / elapsed);
|
|
86
|
+
this.frameCount = 0;
|
|
87
|
+
this.lastFrameTime = now;
|
|
88
|
+
}
|
|
89
|
+
this.rafId = requestAnimationFrame(() => this.monitorFPS());
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Collect a performance sample
|
|
93
|
+
*/
|
|
94
|
+
collectSample() {
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
// Calculate inference metrics
|
|
97
|
+
const avgTime = this.inferenceTimes.length > 0
|
|
98
|
+
? this.inferenceTimes.reduce((a, b) => a + b, 0) / this.inferenceTimes.length
|
|
99
|
+
: 0;
|
|
100
|
+
const minTime = this.inferenceTimes.length > 0
|
|
101
|
+
? Math.min(...this.inferenceTimes)
|
|
102
|
+
: 0;
|
|
103
|
+
const maxTime = this.inferenceTimes.length > 0
|
|
104
|
+
? Math.max(...this.inferenceTimes)
|
|
105
|
+
: 0;
|
|
106
|
+
const throughput = this.inferenceCount / (this.config.sampleInterval / 1000);
|
|
107
|
+
const inference = {
|
|
108
|
+
count: this.inferenceCount,
|
|
109
|
+
avgTime,
|
|
110
|
+
minTime,
|
|
111
|
+
maxTime,
|
|
112
|
+
throughput,
|
|
113
|
+
queueLength: this.queueLength,
|
|
114
|
+
activeCount: this.activeCount,
|
|
115
|
+
};
|
|
116
|
+
// Collect memory metrics
|
|
117
|
+
const memory = this.collectMemoryMetrics();
|
|
118
|
+
// Collect system metrics
|
|
119
|
+
const system = this.collectSystemMetrics();
|
|
120
|
+
// Collect custom metrics
|
|
121
|
+
const custom = {};
|
|
122
|
+
for (const collector of this.config.collectors) {
|
|
123
|
+
try {
|
|
124
|
+
Object.assign(custom, collector());
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Ignore collector errors
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const sample = {
|
|
131
|
+
timestamp: now,
|
|
132
|
+
inference,
|
|
133
|
+
memory,
|
|
134
|
+
system,
|
|
135
|
+
custom,
|
|
136
|
+
};
|
|
137
|
+
// Add to history
|
|
138
|
+
this.samples.push(sample);
|
|
139
|
+
if (this.samples.length > this.config.historySize) {
|
|
140
|
+
this.samples.shift();
|
|
141
|
+
}
|
|
142
|
+
// Check alerts
|
|
143
|
+
this.checkAlerts(sample);
|
|
144
|
+
// Notify listeners
|
|
145
|
+
for (const listener of this.sampleListeners) {
|
|
146
|
+
listener(sample);
|
|
147
|
+
}
|
|
148
|
+
// Reset counters
|
|
149
|
+
this.inferenceCount = 0;
|
|
150
|
+
this.inferenceTimes = [];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Collect memory metrics
|
|
154
|
+
*/
|
|
155
|
+
collectMemoryMetrics() {
|
|
156
|
+
let usedHeap = 0;
|
|
157
|
+
let totalHeap = 0;
|
|
158
|
+
let heapLimit = 0;
|
|
159
|
+
if (typeof performance !== 'undefined' && 'memory' in performance) {
|
|
160
|
+
const memory = performance.memory;
|
|
161
|
+
usedHeap = memory.usedJSHeapSize;
|
|
162
|
+
totalHeap = memory.totalJSHeapSize;
|
|
163
|
+
heapLimit = memory.jsHeapSizeLimit;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
usedHeap,
|
|
167
|
+
totalHeap,
|
|
168
|
+
heapLimit,
|
|
169
|
+
heapUsage: heapLimit > 0 ? usedHeap / heapLimit : 0,
|
|
170
|
+
tensorMemory: this.tensorMemory,
|
|
171
|
+
cacheMemory: this.cacheMemory,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Collect system metrics
|
|
176
|
+
*/
|
|
177
|
+
collectSystemMetrics() {
|
|
178
|
+
const lastSample = this.samples[this.samples.length - 1];
|
|
179
|
+
const deltaTime = lastSample
|
|
180
|
+
? Date.now() - lastSample.timestamp
|
|
181
|
+
: this.config.sampleInterval;
|
|
182
|
+
// Check WebGPU availability
|
|
183
|
+
let webgpuAvailable = false;
|
|
184
|
+
if (typeof navigator !== 'undefined' && 'gpu' in navigator) {
|
|
185
|
+
webgpuAvailable = true;
|
|
186
|
+
}
|
|
187
|
+
// Check WebNN availability
|
|
188
|
+
let webnnAvailable = false;
|
|
189
|
+
if (typeof navigator !== 'undefined' && 'ml' in navigator) {
|
|
190
|
+
webnnAvailable = true;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
fps: this.fps,
|
|
194
|
+
cpuUsage: this.estimateCPUUsage(),
|
|
195
|
+
deltaTime,
|
|
196
|
+
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',
|
|
197
|
+
webgpuAvailable,
|
|
198
|
+
webnnAvailable,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Estimate CPU usage based on inference times
|
|
203
|
+
*/
|
|
204
|
+
estimateCPUUsage() {
|
|
205
|
+
if (this.inferenceTimes.length === 0)
|
|
206
|
+
return 0;
|
|
207
|
+
const totalTime = this.inferenceTimes.reduce((a, b) => a + b, 0);
|
|
208
|
+
return Math.min(1, totalTime / this.config.sampleInterval);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check alerts
|
|
212
|
+
*/
|
|
213
|
+
checkAlerts(sample) {
|
|
214
|
+
for (const alert of this.alerts) {
|
|
215
|
+
const value = this.getMetricValue(sample, alert.metric);
|
|
216
|
+
if (value === undefined)
|
|
217
|
+
continue;
|
|
218
|
+
let triggered = false;
|
|
219
|
+
switch (alert.operator) {
|
|
220
|
+
case '>':
|
|
221
|
+
triggered = value > alert.threshold;
|
|
222
|
+
break;
|
|
223
|
+
case '<':
|
|
224
|
+
triggered = value < alert.threshold;
|
|
225
|
+
break;
|
|
226
|
+
case '>=':
|
|
227
|
+
triggered = value >= alert.threshold;
|
|
228
|
+
break;
|
|
229
|
+
case '<=':
|
|
230
|
+
triggered = value <= alert.threshold;
|
|
231
|
+
break;
|
|
232
|
+
case '==':
|
|
233
|
+
triggered = value === alert.threshold;
|
|
234
|
+
break;
|
|
235
|
+
case '!=':
|
|
236
|
+
triggered = value !== alert.threshold;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
if (triggered) {
|
|
240
|
+
const event = {
|
|
241
|
+
config: alert,
|
|
242
|
+
value,
|
|
243
|
+
timestamp: sample.timestamp,
|
|
244
|
+
};
|
|
245
|
+
for (const listener of this.alertListeners) {
|
|
246
|
+
listener(event);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Get metric value from sample
|
|
253
|
+
*/
|
|
254
|
+
getMetricValue(sample, metric) {
|
|
255
|
+
const parts = metric.split('.');
|
|
256
|
+
let value = sample;
|
|
257
|
+
for (const part of parts) {
|
|
258
|
+
if (value && typeof value === 'object' && part in value) {
|
|
259
|
+
value = value[part];
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return typeof value === 'number' ? value : undefined;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Record an inference
|
|
269
|
+
*/
|
|
270
|
+
recordInference(duration) {
|
|
271
|
+
this.inferenceCount++;
|
|
272
|
+
this.inferenceTimes.push(duration);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Update queue length
|
|
276
|
+
*/
|
|
277
|
+
updateQueueLength(length) {
|
|
278
|
+
this.queueLength = length;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Update active count
|
|
282
|
+
*/
|
|
283
|
+
updateActiveCount(count) {
|
|
284
|
+
this.activeCount = count;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Update tensor memory
|
|
288
|
+
*/
|
|
289
|
+
updateTensorMemory(bytes) {
|
|
290
|
+
this.tensorMemory = bytes;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Update cache memory
|
|
294
|
+
*/
|
|
295
|
+
updateCacheMemory(bytes) {
|
|
296
|
+
this.cacheMemory = bytes;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Add an alert
|
|
300
|
+
*/
|
|
301
|
+
addAlert(config) {
|
|
302
|
+
this.alerts.push(config);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Remove an alert
|
|
306
|
+
*/
|
|
307
|
+
removeAlert(metric) {
|
|
308
|
+
this.alerts = this.alerts.filter(a => a.metric !== metric);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Subscribe to alerts
|
|
312
|
+
*/
|
|
313
|
+
onAlert(callback) {
|
|
314
|
+
this.alertListeners.push(callback);
|
|
315
|
+
return () => {
|
|
316
|
+
const idx = this.alertListeners.indexOf(callback);
|
|
317
|
+
if (idx !== -1)
|
|
318
|
+
this.alertListeners.splice(idx, 1);
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Subscribe to samples
|
|
323
|
+
*/
|
|
324
|
+
onSample(callback) {
|
|
325
|
+
this.sampleListeners.push(callback);
|
|
326
|
+
return () => {
|
|
327
|
+
const idx = this.sampleListeners.indexOf(callback);
|
|
328
|
+
if (idx !== -1)
|
|
329
|
+
this.sampleListeners.splice(idx, 1);
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get current sample
|
|
334
|
+
*/
|
|
335
|
+
getCurrentSample() {
|
|
336
|
+
return this.samples[this.samples.length - 1];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get all samples
|
|
340
|
+
*/
|
|
341
|
+
getSamples() {
|
|
342
|
+
return [...this.samples];
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Get samples in time range
|
|
346
|
+
*/
|
|
347
|
+
getSamplesInRange(startTime, endTime) {
|
|
348
|
+
return this.samples.filter(s => s.timestamp >= startTime && s.timestamp <= endTime);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Get summary statistics
|
|
352
|
+
*/
|
|
353
|
+
getSummary() {
|
|
354
|
+
if (this.samples.length === 0) {
|
|
355
|
+
return {
|
|
356
|
+
avgInferenceTime: 0,
|
|
357
|
+
avgThroughput: 0,
|
|
358
|
+
avgMemoryUsage: 0,
|
|
359
|
+
avgFPS: 0,
|
|
360
|
+
totalInferences: 0,
|
|
361
|
+
uptime: 0,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const avgInferenceTime = this.samples.reduce((sum, s) => sum + s.inference.avgTime, 0) / this.samples.length;
|
|
365
|
+
const avgThroughput = this.samples.reduce((sum, s) => sum + s.inference.throughput, 0) / this.samples.length;
|
|
366
|
+
const avgMemoryUsage = this.samples.reduce((sum, s) => sum + s.memory.heapUsage, 0) / this.samples.length;
|
|
367
|
+
const avgFPS = this.samples.reduce((sum, s) => sum + s.system.fps, 0) / this.samples.length;
|
|
368
|
+
const totalInferences = this.samples.reduce((sum, s) => sum + s.inference.count, 0);
|
|
369
|
+
const firstSample = this.samples[0];
|
|
370
|
+
const lastSample = this.samples[this.samples.length - 1];
|
|
371
|
+
const uptime = lastSample.timestamp - firstSample.timestamp;
|
|
372
|
+
return {
|
|
373
|
+
avgInferenceTime,
|
|
374
|
+
avgThroughput,
|
|
375
|
+
avgMemoryUsage,
|
|
376
|
+
avgFPS,
|
|
377
|
+
totalInferences,
|
|
378
|
+
uptime,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Clear all data
|
|
383
|
+
*/
|
|
384
|
+
clear() {
|
|
385
|
+
this.samples = [];
|
|
386
|
+
this.inferenceCount = 0;
|
|
387
|
+
this.inferenceTimes = [];
|
|
388
|
+
this.queueLength = 0;
|
|
389
|
+
this.activeCount = 0;
|
|
390
|
+
this.tensorMemory = 0;
|
|
391
|
+
this.cacheMemory = 0;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Export data
|
|
395
|
+
*/
|
|
396
|
+
export() {
|
|
397
|
+
return {
|
|
398
|
+
samples: this.getSamples(),
|
|
399
|
+
summary: this.getSummary(),
|
|
400
|
+
config: this.config,
|
|
401
|
+
timestamp: Date.now(),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// ============================================================================
|
|
406
|
+
// Dashboard Generator
|
|
407
|
+
// ============================================================================
|
|
408
|
+
/**
|
|
409
|
+
* Generate HTML dashboard
|
|
410
|
+
*/
|
|
411
|
+
export function generateDashboardHTML(monitor) {
|
|
412
|
+
const summary = monitor.getSummary();
|
|
413
|
+
const samples = monitor.getSamples();
|
|
414
|
+
const lastSample = samples[samples.length - 1];
|
|
415
|
+
const formatBytes = (bytes) => {
|
|
416
|
+
if (bytes < 1024)
|
|
417
|
+
return `${bytes} B`;
|
|
418
|
+
if (bytes < 1024 * 1024)
|
|
419
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
420
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
421
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
422
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
423
|
+
};
|
|
424
|
+
const formatDuration = (ms) => {
|
|
425
|
+
if (ms < 1000)
|
|
426
|
+
return `${ms.toFixed(0)}ms`;
|
|
427
|
+
if (ms < 60000)
|
|
428
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
429
|
+
return `${(ms / 60000).toFixed(1)}m`;
|
|
430
|
+
};
|
|
431
|
+
return `
|
|
432
|
+
<!DOCTYPE html>
|
|
433
|
+
<html lang="en">
|
|
434
|
+
<head>
|
|
435
|
+
<meta charset="UTF-8">
|
|
436
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
437
|
+
<title>edgeFlow.js Performance Dashboard</title>
|
|
438
|
+
<style>
|
|
439
|
+
:root {
|
|
440
|
+
--bg-primary: #0d1117;
|
|
441
|
+
--bg-secondary: #161b22;
|
|
442
|
+
--bg-tertiary: #21262d;
|
|
443
|
+
--text-primary: #f0f6fc;
|
|
444
|
+
--text-secondary: #8b949e;
|
|
445
|
+
--accent: #58a6ff;
|
|
446
|
+
--success: #3fb950;
|
|
447
|
+
--warning: #d29922;
|
|
448
|
+
--error: #f85149;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
* {
|
|
452
|
+
margin: 0;
|
|
453
|
+
padding: 0;
|
|
454
|
+
box-sizing: border-box;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
body {
|
|
458
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
459
|
+
background: var(--bg-primary);
|
|
460
|
+
color: var(--text-primary);
|
|
461
|
+
line-height: 1.6;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.dashboard {
|
|
465
|
+
max-width: 1400px;
|
|
466
|
+
margin: 0 auto;
|
|
467
|
+
padding: 24px;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
header {
|
|
471
|
+
display: flex;
|
|
472
|
+
justify-content: space-between;
|
|
473
|
+
align-items: center;
|
|
474
|
+
margin-bottom: 32px;
|
|
475
|
+
padding-bottom: 16px;
|
|
476
|
+
border-bottom: 1px solid var(--bg-tertiary);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
h1 {
|
|
480
|
+
font-size: 24px;
|
|
481
|
+
font-weight: 600;
|
|
482
|
+
display: flex;
|
|
483
|
+
align-items: center;
|
|
484
|
+
gap: 12px;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.status {
|
|
488
|
+
display: flex;
|
|
489
|
+
align-items: center;
|
|
490
|
+
gap: 8px;
|
|
491
|
+
font-size: 14px;
|
|
492
|
+
color: var(--text-secondary);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.status-dot {
|
|
496
|
+
width: 8px;
|
|
497
|
+
height: 8px;
|
|
498
|
+
border-radius: 50%;
|
|
499
|
+
background: var(--success);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.grid {
|
|
503
|
+
display: grid;
|
|
504
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
505
|
+
gap: 20px;
|
|
506
|
+
margin-bottom: 32px;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.card {
|
|
510
|
+
background: var(--bg-secondary);
|
|
511
|
+
border: 1px solid var(--bg-tertiary);
|
|
512
|
+
border-radius: 12px;
|
|
513
|
+
padding: 20px;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.card-header {
|
|
517
|
+
display: flex;
|
|
518
|
+
justify-content: space-between;
|
|
519
|
+
align-items: center;
|
|
520
|
+
margin-bottom: 16px;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.card-title {
|
|
524
|
+
font-size: 14px;
|
|
525
|
+
font-weight: 500;
|
|
526
|
+
color: var(--text-secondary);
|
|
527
|
+
text-transform: uppercase;
|
|
528
|
+
letter-spacing: 0.5px;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.card-value {
|
|
532
|
+
font-size: 36px;
|
|
533
|
+
font-weight: 700;
|
|
534
|
+
font-variant-numeric: tabular-nums;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.card-value.small {
|
|
538
|
+
font-size: 24px;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.card-unit {
|
|
542
|
+
font-size: 14px;
|
|
543
|
+
color: var(--text-secondary);
|
|
544
|
+
margin-left: 4px;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.card-change {
|
|
548
|
+
font-size: 12px;
|
|
549
|
+
padding: 4px 8px;
|
|
550
|
+
border-radius: 4px;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.card-change.up {
|
|
554
|
+
background: rgba(63, 185, 80, 0.2);
|
|
555
|
+
color: var(--success);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.card-change.down {
|
|
559
|
+
background: rgba(248, 81, 73, 0.2);
|
|
560
|
+
color: var(--error);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.progress-bar {
|
|
564
|
+
height: 8px;
|
|
565
|
+
background: var(--bg-tertiary);
|
|
566
|
+
border-radius: 4px;
|
|
567
|
+
overflow: hidden;
|
|
568
|
+
margin-top: 12px;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.progress-fill {
|
|
572
|
+
height: 100%;
|
|
573
|
+
border-radius: 4px;
|
|
574
|
+
transition: width 0.3s ease;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.progress-fill.blue { background: var(--accent); }
|
|
578
|
+
.progress-fill.green { background: var(--success); }
|
|
579
|
+
.progress-fill.yellow { background: var(--warning); }
|
|
580
|
+
.progress-fill.red { background: var(--error); }
|
|
581
|
+
|
|
582
|
+
.chart-container {
|
|
583
|
+
background: var(--bg-secondary);
|
|
584
|
+
border: 1px solid var(--bg-tertiary);
|
|
585
|
+
border-radius: 12px;
|
|
586
|
+
padding: 20px;
|
|
587
|
+
margin-bottom: 20px;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.chart-header {
|
|
591
|
+
display: flex;
|
|
592
|
+
justify-content: space-between;
|
|
593
|
+
align-items: center;
|
|
594
|
+
margin-bottom: 16px;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.chart-title {
|
|
598
|
+
font-size: 16px;
|
|
599
|
+
font-weight: 600;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.chart {
|
|
603
|
+
height: 200px;
|
|
604
|
+
position: relative;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.chart-line {
|
|
608
|
+
stroke: var(--accent);
|
|
609
|
+
stroke-width: 2;
|
|
610
|
+
fill: none;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.chart-area {
|
|
614
|
+
fill: url(#chartGradient);
|
|
615
|
+
opacity: 0.3;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.chart-grid {
|
|
619
|
+
stroke: var(--bg-tertiary);
|
|
620
|
+
stroke-width: 1;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.table {
|
|
624
|
+
width: 100%;
|
|
625
|
+
border-collapse: collapse;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.table th,
|
|
629
|
+
.table td {
|
|
630
|
+
padding: 12px 16px;
|
|
631
|
+
text-align: left;
|
|
632
|
+
border-bottom: 1px solid var(--bg-tertiary);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.table th {
|
|
636
|
+
font-size: 12px;
|
|
637
|
+
font-weight: 500;
|
|
638
|
+
color: var(--text-secondary);
|
|
639
|
+
text-transform: uppercase;
|
|
640
|
+
letter-spacing: 0.5px;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.table td {
|
|
644
|
+
font-variant-numeric: tabular-nums;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
footer {
|
|
648
|
+
text-align: center;
|
|
649
|
+
padding: 24px;
|
|
650
|
+
color: var(--text-secondary);
|
|
651
|
+
font-size: 14px;
|
|
652
|
+
}
|
|
653
|
+
</style>
|
|
654
|
+
</head>
|
|
655
|
+
<body>
|
|
656
|
+
<div class="dashboard">
|
|
657
|
+
<header>
|
|
658
|
+
<h1>
|
|
659
|
+
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
|
|
660
|
+
<rect width="32" height="32" rx="8" fill="var(--accent)"/>
|
|
661
|
+
<path d="M8 16L14 10L20 16L26 10" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
662
|
+
<path d="M8 22L14 16L20 22L26 16" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity="0.5"/>
|
|
663
|
+
</svg>
|
|
664
|
+
edgeFlow.js Performance Dashboard
|
|
665
|
+
</h1>
|
|
666
|
+
<div class="status">
|
|
667
|
+
<div class="status-dot"></div>
|
|
668
|
+
Running for ${formatDuration(summary.uptime)}
|
|
669
|
+
</div>
|
|
670
|
+
</header>
|
|
671
|
+
|
|
672
|
+
<div class="grid">
|
|
673
|
+
<div class="card">
|
|
674
|
+
<div class="card-header">
|
|
675
|
+
<span class="card-title">Total Inferences</span>
|
|
676
|
+
</div>
|
|
677
|
+
<div class="card-value">${summary.totalInferences.toLocaleString()}</div>
|
|
678
|
+
</div>
|
|
679
|
+
|
|
680
|
+
<div class="card">
|
|
681
|
+
<div class="card-header">
|
|
682
|
+
<span class="card-title">Avg Inference Time</span>
|
|
683
|
+
</div>
|
|
684
|
+
<div class="card-value">${summary.avgInferenceTime.toFixed(1)}<span class="card-unit">ms</span></div>
|
|
685
|
+
</div>
|
|
686
|
+
|
|
687
|
+
<div class="card">
|
|
688
|
+
<div class="card-header">
|
|
689
|
+
<span class="card-title">Throughput</span>
|
|
690
|
+
</div>
|
|
691
|
+
<div class="card-value">${summary.avgThroughput.toFixed(1)}<span class="card-unit">ops/s</span></div>
|
|
692
|
+
</div>
|
|
693
|
+
|
|
694
|
+
<div class="card">
|
|
695
|
+
<div class="card-header">
|
|
696
|
+
<span class="card-title">Avg FPS</span>
|
|
697
|
+
</div>
|
|
698
|
+
<div class="card-value">${Math.round(summary.avgFPS)}</div>
|
|
699
|
+
</div>
|
|
700
|
+
</div>
|
|
701
|
+
|
|
702
|
+
<div class="grid">
|
|
703
|
+
<div class="card">
|
|
704
|
+
<div class="card-header">
|
|
705
|
+
<span class="card-title">Memory Usage</span>
|
|
706
|
+
</div>
|
|
707
|
+
<div class="card-value small">${formatBytes(lastSample?.memory.usedHeap ?? 0)}</div>
|
|
708
|
+
<div class="progress-bar">
|
|
709
|
+
<div class="progress-fill ${summary.avgMemoryUsage > 0.8 ? 'red' : summary.avgMemoryUsage > 0.6 ? 'yellow' : 'green'}"
|
|
710
|
+
style="width: ${(summary.avgMemoryUsage * 100).toFixed(0)}%"></div>
|
|
711
|
+
</div>
|
|
712
|
+
</div>
|
|
713
|
+
|
|
714
|
+
<div class="card">
|
|
715
|
+
<div class="card-header">
|
|
716
|
+
<span class="card-title">Tensor Memory</span>
|
|
717
|
+
</div>
|
|
718
|
+
<div class="card-value small">${formatBytes(lastSample?.memory.tensorMemory ?? 0)}</div>
|
|
719
|
+
</div>
|
|
720
|
+
|
|
721
|
+
<div class="card">
|
|
722
|
+
<div class="card-header">
|
|
723
|
+
<span class="card-title">Cache Memory</span>
|
|
724
|
+
</div>
|
|
725
|
+
<div class="card-value small">${formatBytes(lastSample?.memory.cacheMemory ?? 0)}</div>
|
|
726
|
+
</div>
|
|
727
|
+
|
|
728
|
+
<div class="card">
|
|
729
|
+
<div class="card-header">
|
|
730
|
+
<span class="card-title">Queue Length</span>
|
|
731
|
+
</div>
|
|
732
|
+
<div class="card-value small">${lastSample?.inference.queueLength ?? 0}</div>
|
|
733
|
+
</div>
|
|
734
|
+
</div>
|
|
735
|
+
|
|
736
|
+
<div class="chart-container">
|
|
737
|
+
<div class="chart-header">
|
|
738
|
+
<span class="chart-title">Inference Time History</span>
|
|
739
|
+
</div>
|
|
740
|
+
<div class="chart">
|
|
741
|
+
<svg width="100%" height="100%" viewBox="0 0 600 200" preserveAspectRatio="none">
|
|
742
|
+
<defs>
|
|
743
|
+
<linearGradient id="chartGradient" x1="0" y1="0" x2="0" y2="1">
|
|
744
|
+
<stop offset="0%" stop-color="var(--accent)" stop-opacity="0.5"/>
|
|
745
|
+
<stop offset="100%" stop-color="var(--accent)" stop-opacity="0"/>
|
|
746
|
+
</linearGradient>
|
|
747
|
+
</defs>
|
|
748
|
+
${generateChartPath(samples)}
|
|
749
|
+
</svg>
|
|
750
|
+
</div>
|
|
751
|
+
</div>
|
|
752
|
+
|
|
753
|
+
<div class="chart-container">
|
|
754
|
+
<div class="chart-header">
|
|
755
|
+
<span class="chart-title">Recent Samples</span>
|
|
756
|
+
</div>
|
|
757
|
+
<table class="table">
|
|
758
|
+
<thead>
|
|
759
|
+
<tr>
|
|
760
|
+
<th>Time</th>
|
|
761
|
+
<th>Inferences</th>
|
|
762
|
+
<th>Avg Time</th>
|
|
763
|
+
<th>Throughput</th>
|
|
764
|
+
<th>Memory</th>
|
|
765
|
+
<th>FPS</th>
|
|
766
|
+
</tr>
|
|
767
|
+
</thead>
|
|
768
|
+
<tbody>
|
|
769
|
+
${samples.slice(-10).reverse().map(s => `
|
|
770
|
+
<tr>
|
|
771
|
+
<td>${new Date(s.timestamp).toLocaleTimeString()}</td>
|
|
772
|
+
<td>${s.inference.count}</td>
|
|
773
|
+
<td>${s.inference.avgTime.toFixed(2)}ms</td>
|
|
774
|
+
<td>${s.inference.throughput.toFixed(1)}/s</td>
|
|
775
|
+
<td>${formatBytes(s.memory.usedHeap)}</td>
|
|
776
|
+
<td>${s.system.fps}</td>
|
|
777
|
+
</tr>
|
|
778
|
+
`).join('')}
|
|
779
|
+
</tbody>
|
|
780
|
+
</table>
|
|
781
|
+
</div>
|
|
782
|
+
|
|
783
|
+
<footer>
|
|
784
|
+
Generated at ${new Date().toLocaleString()} | edgeFlow.js Performance Monitor
|
|
785
|
+
</footer>
|
|
786
|
+
</div>
|
|
787
|
+
</body>
|
|
788
|
+
</html>
|
|
789
|
+
`.trim();
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Generate SVG chart path
|
|
793
|
+
*/
|
|
794
|
+
function generateChartPath(samples) {
|
|
795
|
+
if (samples.length < 2)
|
|
796
|
+
return '';
|
|
797
|
+
const width = 600;
|
|
798
|
+
const height = 180;
|
|
799
|
+
const padding = 10;
|
|
800
|
+
const times = samples.map(s => s.inference.avgTime);
|
|
801
|
+
const maxTime = Math.max(...times, 1);
|
|
802
|
+
const points = samples.map((s, i) => {
|
|
803
|
+
const x = padding + (i / (samples.length - 1)) * (width - 2 * padding);
|
|
804
|
+
const y = height - padding - (s.inference.avgTime / maxTime) * (height - 2 * padding);
|
|
805
|
+
return `${x},${y}`;
|
|
806
|
+
});
|
|
807
|
+
const linePath = `M ${points.join(' L ')}`;
|
|
808
|
+
const areaPath = `M ${padding},${height - padding} L ${points.join(' L ')} L ${width - padding},${height - padding} Z`;
|
|
809
|
+
// Grid lines
|
|
810
|
+
const gridLines = [];
|
|
811
|
+
for (let i = 0; i <= 4; i++) {
|
|
812
|
+
const y = padding + (i / 4) * (height - 2 * padding);
|
|
813
|
+
gridLines.push(`<line class="chart-grid" x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`);
|
|
814
|
+
}
|
|
815
|
+
return `
|
|
816
|
+
${gridLines.join('\n')}
|
|
817
|
+
<path class="chart-area" d="${areaPath}"/>
|
|
818
|
+
<path class="chart-line" d="${linePath}"/>
|
|
819
|
+
`;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Generate ASCII dashboard
|
|
823
|
+
*/
|
|
824
|
+
export function generateAsciiDashboard(monitor) {
|
|
825
|
+
const summary = monitor.getSummary();
|
|
826
|
+
const samples = monitor.getSamples();
|
|
827
|
+
const lastSample = samples[samples.length - 1];
|
|
828
|
+
const formatBytes = (bytes) => {
|
|
829
|
+
if (bytes < 1024)
|
|
830
|
+
return `${bytes} B`;
|
|
831
|
+
if (bytes < 1024 * 1024)
|
|
832
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
833
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
834
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
835
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
836
|
+
};
|
|
837
|
+
const bar = (value, max, width = 20) => {
|
|
838
|
+
const filled = Math.round((value / max) * width);
|
|
839
|
+
return '█'.repeat(filled) + '░'.repeat(width - filled);
|
|
840
|
+
};
|
|
841
|
+
const lines = [
|
|
842
|
+
'╔══════════════════════════════════════════════════════════════════════════╗',
|
|
843
|
+
'║ edgeFlow.js Performance Monitor Dashboard ║',
|
|
844
|
+
'╠══════════════════════════════════════════════════════════════════════════╣',
|
|
845
|
+
'║ ║',
|
|
846
|
+
`║ Total Inferences: ${summary.totalInferences.toString().padStart(10)} ║`,
|
|
847
|
+
`║ Avg Inference: ${summary.avgInferenceTime.toFixed(2).padStart(10)}ms ║`,
|
|
848
|
+
`║ Throughput: ${summary.avgThroughput.toFixed(2).padStart(10)} ops/s ║`,
|
|
849
|
+
`║ Avg FPS: ${Math.round(summary.avgFPS).toString().padStart(10)} ║`,
|
|
850
|
+
'║ ║',
|
|
851
|
+
'╟──────────────────────────────────────────────────────────────────────────╢',
|
|
852
|
+
'║ Memory Usage ║',
|
|
853
|
+
`║ Heap: ${bar(summary.avgMemoryUsage, 1)} ${(summary.avgMemoryUsage * 100).toFixed(0).padStart(3)}% ║`,
|
|
854
|
+
`║ Used: ${formatBytes(lastSample?.memory.usedHeap ?? 0).padStart(10)} ║`,
|
|
855
|
+
`║ Tensor: ${formatBytes(lastSample?.memory.tensorMemory ?? 0).padStart(10)} ║`,
|
|
856
|
+
`║ Cache: ${formatBytes(lastSample?.memory.cacheMemory ?? 0).padStart(10)} ║`,
|
|
857
|
+
'║ ║',
|
|
858
|
+
'╟──────────────────────────────────────────────────────────────────────────╢',
|
|
859
|
+
'║ Inference Time History (last 30 samples) ║',
|
|
860
|
+
'║ ║',
|
|
861
|
+
];
|
|
862
|
+
// Add mini chart
|
|
863
|
+
const recentSamples = samples.slice(-30);
|
|
864
|
+
if (recentSamples.length > 0) {
|
|
865
|
+
const times = recentSamples.map(s => s.inference.avgTime);
|
|
866
|
+
const maxTime = Math.max(...times, 1);
|
|
867
|
+
const chartHeight = 5;
|
|
868
|
+
for (let row = chartHeight; row > 0; row--) {
|
|
869
|
+
let line = '║ ';
|
|
870
|
+
for (const time of times) {
|
|
871
|
+
const height = Math.ceil((time / maxTime) * chartHeight);
|
|
872
|
+
line += height >= row ? '▓' : ' ';
|
|
873
|
+
}
|
|
874
|
+
lines.push(line.padEnd(76) + '║');
|
|
875
|
+
}
|
|
876
|
+
lines.push('║ ' + '─'.repeat(30) + ' ║');
|
|
877
|
+
}
|
|
878
|
+
lines.push('║ ║');
|
|
879
|
+
lines.push(`║ Last updated: ${new Date().toLocaleString().padEnd(40)} ║`);
|
|
880
|
+
lines.push('╚══════════════════════════════════════════════════════════════════════════╝');
|
|
881
|
+
return lines.join('\n');
|
|
882
|
+
}
|
|
883
|
+
// ============================================================================
|
|
884
|
+
// Global Instance
|
|
885
|
+
// ============================================================================
|
|
886
|
+
let globalMonitor = null;
|
|
887
|
+
/**
|
|
888
|
+
* Get or create global monitor
|
|
889
|
+
*/
|
|
890
|
+
export function getMonitor(config) {
|
|
891
|
+
if (!globalMonitor || config) {
|
|
892
|
+
globalMonitor = new PerformanceMonitor(config);
|
|
893
|
+
}
|
|
894
|
+
return globalMonitor;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Start monitoring
|
|
898
|
+
*/
|
|
899
|
+
export function startMonitoring(config) {
|
|
900
|
+
const monitor = getMonitor(config);
|
|
901
|
+
monitor.start();
|
|
902
|
+
return monitor;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Stop monitoring
|
|
906
|
+
*/
|
|
907
|
+
export function stopMonitoring() {
|
|
908
|
+
globalMonitor?.stop();
|
|
909
|
+
}
|
|
910
|
+
// ============================================================================
|
|
911
|
+
// Exports
|
|
912
|
+
// ============================================================================
|
|
913
|
+
export default {
|
|
914
|
+
PerformanceMonitor,
|
|
915
|
+
getMonitor,
|
|
916
|
+
startMonitoring,
|
|
917
|
+
stopMonitoring,
|
|
918
|
+
generateDashboardHTML,
|
|
919
|
+
generateAsciiDashboard,
|
|
920
|
+
};
|
|
921
|
+
//# sourceMappingURL=monitor.js.map
|