Complete memoization/caching solution for JavaScript functions with support for any argument types, async/promise functions, cache expiration, and advanced cache management features
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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());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})`);
});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
------------------------------------------------------------
*/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)
*/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 hourCompare 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());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 minutesThe 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 necessaryBest 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);
}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