or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

assertions.mdasync-operations.mdbuffer-operations.mddata-structures.mdencoding-hashing.mdevent-handling.mdindex.mdperformance-monitoring.mdutility-functions.md
tile.json

performance-monitoring.mddocs/

Performance & Monitoring

Performance measurement and tracing utilities for monitoring application performance with cross-platform support and detailed timing information.

Capabilities

Isomorphic Performance

Cross-platform performance measurement with unified API across Node.js and Browser environments.

/**
 * Isomorphic performance interface with optional browser Performance properties
 * Provides core timing methods that work consistently across environments
 */
interface IsomorphicPerformance {
  /** Returns high-resolution timestamp in milliseconds */
  now(): number;
  
  /** Creates a named performance mark at the current time */
  mark(name: string): void;
  
  /** 
   * Creates a performance measure between two marks or from a mark to now
   * @param name - Name for the measure
   * @param startMark - Optional start mark name (uses navigation start if omitted)
   * @param endMark - Optional end mark name (uses current time if omitted)
   */
  measure(name: string, startMark?: string, endMark?: string): void;
  
  /** 
   * Clears performance marks
   * @param name - Optional mark name (clears all marks if omitted)
   */
  clearMarks(name?: string): void;
  
  /** Additional browser-specific properties (when available) */
  clearMeasures?(name?: string): void;
  getEntries?(): PerformanceEntryList;
  getEntriesByName?(name: string, type?: string): PerformanceEntryList;
  getEntriesByType?(type: string): PerformanceEntryList;
}

/**
 * Reference to globalThis.performance with isomorphic typing
 * Works in both Node.js (with perf_hooks) and Browser environments
 */
declare const performance: IsomorphicPerformance;

Usage Examples:

import { performance } from "@fluidframework/common-utils";

// Basic timing
const start = performance.now();
await someAsyncOperation();
const end = performance.now();
console.log(`Operation took ${end - start} milliseconds`);

// Performance marks and measures
performance.mark("operation-start");
await performHeavyComputation();
performance.mark("operation-end");

// Create measure between marks
performance.measure("heavy-computation", "operation-start", "operation-end");

// Clear marks when done
performance.clearMarks();

// Benchmarking utility
class Benchmark {
  private marks = new Map<string, number>();
  
  start(name: string): void {
    this.marks.set(name, performance.now());
    performance.mark(`${name}-start`);
  }
  
  end(name: string): number {
    const startTime = this.marks.get(name);
    if (!startTime) {
      throw new Error(`No start mark found for: ${name}`);
    }
    
    const endTime = performance.now();
    const duration = endTime - startTime;
    
    performance.mark(`${name}-end`);
    performance.measure(name, `${name}-start`, `${name}-end`);
    
    this.marks.delete(name);
    return duration;
  }
  
  time<T>(name: string, fn: () => T): T;
  time<T>(name: string, fn: () => Promise<T>): Promise<T>;
  time<T>(name: string, fn: () => T | Promise<T>): T | Promise<T> {
    this.start(name);
    
    try {
      const result = fn();
      
      if (result instanceof Promise) {
        return result.finally(() => {
          const duration = this.end(name);
          console.log(`${name}: ${duration.toFixed(2)}ms`);
        });
      } else {
        const duration = this.end(name);
        console.log(`${name}: ${duration.toFixed(2)}ms`);
        return result;
      }
    } catch (error) {
      this.end(name); // Ensure cleanup
      throw error;
    }
  }
}

// Usage
const benchmark = new Benchmark();

const result = await benchmark.time("database-query", async () => {
  return await fetchDataFromDatabase();
});

const computed = benchmark.time("computation", () => {
  return heavyMathOperation();
});

Trace Class

Performance tracing helper with detailed timing information and tick counting.

/**
 * Trace event containing detailed timing information
 */
interface ITraceEvent {
  /** Total time elapsed since trace creation in milliseconds */
  totalTimeElapsed: number;
  
  /** Duration since last trace() call in milliseconds */
  duration: number;
  
  /** Number of times trace() has been called */
  tick: number;
}

