Performance API Examples

The Performance API provides timing and performance measurement capabilities for optimizing your Andromeda applications.

Basic Timing

// Measure execution time with performance.now()
const start = performance.now();

// Simulate some work
let sum = 0;
for (let i = 0; i < 1000000; i++) {
  sum += Math.sqrt(i);
}

const end = performance.now();
const duration = end - start;

console.log(`⚡ Calculated sum: ${sum.toFixed(2)}`);
console.log(`⏱️  Execution time: ${duration.toFixed(2)}ms`);

Performance Marks and Measures

// Using performance marks for complex operations
performance.mark("data-processing-start");

// Generate test data
const data = Array.from({ length: 100000 }, (_, i) => ({
  id: i,
  value: Math.random() * 1000,
  category: `category-${i % 10}`,
}));

performance.mark("data-generation-complete");

// Process the data
const processed = data
  .filter((item) => item.value > 500)
  .map((item) => ({ ...item, processed: true }))
  .sort((a, b) => b.value - a.value);

performance.mark("data-processing-end");

// Create measures
performance.measure(
  "data-generation",
  "data-processing-start",
  "data-generation-complete",
);
performance.measure(
  "data-processing",
  "data-generation-complete",
  "data-processing-end",
);
performance.measure(
  "total-operation",
  "data-processing-start",
  "data-processing-end",
);

console.log(`📊 Processed ${processed.length} items from ${data.length} total`);

// Get performance entries
const entries = performance.getEntries();
console.log("📈 Performance entries:");
entries.forEach((entry) => {
  console.log(`   ${entry.name}: ${entry.duration.toFixed(2)}ms`);
});

Benchmarking Functions

async function benchmark<T>(
  name: string,
  fn: () => T | Promise<T>,
  iterations: number = 1000,
): Promise<{ average: number; min: number; max: number; total: number }> {
  const times: number[] = [];

  console.log(`🏃 Running benchmark: ${name} (${iterations} iterations)`);

  for (let i = 0; i < iterations; i++) {
    const start = performance.now();
    await fn();
    const end = performance.now();
    times.push(end - start);
  }

  const total = times.reduce((sum, time) => sum + time, 0);
  const average = total / iterations;
  const min = Math.min(...times);
  const max = Math.max(...times);

  console.log(`📈 Results for ${name}:`);
  console.log(`   Average: ${average.toFixed(3)}ms`);
  console.log(`   Min:     ${min.toFixed(3)}ms`);
  console.log(`   Max:     ${max.toFixed(3)}ms`);
  console.log(`   Total:   ${total.toFixed(3)}ms`);

  return { average, min, max, total };
}

// Benchmark different operations
await benchmark("Array creation", () => {
  return new Array(10000).fill(0).map((_, i) => i * 2);
});

await benchmark("Object manipulation", () => {
  const obj = { count: 0 };
  for (let i = 0; i < 1000; i++) {
    obj.count += i;
  }
  return obj;
});

await benchmark("String operations", () => {
  let result = "";
  for (let i = 0; i < 100; i++) {
    result += `item-${i}-`;
  }
  return result;
});

Performance Monitoring Class

class PerformanceMonitor {
  #measurements: Map<string, number[]> = new Map();

  startTimer(name: string): string {
    const markName = `${name}-start-${Date.now()}`;
    performance.mark(markName);
    return markName;
  }

  endTimer(startMarkName: string, operationName: string): number {
    const endMarkName = `${operationName}-end-${Date.now()}`;
    performance.mark(endMarkName);

    const measureName = `${operationName}-measure`;
    performance.measure(measureName, startMarkName, endMarkName);

    const entries = performance.getEntriesByName(measureName);
    const duration = entries[entries.length - 1].duration;

    // Store measurement
    if (!this.#measurements.has(operationName)) {
      this.#measurements.set(operationName, []);
    }
    this.#measurements.get(operationName)!.push(duration);

    return duration;
  }

  getStats(operationName: string) {
    const times = this.#measurements.get(operationName);
    if (!times || times.length === 0) {
      return null;
    }

    const total = times.reduce((sum, time) => sum + time, 0);
    const average = total / times.length;
    const min = Math.min(...times);
    const max = Math.max(...times);
    const median =
      [...times].sort((a, b) => a - b)[Math.floor(times.length / 2)];

    return {
      count: times.length,
      total: total.toFixed(3),
      average: average.toFixed(3),
      min: min.toFixed(3),
      max: max.toFixed(3),
      median: median.toFixed(3),
    };
  }

