CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-memoizee

Complete memoization/caching solution for JavaScript functions with support for any argument types, async/promise functions, cache expiration, and advanced cache management features

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

profiling.mddocs/

Performance Profiling

Built-in profiling and statistics system that provides detailed insights into cache performance, hit rates, and function call patterns. Essential for optimizing memoization strategies and identifying performance bottlenecks in applications using memoized functions.

Capabilities

Profile Module Initialization

Enable profiling for all memoized functions by importing the profile module before any memoization setup.

/**
 * Import profile module to enable statistics collection
 * Must be imported BEFORE creating any memoized functions to track
 */
const memProfile = require("memoizee/profile");

Usage Examples:

// Import profile module first (before any memoization)
const memProfile = require("memoizee/profile");
const memoize = require("memoizee");

// Now create memoized functions - they will be automatically tracked
function expensiveCalculation(x, y) {
  return Math.pow(x, y) + Math.random();
}

const memoized1 = memoize(expensiveCalculation);
const memoized2 = memoize(expensiveCalculation, { profileName: "Power Calculation" });

// Use the functions
memoized1(2, 10);  // Initial execution
memoized1(2, 10);  // Cache hit
memoized1(3, 5);   // Initial execution

memoized2(2, 8);   // Initial execution  
memoized2(2, 8);   // Cache hit

// Access statistics
console.log(memProfile.statistics);
console.log(memProfile.log());

Statistics Data Structure

Access raw performance statistics programmatically for custom analysis.

/**
 * Raw statistics object containing performance data
 * @type {Object<string, {initial: number, cached: number}>}
 */
memProfile.statistics = {
  // Key format: "profileName, sourceLocation" or just "sourceLocation"
  "profile-name, at /path/to/file.js:line:column": {
    initial: number,    // Number of initial function executions
    cached: number      // Number of cache hits
  }
};

Usage Examples:

const memProfile = require("memoizee/profile");
const memoize = require("memoizee");

const memoizedFn = memoize(someFunction, { profileName: "MyFunction" });

// Generate some activity
memoizedFn("arg1");  // initial: 1, cached: 0
memoizedFn("arg1");  // initial: 1, cached: 1  
memoizedFn("arg2");  // initial: 2, cached: 1
memoizedFn("arg1");  // initial: 2, cached: 2

// Access raw statistics
const stats = memProfile.statistics;
Object.keys(stats).forEach(key => {
  const { initial, cached } = stats[key];
  const total = initial + cached;
  const hitRate = total > 0 ? (cached / total * 100).toFixed(2) : 0;
  console.log(`${key}: ${hitRate}% hit rate (${cached}/${total})`);
});

Formatted Statistics Report

Generate human-readable statistics reports for performance analysis and debugging.

/**
 * Generate formatted statistics report
 * @returns {string} Formatted table with performance statistics
 */
memProfile.log();

Usage Examples:

const memProfile = require("memoizee/profile");
const memoize = require("memoizee");

// Create multiple memoized functions with different profiles
const fibonacci = memoize(function fib(n) {
  if (n < 2) return n;
  return fib(n - 1) + fib(n - 2);
}, { profileName: "Fibonacci" });

const factorial = memoize(function fact(n) {
  if (n <= 1) return 1;
  return n * fact(n - 1);
}, { profileName: "Factorial" });

const dataProcessor = memoize(function processData(data) {
  return data.map(x => x * 2).filter(x => x > 10);
});

// Generate some activity
fibonacci(10);    // Creates many cache entries due to recursion
factorial(5);     // Creates some cache entries  
dataProcessor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

// Print formatted report
console.log(memProfile.log());

/* Example output:
------------------------------------------------------------
Memoize statistics:

 Init  Cache  %Cache  Source location
   67     78   53.79  (all)
   11     55   83.33  Fibonacci, at /path/to/script.js:15:32
    5      4   44.44  Factorial, at /path/to/script.js:20:29  
    1      0    0.00  at /path/to/script.js:25:45
------------------------------------------------------------
*/