/**
 * Performance tracing helper for detailed operation monitoring
 * Tracks cumulative time and provides incremental timing information
 */
class Trace {
  /**
   * Creates and starts a new trace
   * @returns New Trace instance ready for timing
   */
  static start(): Trace;
  
  /**
   * Records a trace event and returns timing information
   * @returns Trace event with current timing data
   */
  trace(): ITraceEvent;
}

Usage Examples:

import { Trace } from "@fluidframework/common-utils";

// Basic tracing
const trace = Trace.start();

// Perform operations and trace progress
await step1();
const event1 = trace.trace();
console.log(`Step 1: ${event1.duration}ms (total: ${event1.totalTimeElapsed}ms, tick: ${event1.tick})`);

await step2();
const event2 = trace.trace();
console.log(`Step 2: ${event2.duration}ms (total: ${event2.totalTimeElapsed}ms, tick: ${event2.tick})`);

await step3();
const event3 = trace.trace();
console.log(`Step 3: ${event3.duration}ms (total: ${event3.totalTimeElapsed}ms, tick: ${event3.tick})`);

// Pipeline performance monitoring
class PipelineTracer {
  private trace: Trace;
  private stepName: string = "";
  
  start(name: string): this {
    this.trace = Trace.start();
    this.stepName = name;
    console.log(`Starting pipeline: ${name}`);
    return this;
  }
  
  step(stepName: string): this {
    if (!this.trace) {
      throw new Error("Trace not started");
    }
    
    const event = this.trace.trace();
    console.log(`${this.stepName} - ${stepName}: ${event.duration.toFixed(2)}ms (cumulative: ${event.totalTimeElapsed.toFixed(2)}ms)`);
    return this;
  }
  
  finish(): ITraceEvent {
    if (!this.trace) {
      throw new Error("Trace not started");
    }
    
    const finalEvent = this.trace.trace();
    console.log(`${this.stepName} completed: ${finalEvent.totalTimeElapsed.toFixed(2)}ms total, ${finalEvent.tick} steps`);
    return finalEvent;
  }
}

// Usage
const tracer = new PipelineTracer();

await tracer
  .start("Data Processing Pipeline")
  .step("Load data");

await processData();
tracer.step("Process data");

await validateResults();
tracer.step("Validate results");

await saveResults();
const finalEvent = tracer.finish();

// Trace with conditional logging
class ConditionalTracer {
  private trace: Trace | null = null;
  private enabled: boolean;
  
  constructor(enabled: boolean = false) {
    this.enabled = enabled;
  }
  
  start(): void {
    if (this.enabled) {
      this.trace = Trace.start();
    }
  }
  
  checkpoint(name: string): ITraceEvent | null {
    if (!this.enabled || !this.trace) {
      return null;
    }
    
    const event = this.trace.trace();
    console.log(`Checkpoint ${name}: ${event.duration.toFixed(2)}ms`);
    return event;
  }
  
  enable(): void {
    this.enabled = true;
  }
  
  disable(): void {
    this.enabled = false;
    this.trace = null;
  }
}

// Debug mode tracing
const DEBUG = process.env.NODE_ENV === "development";
const debugTracer = new ConditionalTracer(DEBUG);

debugTracer.start();
await operation1();
debugTracer.checkpoint("Operation 1");

await operation2();
debugTracer.checkpoint("Operation 2");

Advanced Monitoring Patterns

Performance Profiler

import { Trace, performance } from "@fluidframework/common-utils";

class PerformanceProfiler {
  private profiles = new Map<string, {
    count: number;
    totalTime: number;
    minTime: number;
    maxTime: number;
    avgTime: number;
  }>();
  