  getAllStats() {
    const stats: Record<string, any> = {};
    for (const [operation, _] of this.#measurements) {
      stats[operation] = this.getStats(operation);
    }
    return stats;
  }

  clear(operationName?: string) {
    if (operationName) {
      this.#measurements.delete(operationName);
      performance.clearMarks(`${operationName}-start`);
      performance.clearMarks(`${operationName}-end`);
      performance.clearMeasures(`${operationName}-measure`);
    } else {
      this.#measurements.clear();
      performance.clearMarks();
      performance.clearMeasures();
    }
  }
}

// Usage example
const monitor = new PerformanceMonitor();

// Monitor database operations
const dbTimer = monitor.startTimer("database-query");
// Simulate database work
await new Promise((resolve) => setTimeout(resolve, Math.random() * 100));
monitor.endTimer(dbTimer, "database-query");

// Monitor API calls
const apiTimer = monitor.startTimer("api-call");
// Simulate API call
await new Promise((resolve) => setTimeout(resolve, Math.random() * 200));
monitor.endTimer(apiTimer, "api-call");

// Get statistics
console.log("📊 Performance Statistics:");
console.log(monitor.getAllStats());

Memory and Resource Monitoring

class ResourceMonitor {
  #startTime: number;
  #checkpoints: Array<{ name: string; time: number; memory?: number }> = [];

  constructor() {
    this.#startTime = performance.now();
    this.checkpoint("initialization");
  }

  checkpoint(name: string): void {
    const currentTime = performance.now();
    this.#checkpoints.push({
      name,
      time: currentTime - this.#startTime,
    });
  }

  getResourceUsage(): Array<
    { name: string; timeFromStart: string; timeDiff: string }
  > {
    return this.#checkpoints.map((checkpoint, index) => {
      const timeDiff = index > 0
        ? (checkpoint.time - this.#checkpoints[index - 1].time).toFixed(2)
        : "0.00";

      return {
        name: checkpoint.name,
        timeFromStart: checkpoint.time.toFixed(2) + "ms",
        timeDiff: timeDiff + "ms",
      };
    });
  }

  report(): void {
    console.log("🔍 Resource Usage Report:");
    console.log("=".repeat(50));

    const usage = this.getResourceUsage();
    usage.forEach((item) => {
      console.log(`📌 ${item.name}:`);
      console.log(`   Time from start: ${item.timeFromStart}`);
      console.log(`   Time diff: ${item.timeDiff}`);
    });

    console.log("=".repeat(50));
    console.log(
      `⏱️  Total runtime: ${
        (performance.now() - this.#startTime).toFixed(2)
      }ms`,
    );
  }
}

// Usage example
const resourceMonitor = new ResourceMonitor();

// Simulate some work
for (let i = 0; i < 5; i++) {
  // Some computational work
  const result = Array.from({ length: 10000 }, (_, j) => j * i).reduce(
    (a, b) => a + b,
    0,
  );
  resourceMonitor.checkpoint(`computation-${i + 1}`);
}

resourceMonitor.report();

Performance Best Practices

// Example of optimizing code using performance measurements
function optimizeOperation() {
  console.log("🎯 Performance Optimization Example");

  // Test different approaches
  const testData = Array.from({ length: 100000 }, (_, i) => i);

  // Approach 1: Traditional for loop
  performance.mark("for-loop-start");
  let sum1 = 0;
  for (let i = 0; i < testData.length; i++) {
    sum1 += testData[i];
  }
  performance.mark("for-loop-end");
  performance.measure("for-loop", "for-loop-start", "for-loop-end");

  // Approach 2: Array.reduce
  performance.mark("reduce-start");
  const sum2 = testData.reduce((acc, val) => acc + val, 0);
  performance.mark("reduce-end");
  performance.measure("reduce", "reduce-start", "reduce-end");

  // Approach 3: for...of loop
  performance.mark("for-of-start");
  let sum3 = 0;
  for (const value of testData) {
    sum3 += value;
  }
  performance.mark("for-of-end");
  performance.measure("for-of", "for-of-start", "for-of-end");

  // Compare results
  const measures = performance.getEntriesByType("measure");
  console.log("🏁 Performance Comparison:");
  measures.slice(-3).forEach((measure) => {
    console.log(`   ${measure.name}: ${measure.duration.toFixed(3)}ms`);
  });

  // All sums should be equal
  console.log(`✅ Results match: ${sum1 === sum2 && sum2 === sum3}`);
}

optimizeOperation();

Run the Examples

Save any of these examples as .ts files and run them:

andromeda run performance-example.ts

The Performance API helps you identify bottlenecks and optimize your Andromeda applications for better performance!