Performance measurement and tracing utilities for monitoring application performance with cross-platform support and detailed timing information.
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();
});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");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();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.