Angular Performance Profiler
In modern web development, understanding and optimizing application performance is crucial for a great user experience. This challenge requires you to build a basic profiling tool within an Angular application to measure the execution time of specific functions. This will help developers identify performance bottlenecks and make informed decisions about code optimization.
Problem Description
Your task is to create a reusable Angular service that can be used to profile the execution time of any given function. This service should allow you to start and stop a timer and report the duration. You will then integrate this service into a sample Angular component to demonstrate its usage.
Key Requirements:
-
ProfilerService:- Create a service named
ProfilerService. - This service should expose a method, let's call it
profileFunction, which takes a function as an argument. - Inside
profileFunction, the service should:- Record the start time.
- Execute the provided function.
- Record the end time.
- Calculate the duration (end time - start time).
- Return the result of the executed function.
- Log the function name and its execution duration to the console.
- The service should handle functions with and without arguments.
- The service should also handle functions that return a value.
- Create a service named
-
AppComponentIntegration:- Create or modify an existing
AppComponent. - Inject the
ProfilerServiceintoAppComponent. - Create at least two distinct functions within
AppComponentthat perform some (simulated) work, e.g., a loop, data manipulation. - Use the
ProfilerServiceto profile the execution of these functions. - Display a message in the component's template indicating that profiling has been performed for each function, and potentially its result (if applicable).
- Create or modify an existing
Expected Behavior:
When the component is initialized or a specific action is triggered (e.g., a button click), the profiled functions should execute. The console should display output similar to:
Profiling 'mySlowFunction'...
'mySlowFunction' executed in X.XX ms.
Profiling 'anotherOperation'...
'anotherOperation' executed in Y.YY ms.
The component's UI should update to reflect the successful profiling.
Edge Cases to Consider:
- Functions that return
void. - Functions that throw errors (your profiler should ideally not break, but for this challenge, focus on successful execution).
- Asynchronous functions (for a more advanced version, but for this core challenge, assume synchronous functions).
Examples
Example 1:
// In ProfilerService
profileFunction(func: Function, funcName: string = func.name): any {
const startTime = performance.now();
const result = func();
const endTime = performance.now();
const duration = endTime - startTime;
console.log(`Profiling '${funcName}'...`);
console.log(`'${funcName}' executed in ${duration.toFixed(2)} ms.`);
return result;
}
// In AppComponent
import { Component } from '@angular/core';
import { ProfilerService } from './profiler.service'; // Assuming service path
@Component({
selector: 'app-root',
template: `
<h1>Angular Profiling Demo</h1>
<p>Check the console for profiling results.</p>
<p>{{ message }}</p>
`,
})
export class AppComponent {
message = '';
constructor(private profilerService: ProfilerService) {
this.performProfiledOperations();
}
private performProfiledOperations() {
const sum = this.profilerService.profileFunction(this.calculateSum, 'calculateSum');
this.message = `Sum calculated: ${sum}. Profiling complete.`;
}
private calculateSum(): number {
let total = 0;
for (let i = 0; i < 1000000; i++) {
total += i;
}
return total;
}
}
Input: The Angular application bootstraps, and AppComponent is created.
Output (Console):
Profiling 'calculateSum'...
'calculateSum' executed in XXXX.XX ms.
Output (UI):
Angular Profiling Demo
Check the console for profiling results.
Sum calculated: 499999500000. Profiling complete.
Explanation: The ProfilerService is injected into AppComponent. When performProfiledOperations is called, calculateSum is executed via the profilerService.profileFunction method. The service times the execution, logs to the console, and returns the sum, which is then displayed in the UI.
Example 2:
Consider a function that returns void and performs a side effect.
// In AppComponent
import { Component } from '@angular/core';
import { ProfilerService } from './profiler.service';
@Component({
selector: 'app-root',
template: `
<h1>Angular Profiling Demo</h1>
<p>Check the console for profiling results.</p>
<button (click)="triggerComplexTask()">Run Complex Task</button>
<p>{{ uiMessage }}</p>
`,
})
export class AppComponent {
uiMessage = '';
constructor(private profilerService: ProfilerService) {}
triggerComplexTask() {
this.profilerService.profileFunction(this.performComplexTask, 'performComplexTask');
this.uiMessage = 'Complex task profiled.';
}
private performComplexTask(): void {
// Simulate some work that doesn't return a value
for (let i = 0; i < 500000; i++) {
// Do something simple
Math.sqrt(i);
}
console.log('Complex task finished its internal work.');
}
}
Input: The user clicks the "Run Complex Task" button. Output (Console):
Profiling 'performComplexTask'...
'performComplexTask' executed in YYYY.YY ms.
Complex task finished its internal work.
Output (UI):
Angular Profiling Demo
Check the console for profiling results.
Run Complex Task
Complex task profiled.
Explanation: Clicking the button triggers triggerComplexTask. This method then calls profilerService.profileFunction with performComplexTask. The service measures its duration, logs it, and the component's UI updates.
Constraints
- The
ProfilerServiceshould only use built-in browser APIs likeperformance.now(). No external libraries for profiling are allowed. - The profiling should be synchronous; do not implement asynchronous profiling for this challenge.
- Functions being profiled should not modify the
ProfilerServicestate directly.
Notes
- Consider how you will pass the function name to the profiler for clearer console output. The
Function.prototype.nameproperty can be useful. - Think about how to make the
ProfilerServicea singleton to ensure it's available application-wide. - The goal is to create a basic profiling tool. Advanced features like capturing memory usage or detailed call stacks are out of scope for this challenge.
- Ensure your solution follows standard Angular best practices for service creation and component integration.