  profile<T>(name: string, fn: () => T): T;
  profile<T>(name: string, fn: () => Promise<T>): Promise<T>;
  profile<T>(name: string, fn: () => T | Promise<T>): T | Promise<T> {
    const start = performance.now();
    
    const updateStats = (duration: number) => {
      const existing = this.profiles.get(name) || {
        count: 0,
        totalTime: 0,
        minTime: Infinity,
        maxTime: 0,
        avgTime: 0
      };
      
      existing.count++;
      existing.totalTime += duration;
      existing.minTime = Math.min(existing.minTime, duration);
      existing.maxTime = Math.max(existing.maxTime, duration);
      existing.avgTime = existing.totalTime / existing.count;
      
      this.profiles.set(name, existing);
    };
    
    try {
      const result = fn();
      
      if (result instanceof Promise) {
        return result.finally(() => {
          const duration = performance.now() - start;
          updateStats(duration);
        });
      } else {
        const duration = performance.now() - start;
        updateStats(duration);
        return result;
      }
    } catch (error) {
      const duration = performance.now() - start;
      updateStats(duration);
      throw error;
    }
  }
  
  getStats(name?: string) {
    if (name) {
      return this.profiles.get(name);
    }
    return Object.fromEntries(this.profiles);
  }
  
  reset(name?: string): void {
    if (name) {
      this.profiles.delete(name);
    } else {
      this.profiles.clear();
    }
  }
  
  report(): void {
    console.log("\n=== Performance Report ===");
    for (const [name, stats] of this.profiles) {
      console.log(`${name}:`);
      console.log(`  Calls: ${stats.count}`);
      console.log(`  Total: ${stats.totalTime.toFixed(2)}ms`);
      console.log(`  Avg: ${stats.avgTime.toFixed(2)}ms`);
      console.log(`  Min: ${stats.minTime.toFixed(2)}ms`);
      console.log(`  Max: ${stats.maxTime.toFixed(2)}ms`);
      console.log();
    }
  }
}

// Usage
const profiler = new PerformanceProfiler();

// Profile individual operations
for (let i = 0; i < 100; i++) {
  await profiler.profile("database-read", () => readFromDatabase());
  await profiler.profile("cache-write", () => writeToCache());
}

// Get performance statistics
profiler.report();

Real-time Performance Monitor

import { Trace, performance } from "@fluidframework/common-utils";

class RealTimeMonitor {
  private activeTraces = new Map<string, Trace>();
  private thresholds = new Map<string, number>();
  private onSlowOperation?: (name: string, duration: number) => void;
  
  setThreshold(operation: string, thresholdMs: number): void {
    this.thresholds.set(operation, thresholdMs);
  }
  
  onSlow(callback: (name: string, duration: number) => void): void {
    this.onSlowOperation = callback;
  }
  
  startMonitoring(name: string): void {
    this.activeTraces.set(name, Trace.start());
  }
  
  stopMonitoring(name: string): number | null {
    const trace = this.activeTraces.get(name);
    if (!trace) return null;
    
    const event = trace.trace();
    this.activeTraces.delete(name);
    
    const threshold = this.thresholds.get(name);
    if (threshold && event.totalTimeElapsed > threshold) {
      this.onSlowOperation?.(name, event.totalTimeElapsed);
    }
    
    return event.totalTimeElapsed;
  }
  
  monitor<T>(name: string, fn: () => T): T;
  monitor<T>(name: string, fn: () => Promise<T>): Promise<T>;
  monitor<T>(name: string, fn: () => T | Promise<T>): T | Promise<T> {
    this.startMonitoring(name);
    
    try {
      const result = fn();
      
      if (result instanceof Promise) {
        return result.finally(() => {
          this.stopMonitoring(name);
        });
      } else {
        this.stopMonitoring(name);
        return result;
      }
    } catch (error) {
      this.stopMonitoring(name);
      throw error;
    }
  }
}

// Usage
const monitor = new RealTimeMonitor();

// Set performance thresholds
monitor.setThreshold("api-call", 1000); // 1 second
monitor.setThreshold("db-query", 500);  // 500ms

// Handle slow operations
monitor.onSlow((name, duration) => {
  console.warn(`Slow operation detected: ${name} took ${duration.toFixed(2)}ms`);
});

// Monitor operations
await monitor.monitor("api-call", () => fetchFromAPI());
await monitor.monitor("db-query", () => queryDatabase());

These performance monitoring utilities provide comprehensive tools for measuring, tracking, and optimizing application performance in Fluid Framework applications.