Profile Names and Source Location

Control how functions are identified in profiling reports using profile names and automatic source location detection.

/**
 * Configure profile identification
 * @param {string} profileName - Custom name for the memoized function
 */
const options = {
  profileName: string
};

Usage Examples:

const memProfile = require("memoizee/profile");
const memoize = require("memoizee");

// Functions with custom profile names
const userLookup = memoize(function(userId) {
  return database.getUser(userId);
}, { profileName: "User Lookup" });

const configParser = memoize(function(configPath) {
  return parseConfigFile(configPath);
}, { profileName: "Config Parser" });

// Function without custom name (uses source location)
const genericProcessor = memoize(function(data) {
  return data.map(x => x.toUpperCase());
});

// Functions are identified in reports by their profile names or source location
console.log(memProfile.log());

/* Output shows:
- "User Lookup, at /path/to/file.js:line:column"
- "Config Parser, at /path/to/file.js:line:column"  
- "at /path/to/file.js:line:column" (for genericProcessor)
*/

Advanced Profiling Patterns

Performance Monitoring

Set up continuous performance monitoring for production applications.

const memProfile = require("memoizee/profile");
const memoize = require("memoizee");

// Create monitored functions
const criticalFunction = memoize(expensiveOperation, {
  profileName: "Critical Path Operation"
});

// Monitor performance periodically
setInterval(() => {
  const stats = memProfile.statistics;
  
  Object.keys(stats).forEach(key => {
    const { initial, cached } = stats[key];
    const total = initial + cached;
    const hitRate = total > 0 ? (cached / total * 100) : 0;
    
    // Alert on poor cache performance
    if (total > 100 && hitRate < 50) {
      console.warn(`Low cache hit rate for ${key}: ${hitRate.toFixed(2)}%`);
    }
    
    // Alert on high cache usage
    if (cached > 10000) {
      console.info(`High cache usage for ${key}: ${cached} hits`);
    }
  });
}, 60000); // Check every minute

// Reset statistics periodically
setInterval(() => {
  // Save current stats before reset
  const currentStats = JSON.parse(JSON.stringify(memProfile.statistics));
  
  // Clear statistics (requires recreating memoized functions)
  Object.keys(memProfile.statistics).forEach(key => {
    delete memProfile.statistics[key];
  });
  
  console.log("Statistics reset. Previous period:", currentStats);
}, 3600000); // Reset every hour

A/B Testing Cache Strategies

Compare different memoization strategies using profiling data.

const memProfile = require("memoizee/profile");
const memoize = require("memoizee");

// Strategy A: Primitive mode
const strategyA = memoize(dataTransform, {
  primitive: true,
  profileName: "Strategy A (Primitive)"
});

// Strategy B: Object mode with normalizer
const strategyB = memoize(dataTransform, {
  normalizer: (args) => JSON.stringify(args[0]),
  profileName: "Strategy B (Normalized)"
});

// Strategy C: Size-limited cache
const strategyC = memoize(dataTransform, {
  max: 100,
  profileName: "Strategy C (Limited)"
});

// Test each strategy
const testData = generateTestCases(1000);

console.time("Strategy A");
testData.forEach(data => strategyA(data));
console.timeEnd("Strategy A");

console.time("Strategy B");  
testData.forEach(data => strategyB(data));
console.timeEnd("Strategy B");

console.time("Strategy C");
testData.forEach(data => strategyC(data));
console.timeEnd("Strategy C");

// Compare cache performance
console.log(memProfile.log());

Custom Statistics Analysis

Build custom analytics on top of the profiling data.

const memProfile = require("memoizee/profile");

