Skip to content

Code Analysis

modestbench’s code analysis feature (via the analyze command, alias profile) helps you identify which functions in your codebase consume the most execution time. This is invaluable for:

  • Finding benchmark candidates: Discover which functions would benefit most from optimization
  • Guided performance work: Focus your efforts on code that matters
  • Understanding bottlenecks: See where your application spends time during execution

The analyzer uses Node.js’s built-in V8 profiler (--cpu-prof) to capture real execution data, then filters and presents the results.

Analyze any Node.js command to see hot code paths:

Terminal window
# Try the profiling demo (shows clear hot paths)
modestbench analyze "node examples/profiling-demo.js"
# Analyze your test suite
modestbench analyze "npm test"
# Analyze a specific script
modestbench analyze "node ./src/server.js"
# Analyze your application startup
modestbench analyze "node ./dist/index.js"

CPU profiles are stored in .modestbench/profiles/ to keep your workspace clean. This directory is automatically gitignored.

You can analyze existing profiles with the --input flag:

Terminal window
modestbench analyze --input .modestbench/profiles/CPU.bunchanumbers.cpuprofile

The profiler shows functions that consume the most CPU time:

██ Profile Analysis
Command: npm test
Duration: 5.2s
Total Ticks: 8,234
██ Benchmark Candidates
Top functions by execution time:
sortArray 12.3% (1,013 ticks)
src/utils/sorting.js:42
validateSchema 8.7% (716 ticks)
src/validators/schema.js:28
parseInput 6.1% (502 ticks)
src/parsers/input.js:15
... (showing top 3 of 45 user functions)

Functions are color-coded by execution percentage:

  • Red (≥10%): Critical hot paths - highest priority for benchmarking
  • Yellow (≥5%): Significant execution time - good candidates
  • Cyan (≥2%): Moderate impact - consider for optimization
  • White (<2%): Minor impact - usually not worth benchmarking

A “tick” is a CPU time sample. The V8 profiler periodically samples what function is executing. More ticks = more time spent in that function. The percentage shows what portion of total execution time each function consumed.

