chore: lint
This commit is contained in:
@@ -104,250 +104,250 @@ export class EnhancedPerformanceMonitor extends FrontendPerformanceMonitor {
|
|||||||
private memoryCheckInterval: NodeJS.Timeout | null = null;
|
private memoryCheckInterval: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
constructor(options?: {
|
constructor(options?: {
|
||||||
memoryWarningThreshold?: number,
|
memoryWarningThreshold?: number,
|
||||||
maxMetricsHistory?: number,
|
maxMetricsHistory?: number,
|
||||||
memoryAlertCallback?: (usage: MemoryInfo) => void
|
memoryAlertCallback?: (usage: MemoryInfo) => void
|
||||||
}) {
|
}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
if (options.memoryWarningThreshold) {
|
if (options.memoryWarningThreshold) {
|
||||||
this.memoryWarningThreshold = options.memoryWarningThreshold;
|
this.memoryWarningThreshold = options.memoryWarningThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.maxMetricsHistory) {
|
||||||
|
this.maxMetricsHistory = options.maxMetricsHistory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.memoryAlertCallback) {
|
||||||
|
this.memoryAlertCallback = options.memoryAlertCallback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.maxMetricsHistory) {
|
// Override the parent's monitoring with our enhanced version
|
||||||
this.maxMetricsHistory = options.maxMetricsHistory;
|
this.startEnhancedMonitoring();
|
||||||
}
|
|
||||||
|
|
||||||
if (options.memoryAlertCallback) {
|
|
||||||
this.memoryAlertCallback = options.memoryAlertCallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override the parent's monitoring with our enhanced version
|
|
||||||
this.startEnhancedMonitoring();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private startEnhancedMonitoring(): void {
|
private startEnhancedMonitoring(): void {
|
||||||
// Stop any existing monitoring from parent class
|
// Stop any existing monitoring from parent class
|
||||||
if (this.rafHandle) {
|
if (this.rafHandle) {
|
||||||
cancelAnimationFrame(this.rafHandle);
|
cancelAnimationFrame(this.rafHandle);
|
||||||
}
|
|
||||||
|
|
||||||
if (this.memoryCheckInterval) {
|
|
||||||
clearInterval(this.memoryCheckInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced FPS monitoring with frame time tracking
|
|
||||||
let lastFrameTime = performance.now();
|
|
||||||
let frameCount = 0;
|
|
||||||
let frameTimes: number[] = [];
|
|
||||||
|
|
||||||
const measureFPS = () => {
|
|
||||||
const now = performance.now();
|
|
||||||
const frameTime = now - lastFrameTime;
|
|
||||||
lastFrameTime = now;
|
|
||||||
|
|
||||||
// Track individual frame times for jank detection
|
|
||||||
frameTimes.push(frameTime);
|
|
||||||
if (frameTimes.length > 60) { // Keep only last 60 frame times
|
|
||||||
frameTimes.shift();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frameCount++;
|
if (this.memoryCheckInterval) {
|
||||||
this.frameTimeHistory.push(frameTime);
|
clearInterval(this.memoryCheckInterval);
|
||||||
|
|
||||||
// Calculate FPS every second
|
|
||||||
if (this.frameTimeHistory.length >= 60) {
|
|
||||||
const totalTime = this.frameTimeHistory.reduce((sum, time) => sum + time, 0);
|
|
||||||
const fps = Math.round((this.frameTimeHistory.length * 1000) / totalTime);
|
|
||||||
|
|
||||||
// Get metrics from parent class
|
|
||||||
const metrics = this.getMetrics();
|
|
||||||
metrics.fps.push(fps);
|
|
||||||
|
|
||||||
// Limit metrics history
|
|
||||||
if (metrics.fps.length > this.maxMetricsHistory) {
|
|
||||||
metrics.fps.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect jank (long frames)
|
|
||||||
const jankThreshold = 16.7 * 2; // 2x normal frame time at 60fps
|
|
||||||
const jankFrames = frameTimes.filter(time => time > jankThreshold);
|
|
||||||
|
|
||||||
if (jankFrames.length > 10) { // If more than 10 out of 60 frames are janky
|
|
||||||
this.detectPerformanceIssue('jank', {
|
|
||||||
jankFrames: jankFrames.length,
|
|
||||||
averageJankTime: jankFrames.reduce((sum, time) => sum + time, 0) / jankFrames.length
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset for next measurement
|
|
||||||
this.frameTimeHistory = [];
|
|
||||||
frameTimes = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enhanced FPS monitoring with frame time tracking
|
||||||
|
let lastFrameTime = performance.now();
|
||||||
|
let frameCount = 0;
|
||||||
|
let frameTimes: number[] = [];
|
||||||
|
|
||||||
|
const measureFPS = () => {
|
||||||
|
const now = performance.now();
|
||||||
|
const frameTime = now - lastFrameTime;
|
||||||
|
lastFrameTime = now;
|
||||||
|
|
||||||
|
// Track individual frame times for jank detection
|
||||||
|
frameTimes.push(frameTime);
|
||||||
|
if (frameTimes.length > 60) { // Keep only last 60 frame times
|
||||||
|
frameTimes.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
frameCount++;
|
||||||
|
this.frameTimeHistory.push(frameTime);
|
||||||
|
|
||||||
|
// Calculate FPS every second
|
||||||
|
if (this.frameTimeHistory.length >= 60) {
|
||||||
|
const totalTime = this.frameTimeHistory.reduce((sum, time) => sum + time, 0);
|
||||||
|
const fps = Math.round((this.frameTimeHistory.length * 1000) / totalTime);
|
||||||
|
|
||||||
|
// Get metrics from parent class
|
||||||
|
const metrics = this.getMetrics();
|
||||||
|
metrics.fps.push(fps);
|
||||||
|
|
||||||
|
// Limit metrics history
|
||||||
|
if (metrics.fps.length > this.maxMetricsHistory) {
|
||||||
|
metrics.fps.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect jank (long frames)
|
||||||
|
const jankThreshold = 16.7 * 2; // 2x normal frame time at 60fps
|
||||||
|
const jankFrames = frameTimes.filter(time => time > jankThreshold);
|
||||||
|
|
||||||
|
if (jankFrames.length > 10) { // If more than 10 out of 60 frames are janky
|
||||||
|
this.detectPerformanceIssue('jank', {
|
||||||
|
jankFrames: jankFrames.length,
|
||||||
|
averageJankTime: jankFrames.reduce((sum, time) => sum + time, 0) / jankFrames.length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset for next measurement
|
||||||
|
this.frameTimeHistory = [];
|
||||||
|
frameTimes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rafHandle = requestAnimationFrame(measureFPS);
|
||||||
|
};
|
||||||
|
|
||||||
this.rafHandle = requestAnimationFrame(measureFPS);
|
this.rafHandle = requestAnimationFrame(measureFPS);
|
||||||
};
|
|
||||||
|
|
||||||
this.rafHandle = requestAnimationFrame(measureFPS);
|
// Enhanced memory monitoring
|
||||||
|
if (window.performance && (performance as any).memory) {
|
||||||
|
this.memoryCheckInterval = setInterval(() => {
|
||||||
|
const memory = (performance as any).memory;
|
||||||
|
const memoryInfo = {
|
||||||
|
usedJSHeapSize: memory.usedJSHeapSize,
|
||||||
|
totalJSHeapSize: memory.totalJSHeapSize,
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
|
||||||
// Enhanced memory monitoring
|
// Get metrics from parent class
|
||||||
if (window.performance && (performance as any).memory) {
|
const metrics = this.getMetrics();
|
||||||
this.memoryCheckInterval = setInterval(() => {
|
metrics.memoryUsage.push(memoryInfo);
|
||||||
const memory = (performance as any).memory;
|
|
||||||
const memoryInfo = {
|
|
||||||
usedJSHeapSize: memory.usedJSHeapSize,
|
|
||||||
totalJSHeapSize: memory.totalJSHeapSize,
|
|
||||||
timestamp: Date.now()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get metrics from parent class
|
// Limit metrics history
|
||||||
const metrics = this.getMetrics();
|
if (metrics.memoryUsage.length > this.maxMetricsHistory) {
|
||||||
metrics.memoryUsage.push(memoryInfo);
|
metrics.memoryUsage.shift();
|
||||||
|
}
|
||||||
|
|
||||||
// Limit metrics history
|
// Check for memory warnings
|
||||||
if (metrics.memoryUsage.length > this.maxMetricsHistory) {
|
if (memoryInfo.usedJSHeapSize > this.memoryWarningThreshold) {
|
||||||
metrics.memoryUsage.shift();
|
this.detectPerformanceIssue('memory', memoryInfo);
|
||||||
}
|
|
||||||
|
|
||||||
// Check for memory warnings
|
if (this.memoryAlertCallback) {
|
||||||
if (memoryInfo.usedJSHeapSize > this.memoryWarningThreshold) {
|
this.memoryAlertCallback(memoryInfo);
|
||||||
this.detectPerformanceIssue('memory', memoryInfo);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.memoryAlertCallback) {
|
// Check for memory leaks (steady increase)
|
||||||
this.memoryAlertCallback(memoryInfo);
|
if (metrics.memoryUsage.length >= 10) {
|
||||||
}
|
const recentMemory = metrics.memoryUsage.slice(-10);
|
||||||
}
|
let increasingCount = 0;
|
||||||
|
|
||||||
// Check for memory leaks (steady increase)
|
for (let i = 1; i < recentMemory.length; i++) {
|
||||||
if (metrics.memoryUsage.length >= 10) {
|
if (recentMemory[i].usedJSHeapSize > recentMemory[i - 1].usedJSHeapSize) {
|
||||||
const recentMemory = metrics.memoryUsage.slice(-10);
|
increasingCount++;
|
||||||
let increasingCount = 0;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 1; i < recentMemory.length; i++) {
|
// If memory increased in 8 out of 9 consecutive readings
|
||||||
if (recentMemory[i].usedJSHeapSize > recentMemory[i-1].usedJSHeapSize) {
|
if (increasingCount >= 8) {
|
||||||
increasingCount++;
|
this.detectPerformanceIssue('memoryLeak', {
|
||||||
}
|
startMemory: recentMemory[0].usedJSHeapSize,
|
||||||
}
|
currentMemory: recentMemory[recentMemory.length - 1].usedJSHeapSize,
|
||||||
|
increaseRate: (recentMemory[recentMemory.length - 1].usedJSHeapSize - recentMemory[0].usedJSHeapSize) /
|
||||||
// If memory increased in 8 out of 9 consecutive readings
|
(recentMemory[recentMemory.length - 1].timestamp - recentMemory[0].timestamp) * 1000 // bytes per second
|
||||||
if (increasingCount >= 8) {
|
});
|
||||||
this.detectPerformanceIssue('memoryLeak', {
|
}
|
||||||
startMemory: recentMemory[0].usedJSHeapSize,
|
}
|
||||||
currentMemory: recentMemory[recentMemory.length - 1].usedJSHeapSize,
|
}, 1000);
|
||||||
increaseRate: (recentMemory[recentMemory.length - 1].usedJSHeapSize - recentMemory[0].usedJSHeapSize) /
|
}
|
||||||
(recentMemory[recentMemory.length - 1].timestamp - recentMemory[0].timestamp) * 1000 // bytes per second
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to detect various performance issues
|
// Method to detect various performance issues
|
||||||
private detectPerformanceIssue(type: 'jank' | 'memory' | 'memoryLeak', data: any): void {
|
private detectPerformanceIssue(type: 'jank' | 'memory' | 'memoryLeak', data: any): void {
|
||||||
console.warn(`Performance issue detected: ${type}`, data);
|
console.warn(`Performance issue detected: ${type}`, data);
|
||||||
|
|
||||||
if (type === 'memory' || type === 'memoryLeak') {
|
if (type === 'memory' || type === 'memoryLeak') {
|
||||||
// Auto-throttle rendering if memory issues detected
|
// Auto-throttle rendering if memory issues detected
|
||||||
if (!this.isThrottled) {
|
if (!this.isThrottled) {
|
||||||
this.throttleRendering();
|
this.throttleRendering();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suggest garbage collection
|
||||||
|
this.suggestGarbageCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suggest garbage collection
|
|
||||||
this.suggestGarbageCollection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get access to the metrics from parent class
|
// Get access to the metrics from parent class
|
||||||
private getMetrics(): any {
|
private getMetrics(): any {
|
||||||
return (this as any).metrics;
|
return (this as any).metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throttle rendering to reduce memory pressure
|
// Throttle rendering to reduce memory pressure
|
||||||
private throttleRendering(): void {
|
private throttleRendering(): void {
|
||||||
this.isThrottled = true;
|
this.isThrottled = true;
|
||||||
console.info('Throttling rendering due to memory pressure');
|
console.info('Throttling rendering due to memory pressure');
|
||||||
// Application code would implement throttling behavior
|
// Application code would implement throttling behavior
|
||||||
}
|
}
|
||||||
|
|
||||||
// Un-throttle rendering when memory pressure is reduced
|
// Un-throttle rendering when memory pressure is reduced
|
||||||
public unthrottleRendering(): void {
|
public unthrottleRendering(): void {
|
||||||
if (this.isThrottled) {
|
if (this.isThrottled) {
|
||||||
this.isThrottled = false;
|
this.isThrottled = false;
|
||||||
console.info('Resuming normal rendering');
|
console.info('Resuming normal rendering');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suggest garbage collection to the browser
|
// Suggest garbage collection to the browser
|
||||||
private suggestGarbageCollection(): void {
|
private suggestGarbageCollection(): void {
|
||||||
if (window.gc) {
|
if (window.gc) {
|
||||||
try {
|
try {
|
||||||
window.gc();
|
window.gc();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// gc() might not be available without special flags
|
// gc() might not be available without special flags
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Alternative approach to encourage garbage collection
|
// Alternative approach to encourage garbage collection
|
||||||
const largeArray = new Array(1000000).fill(0);
|
const largeArray = new Array(1000000).fill(0);
|
||||||
largeArray.length = 0;
|
largeArray.length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enhanced performance report with more detailed metrics
|
// Enhanced performance report with more detailed metrics
|
||||||
public getEnhancedPerformanceReport(): EnhancedPerformanceReport {
|
public getEnhancedPerformanceReport(): EnhancedPerformanceReport {
|
||||||
const baseReport = super.getPerformanceReport();
|
const baseReport = super.getPerformanceReport();
|
||||||
const metrics = this.getMetrics();
|
const metrics = this.getMetrics();
|
||||||
|
|
||||||
// Calculate 95th percentile render time
|
// Calculate 95th percentile render time
|
||||||
const sortedRenderTimes = [...metrics.renderTime].sort((a, b) => a - b);
|
const sortedRenderTimes = [...metrics.renderTime].sort((a, b) => a - b);
|
||||||
const idx95 = Math.floor(sortedRenderTimes.length * 0.95);
|
const idx95 = Math.floor(sortedRenderTimes.length * 0.95);
|
||||||
const renderTime95Percentile = sortedRenderTimes[idx95] || 0;
|
const renderTime95Percentile = sortedRenderTimes[idx95] || 0;
|
||||||
|
|
||||||
// Calculate memory growth rate
|
// Calculate memory growth rate
|
||||||
let memoryGrowthRate = 0;
|
let memoryGrowthRate = 0;
|
||||||
if (metrics.memoryUsage.length >= 2) {
|
if (metrics.memoryUsage.length >= 2) {
|
||||||
const first = metrics.memoryUsage[0];
|
const first = metrics.memoryUsage[0];
|
||||||
const last = metrics.memoryUsage[metrics.memoryUsage.length - 1];
|
const last = metrics.memoryUsage[metrics.memoryUsage.length - 1];
|
||||||
const timeDiffInSeconds = (last.timestamp - first.timestamp) / 1000;
|
const timeDiffInSeconds = (last.timestamp - first.timestamp) / 1000;
|
||||||
memoryGrowthRate = timeDiffInSeconds > 0
|
memoryGrowthRate = timeDiffInSeconds > 0
|
||||||
? (last.usedJSHeapSize - first.usedJSHeapSize) / timeDiffInSeconds
|
? (last.usedJSHeapSize - first.usedJSHeapSize) / timeDiffInSeconds
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...baseReport,
|
...baseReport,
|
||||||
renderTime95Percentile,
|
renderTime95Percentile,
|
||||||
memoryGrowthRate,
|
memoryGrowthRate,
|
||||||
isThrottled: this.isThrottled,
|
isThrottled: this.isThrottled,
|
||||||
heapUsagePercentage: baseReport.lastMemoryUsage
|
heapUsagePercentage: baseReport.lastMemoryUsage
|
||||||
? (baseReport.lastMemoryUsage.usedJSHeapSize / baseReport.lastMemoryUsage.totalJSHeapSize) * 100
|
? (baseReport.lastMemoryUsage.usedJSHeapSize / baseReport.lastMemoryUsage.totalJSHeapSize) * 100
|
||||||
: 0
|
: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up resources when no longer needed
|
// Clean up resources when no longer needed
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
if (this.rafHandle) {
|
if (this.rafHandle) {
|
||||||
cancelAnimationFrame(this.rafHandle);
|
cancelAnimationFrame(this.rafHandle);
|
||||||
this.rafHandle = null;
|
this.rafHandle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.memoryCheckInterval) {
|
if (this.memoryCheckInterval) {
|
||||||
clearInterval(this.memoryCheckInterval);
|
clearInterval(this.memoryCheckInterval);
|
||||||
this.memoryCheckInterval = null;
|
this.memoryCheckInterval = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extended types
|
// Extended types
|
||||||
interface EnhancedPerformanceReport extends PerformanceReport {
|
interface EnhancedPerformanceReport extends PerformanceReport {
|
||||||
renderTime95Percentile: number;
|
renderTime95Percentile: number;
|
||||||
memoryGrowthRate: number; // bytes per second
|
memoryGrowthRate: number; // bytes per second
|
||||||
isThrottled: boolean;
|
isThrottled: boolean;
|
||||||
heapUsagePercentage: number;
|
heapUsagePercentage: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backend Performance Monitoring
|
// Backend Performance Monitoring
|
||||||
export class BackendPerformanceMonitor {
|
export class BackendPerformanceMonitor {
|
||||||
|
|||||||
Reference in New Issue
Block a user