Hone logo
Hone
Problems

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:

  1. 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.
  2. AppComponent Integration:

    • Create or modify an existing AppComponent.
    • Inject the ProfilerService into AppComponent.
    • Create at least two distinct functions within AppComponent that perform some (simulated) work, e.g., a loop, data manipulation.
    • Use the ProfilerService to 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).

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 ProfilerService should only use built-in browser APIs like performance.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 ProfilerService state directly.

Notes

  • Consider how you will pass the function name to the profiler for clearer console output. The Function.prototype.name property can be useful.
  • Think about how to make the ProfilerService a 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.
Loading editor...
typescript