0
# Performance Profiling
1
2
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.
3
4
## Capabilities
5
6
### Profile Module Initialization
7
8
Enable profiling for all memoized functions by importing the profile module before any memoization setup.
9
10
```javascript { .api }
11
/**
12
* Import profile module to enable statistics collection
13
* Must be imported BEFORE creating any memoized functions to track
14
*/
15
const memProfile = require("memoizee/profile");
16
```
17
18
**Usage Examples:**
19
20
```javascript
21
// Import profile module first (before any memoization)
22
const memProfile = require("memoizee/profile");
23
const memoize = require("memoizee");
24
25
// Now create memoized functions - they will be automatically tracked
26
function expensiveCalculation(x, y) {
27
return Math.pow(x, y) + Math.random();
28
}
29
30
const memoized1 = memoize(expensiveCalculation);
31
const memoized2 = memoize(expensiveCalculation, { profileName: "Power Calculation" });
32
33
// Use the functions
34
memoized1(2, 10); // Initial execution
35
memoized1(2, 10); // Cache hit
36
memoized1(3, 5); // Initial execution
37
38
memoized2(2, 8); // Initial execution
39
memoized2(2, 8); // Cache hit
40
41
// Access statistics
42
console.log(memProfile.statistics);
43
console.log(memProfile.log());
44
```
45
46
### Statistics Data Structure
47
48
Access raw performance statistics programmatically for custom analysis.
49
50
```javascript { .api }
51
/**
52
* Raw statistics object containing performance data
53
* @type {Object<string, {initial: number, cached: number}>}
54
*/
55
memProfile.statistics = {
56
// Key format: "profileName, sourceLocation" or just "sourceLocation"
57
"profile-name, at /path/to/file.js:line:column": {
58
initial: number, // Number of initial function executions
59
cached: number // Number of cache hits
60
}
61
};
62
```
63
64
**Usage Examples:**
65
66
```javascript
67
const memProfile = require("memoizee/profile");
68
const memoize = require("memoizee");
69
70
const memoizedFn = memoize(someFunction, { profileName: "MyFunction" });
71
72
// Generate some activity
73
memoizedFn("arg1"); // initial: 1, cached: 0
74
memoizedFn("arg1"); // initial: 1, cached: 1
75
memoizedFn("arg2"); // initial: 2, cached: 1
76
memoizedFn("arg1"); // initial: 2, cached: 2
77
78
// Access raw statistics
79
const stats = memProfile.statistics;
80
Object.keys(stats).forEach(key => {
81
const { initial, cached } = stats[key];
82
const total = initial + cached;
83
const hitRate = total > 0 ? (cached / total * 100).toFixed(2) : 0;
84
console.log(`${key}: ${hitRate}% hit rate (${cached}/${total})`);
85
});
86
```
87
88
### Formatted Statistics Report
89
90
Generate human-readable statistics reports for performance analysis and debugging.
91
92
```javascript { .api }
93
/**
94
* Generate formatted statistics report
95
* @returns {string} Formatted table with performance statistics
96
*/
97
memProfile.log();
98
```
99
100
**Usage Examples:**
101
102
```javascript
103
const memProfile = require("memoizee/profile");
104
const memoize = require("memoizee");
105
106
// Create multiple memoized functions with different profiles
107
const fibonacci = memoize(function fib(n) {
108
if (n < 2) return n;
109
return fib(n - 1) + fib(n - 2);
110
}, { profileName: "Fibonacci" });
111
112
const factorial = memoize(function fact(n) {
113
if (n <= 1) return 1;
114
return n * fact(n - 1);
115
}, { profileName: "Factorial" });
116
117
const dataProcessor = memoize(function processData(data) {
118
return data.map(x => x * 2).filter(x => x > 10);
119
});
120
121
// Generate some activity
122
fibonacci(10); // Creates many cache entries due to recursion
123
factorial(5); // Creates some cache entries
124
dataProcessor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
125
126
// Print formatted report
127
console.log(memProfile.log());
128
129
/* Example output:
130
------------------------------------------------------------
131
Memoize statistics:
132
133
Init Cache %Cache Source location
134
67 78 53.79 (all)
135
11 55 83.33 Fibonacci, at /path/to/script.js:15:32
136
5 4 44.44 Factorial, at /path/to/script.js:20:29
137
1 0 0.00 at /path/to/script.js:25:45
138
------------------------------------------------------------
139
*/
140
```
141
142
### Profile Names and Source Location
143
144
Control how functions are identified in profiling reports using profile names and automatic source location detection.
145
146
```javascript { .api }
147
/**
148
* Configure profile identification
149
* @param {string} profileName - Custom name for the memoized function
150
*/
151
const options = {
152
profileName: string
153
};
154
```
155
156
**Usage Examples:**
157
158
```javascript
159
const memProfile = require("memoizee/profile");
160
const memoize = require("memoizee");
161
162
// Functions with custom profile names
163
const userLookup = memoize(function(userId) {
164
return database.getUser(userId);
165
}, { profileName: "User Lookup" });
166
167
const configParser = memoize(function(configPath) {
168
return parseConfigFile(configPath);
169
}, { profileName: "Config Parser" });
170
171
// Function without custom name (uses source location)
172
const genericProcessor = memoize(function(data) {
173
return data.map(x => x.toUpperCase());
174
});
175
176
// Functions are identified in reports by their profile names or source location
177
console.log(memProfile.log());
178
179
/* Output shows:
180
- "User Lookup, at /path/to/file.js:line:column"
181
- "Config Parser, at /path/to/file.js:line:column"
182
- "at /path/to/file.js:line:column" (for genericProcessor)
183
*/
184
```
185
186
## Advanced Profiling Patterns
187
188
### Performance Monitoring
189
190
Set up continuous performance monitoring for production applications.
191
192
```javascript
193
const memProfile = require("memoizee/profile");
194
const memoize = require("memoizee");
195
196
// Create monitored functions
197
const criticalFunction = memoize(expensiveOperation, {
198
profileName: "Critical Path Operation"
199
});
200
201
// Monitor performance periodically
202
setInterval(() => {
203
const stats = memProfile.statistics;
204
205
Object.keys(stats).forEach(key => {
206
const { initial, cached } = stats[key];
207
const total = initial + cached;
208
const hitRate = total > 0 ? (cached / total * 100) : 0;
209
210
// Alert on poor cache performance
211
if (total > 100 && hitRate < 50) {
212
console.warn(`Low cache hit rate for ${key}: ${hitRate.toFixed(2)}%`);
213
}
214
215
// Alert on high cache usage
216
if (cached > 10000) {
217
console.info(`High cache usage for ${key}: ${cached} hits`);
218
}
219
});
220
}, 60000); // Check every minute
221
222
// Reset statistics periodically
223
setInterval(() => {
224
// Save current stats before reset
225
const currentStats = JSON.parse(JSON.stringify(memProfile.statistics));
226
227
// Clear statistics (requires recreating memoized functions)
228
Object.keys(memProfile.statistics).forEach(key => {
229
delete memProfile.statistics[key];
230
});
231
232
console.log("Statistics reset. Previous period:", currentStats);
233
}, 3600000); // Reset every hour
234
```
235
236
### A/B Testing Cache Strategies
237
238
Compare different memoization strategies using profiling data.
239
240
```javascript
241
const memProfile = require("memoizee/profile");
242
const memoize = require("memoizee");
243
244
// Strategy A: Primitive mode
245
const strategyA = memoize(dataTransform, {
246
primitive: true,
247
profileName: "Strategy A (Primitive)"
248
});
249
250
// Strategy B: Object mode with normalizer
251
const strategyB = memoize(dataTransform, {
252
normalizer: (args) => JSON.stringify(args[0]),
253
profileName: "Strategy B (Normalized)"
254
});
255
256
// Strategy C: Size-limited cache
257
const strategyC = memoize(dataTransform, {
258
max: 100,
259
profileName: "Strategy C (Limited)"
260
});
261
262
// Test each strategy
263
const testData = generateTestCases(1000);
264
265
console.time("Strategy A");
266
testData.forEach(data => strategyA(data));
267
console.timeEnd("Strategy A");
268
269
console.time("Strategy B");
270
testData.forEach(data => strategyB(data));
271
console.timeEnd("Strategy B");
272
273
console.time("Strategy C");
274
testData.forEach(data => strategyC(data));
275
console.timeEnd("Strategy C");
276
277
// Compare cache performance
278
console.log(memProfile.log());
279
```
280
281
### Custom Statistics Analysis
282
283
Build custom analytics on top of the profiling data.
284
285
```javascript
286
const memProfile = require("memoizee/profile");
287
288
function analyzePerformance() {
289
const stats = memProfile.statistics;
290
const analysis = {
291
totalFunctions: Object.keys(stats).length,
292
totalCalls: 0,
293
totalHits: 0,
294
functions: []
295
};
296
297
Object.entries(stats).forEach(([name, data]) => {
298
const total = data.initial + data.cached;
299
const hitRate = total > 0 ? (data.cached / total) : 0;
300
301
analysis.totalCalls += total;
302
analysis.totalHits += data.cached;
303
304
analysis.functions.push({
305
name,
306
calls: total,
307
hits: data.cached,
308
hitRate,
309
efficiency: hitRate * Math.log(total + 1) // Weighted by usage
310
});
311
});
312
313
// Sort by efficiency
314
analysis.functions.sort((a, b) => b.efficiency - a.efficiency);
315
316
analysis.overallHitRate = analysis.totalCalls > 0
317
? (analysis.totalHits / analysis.totalCalls)
318
: 0;
319
320
return analysis;
321
}
322
323
// Generate detailed performance report
324
function generatePerformanceReport() {
325
const analysis = analyzePerformance();
326
327
console.log("=== Performance Analysis ===");
328
console.log(`Total Functions: ${analysis.totalFunctions}`);
329
console.log(`Total Calls: ${analysis.totalCalls}`);
330
console.log(`Overall Hit Rate: ${(analysis.overallHitRate * 100).toFixed(2)}%`);
331
console.log("\nTop Performing Functions:");
332
333
analysis.functions.slice(0, 5).forEach((fn, i) => {
334
console.log(`${i + 1}. ${fn.name}`);
335
console.log(` Hit Rate: ${(fn.hitRate * 100).toFixed(2)}%`);
336
console.log(` Calls: ${fn.calls}, Hits: ${fn.hits}`);
337
console.log(` Efficiency Score: ${fn.efficiency.toFixed(2)}`);
338
});
339
}
340
341
// Run analysis periodically
342
setInterval(generatePerformanceReport, 300000); // Every 5 minutes
343
```
344
345
## Production Considerations
346
347
### Performance Impact
348
349
The profiling module adds overhead to memoized functions:
350
351
```javascript
352
// ⚠️ Profiling overhead considerations
353
const memProfile = require("memoizee/profile");
354
355
// Profiling adds:
356
// - Stack trace analysis for source location detection
357
// - Event listener overhead for cache operations
358
// - Statistics object maintenance
359
// - Memory usage for storing statistics
360
361
// Recommendations:
362
// ✅ Use in development and testing
363
// ✅ Use in production with monitoring systems
364
// ❌ Avoid in high-performance critical paths unless necessary
365
```
366
367
### Production Setup
368
369
Best practices for using profiling in production environments:
370
371
```javascript
372
// Conditional profiling based on environment
373
if (process.env.NODE_ENV !== 'production' || process.env.ENABLE_MEMOIZE_PROFILING) {
374
require("memoizee/profile");
375
}
376
377
const memoize = require("memoizee");
378
379
// Profile only critical functions in production
380
const criticalMemoized = memoize(criticalFunction, {
381
profileName: process.env.NODE_ENV === 'production'
382
? "Critical Function"
383
: undefined // Use source location in development
384
});
385
386
// Export statistics for monitoring systems
387
if (typeof require("memoizee/profile") !== 'undefined') {
388
setInterval(() => {
389
const stats = require("memoizee/profile").statistics;
390
// Send stats to monitoring service
391
monitoringService.sendMetrics('memoize_stats', stats);
392
}, 60000);
393
}
394
```
395
396
### Memory Management
397
398
Profiling statistics accumulate over time and may need periodic cleanup:
399
400
```javascript
401
const memProfile = require("memoizee/profile");
402
403
// Monitor statistics memory usage
404
function getStatsMemoryUsage() {
405
const stats = memProfile.statistics;
406
const entryCount = Object.keys(stats).length;
407
const estimatedBytes = entryCount * 200; // Rough estimate
408
return { entryCount, estimatedBytes };
409
}
410
411
// Periodic cleanup (if needed)
412
setInterval(() => {
413
const usage = getStatsMemoryUsage();
414
console.log(`Stats memory: ${usage.entryCount} entries, ~${usage.estimatedBytes} bytes`);
415
416
// Reset if too large (requires application restart or recreation of memoized functions)
417
if (usage.entryCount > 10000) {
418
console.log("Consider resetting memoization statistics");
419
}
420
}, 600000); // Check every 10 minutes
421
```