function analyzePerformance() {
  const stats = memProfile.statistics;
  const analysis = {
    totalFunctions: Object.keys(stats).length,
    totalCalls: 0,
    totalHits: 0,
    functions: []
  };
  
  Object.entries(stats).forEach(([name, data]) => {
    const total = data.initial + data.cached;
    const hitRate = total > 0 ? (data.cached / total) : 0;
    
    analysis.totalCalls += total;
    analysis.totalHits += data.cached;
    
    analysis.functions.push({
      name,
      calls: total,
      hits: data.cached,
      hitRate,
      efficiency: hitRate * Math.log(total + 1) // Weighted by usage
    });
  });
  
  // Sort by efficiency
  analysis.functions.sort((a, b) => b.efficiency - a.efficiency);
  
  analysis.overallHitRate = analysis.totalCalls > 0 
    ? (analysis.totalHits / analysis.totalCalls) 
    : 0;
  
  return analysis;
}

// Generate detailed performance report
function generatePerformanceReport() {
  const analysis = analyzePerformance();
  
  console.log("=== Performance Analysis ===");
  console.log(`Total Functions: ${analysis.totalFunctions}`);
  console.log(`Total Calls: ${analysis.totalCalls}`);
  console.log(`Overall Hit Rate: ${(analysis.overallHitRate * 100).toFixed(2)}%`);
  console.log("\nTop Performing Functions:");
  
  analysis.functions.slice(0, 5).forEach((fn, i) => {
    console.log(`${i + 1}. ${fn.name}`);
    console.log(`   Hit Rate: ${(fn.hitRate * 100).toFixed(2)}%`);
    console.log(`   Calls: ${fn.calls}, Hits: ${fn.hits}`);
    console.log(`   Efficiency Score: ${fn.efficiency.toFixed(2)}`);
  });
}

// Run analysis periodically
setInterval(generatePerformanceReport, 300000); // Every 5 minutes

Production Considerations

Performance Impact

The profiling module adds overhead to memoized functions:

// ⚠️ Profiling overhead considerations
const memProfile = require("memoizee/profile");

// Profiling adds:
// - Stack trace analysis for source location detection
// - Event listener overhead for cache operations  
// - Statistics object maintenance
// - Memory usage for storing statistics

// Recommendations:
// ✅ Use in development and testing
// ✅ Use in production with monitoring systems
// ❌ Avoid in high-performance critical paths unless necessary

Production Setup

Best practices for using profiling in production environments:

// Conditional profiling based on environment
if (process.env.NODE_ENV !== 'production' || process.env.ENABLE_MEMOIZE_PROFILING) {
  require("memoizee/profile");
}

const memoize = require("memoizee");

// Profile only critical functions in production
const criticalMemoized = memoize(criticalFunction, {
  profileName: process.env.NODE_ENV === 'production' 
    ? "Critical Function" 
    : undefined  // Use source location in development
});

// Export statistics for monitoring systems
if (typeof require("memoizee/profile") !== 'undefined') {
  setInterval(() => {
    const stats = require("memoizee/profile").statistics;
    // Send stats to monitoring service
    monitoringService.sendMetrics('memoize_stats', stats);
  }, 60000);
}

Memory Management

Profiling statistics accumulate over time and may need periodic cleanup:

const memProfile = require("memoizee/profile");

// Monitor statistics memory usage
function getStatsMemoryUsage() {
  const stats = memProfile.statistics;
  const entryCount = Object.keys(stats).length;
  const estimatedBytes = entryCount * 200; // Rough estimate
  return { entryCount, estimatedBytes };
}

// Periodic cleanup (if needed)
setInterval(() => {
  const usage = getStatsMemoryUsage();
  console.log(`Stats memory: ${usage.entryCount} entries, ~${usage.estimatedBytes} bytes`);
  
  // Reset if too large (requires application restart or recreation of memoized functions)
  if (usage.entryCount > 10000) {
    console.log("Consider resetting memoization statistics");
  }
}, 600000); // Check every 10 minutes

docs

async-functions.md

cache-management.md

function-memoization.md

index.md

method-memoization.md

profiling.md

weakmap-memoization.md

tile.json