By default, the profiler uses smart detection to focus on your code:

  • Includes: Functions from your project directory
  • Excludes: node_modules dependencies
  • Excludes: Node.js internals (node:*, internal/*)

This filtering helps you focus on code you can actually optimize, rather than third-party libraries.

Override smart detection with explicit patterns:

Terminal window
# Focus on specific files
modestbench analyze "npm test" --filter-file "**/utils/**"
# Set minimum threshold (default: 1%)
modestbench analyze "npm test" --min-percent 5.0
# Show more results (default: 25)
modestbench analyze "npm test" --top 50

Use --group-by-file to see results organized by source file:

Terminal window
modestbench analyze "npm test" --group-by-file

Output:

██ Grouped by File
▓ src/utils/sorting.js 18.5% (1,523 ticks)
▪ sortArray 12.3% (1,013 ticks) :42
▪ quickSort 4.2% (346 ticks) :98
▪ partition 2.0% (164 ticks) :125
▓ src/validators/schema.js 11.3% (931 ticks)
▪ validateSchema 8.7% (716 ticks) :28
▪ checkRequired 2.6% (215 ticks) :145

This view is particularly useful for:

  • Identifying files with multiple hot functions
  • Understanding the overall performance impact of a module
  • Prioritizing which files to focus optimization efforts on

If you already have V8 profile logs, you can analyze them directly:

Terminal window
# Analyze an existing profile
modestbench analyze --input isolate-0x123-v8.log

This is useful for:

  • Analyzing profiles from production environments
  • Comparing profiles from different runs
  • Processing profiles generated with custom NODE_OPTIONS

Here’s a recommended workflow for using profiling data:

Start by profiling a representative workload:

Terminal window
# Profile your test suite (good for library development)
modestbench analyze "npm test"
# Or profile your application with typical usage
modestbench analyze "node ./dist/app.js --run-typical-task"

Look for functions that meet these criteria:

  • High execution percentage (≥5%)
  • Pure/deterministic: Same inputs → same outputs
  • Frequently called: Optimization will have broad impact
  • In your codebase: You can actually change them

First, initialize the benchmarking system if you haven’t already:

Terminal window
modestbench init

Now, for each candidate, create a focused benchmark:

benchmarks/sorting.bench.js
import { sortArray } from '../src/utils/sorting.js';
const small = Array.from({ length: 100 }, () => Math.random());
const medium = Array.from({ length: 1000 }, () => Math.random());
const large = Array.from({ length: 10000 }, () => Math.random());
export default {
suites: {
'Array Sorting': {
benchmarks: {
'sortArray - small (100)': () => sortArray([...small]),
'sortArray - medium (1000)': () => sortArray([...medium]),
'sortArray - large (10000)': () => sortArray([...large]),
},
},
},
};

Run your benchmarks to establish a baseline:

Terminal window
modestbench
# Track over time
modestbench history trends

Find hot paths in your test suite:

Terminal window
modestbench analyze "npm test"

Why this is useful:

  • Tests often exercise core functionality
  • High test execution time often indicates real performance issues
  • Easy to reproduce and measure

Identify slow initialization code:

Terminal window
modestbench analyze "node ./dist/app.js" --group-by-file

Look for:

  • Heavy parsing/validation on startup
  • Expensive module initialization
  • Synchronous I/O during startup

Target specific operations:

Terminal window
# Profile data processing
modestbench analyze "node scripts/process-data.js"
# Profile API requests
modestbench analyze "node scripts/load-test.js"
Terminal window
modestbench analyze [command]
Options:
--input, -i Path to existing *.cpuprofile file
--filter-file Filter functions by file glob pattern
--min-percent Minimum execution percentage (default: 0.5)
--top, -n Number of top functions to show (default: 25)
--group-by-file Group results by source file
--color Enable/disable colored output

V8 profiling has some variability. Run 2-3 times to verify consistent results:

Terminal window
modestbench analyze "npm test"
# Run again
modestbench analyze "npm test"

Functions consistently appearing at the top are your best candidates.

Profile code that exercises realistic scenarios:

Terminal window
# ❌ Not representative
modestbench analyze "node --eval 'console.log(1)'"
# ✅ Representative workload
modestbench analyze "npm test"
modestbench analyze "node ./scripts/typical-task.js"

Use profiling to discover candidates, benchmarking to measure improvements:

Terminal window
# 1. Profile to find hot paths
modestbench analyze "npm test" > profile-baseline.txt
# 2. Create benchmarks for hot functions
# ... create benchmarks ...
# 3. Run benchmarks to establish baseline
modestbench
# 4. Optimize the code
# ... make improvements ...
# 5. Re-run benchmarks to measure impact
modestbench
# 6. Profile again to verify
modestbench analyze "npm test" > profile-after.txt

High percentage doesn’t always mean “needs optimization”:

  • Expected hot paths: Core algorithms should take time
  • Appropriate complexity: O(n²) algorithms might be fine for small n
  • Already fast enough: Sometimes 5% of a fast program is still fast

Focus on functions where optimization would provide meaningful benefit.

Use the profiler in your own tools:

import {
runWithProfiling,
parseProfile,
filterProfile,
ProfileHumanReporter,
findPackageRoot,
} from 'modestbench';
// Run profiling
const logPath = await runWithProfiling('npm test', {
cwd: process.cwd(),
});
// Parse results
const profileData = await parseProfile(logPath);
// Filter to user code
const packageRoot = await findPackageRoot(process.cwd());
const filtered = filterProfile(
profileData,
{
smartDetection: true,
minExecutionPercent: 2.0,
topN: 50,
},
packageRoot,
);
// Display results
const reporter = new ProfileHumanReporter({ color: true });
reporter.report(filtered);

If you see “No profile log generated”:

  • Ensure the command actually runs Node.js code
  • Check that the process completes successfully
  • Try with a simpler command first: modestbench analyze "node --eval 'console.log(1)'"

If the profiler shows 0 user functions:

  • Your code might be too fast to profile (good problem!)
  • Try running longer workloads
  • Lower the --min-percent threshold
  • Check that --filter-file patterns match your code

Profile logs are reused if they exist. To get fresh data, remove the isolate-*.log files created by Node.js’s V8 profiler (via the --cpu-prof flag that modestbench analyze uses under the hood):

Terminal window
# Remove old logs
rm isolate-*.log
# Run profiling again
modestbench analyze "npm test"