Performance is a fundamental concern for any modern web application, and Meteor apps are no exception. Currently, one of our main focuses is optimizing the Meteor bundler, which can be a significant bottleneck in larger projects.
Although Meteor already offers built-in tools for performance analysis, using native CPU profiling can provide much deeper insights into performance bottlenecks. In this article, we’ll explore how to effectively use CPU profiling in Meteor, including the difference between Meteor’s current profiling system and native profiling based on Node.js Inspector, with special emphasis on how this can help improve the bundling process.
What is CPU Profiling?
CPU profiling is a performance analysis technique that tracks exactly which functions are being executed by the CPU, for how long, and how frequently. This type of analysis allows you to identify precisely where your code is spending the most processing time.
Unlike simple time measurement techniques (like basic timers), CPU profiling creates a comprehensive view of code execution, including:
- Function call tree
- Execution time for each function
- Frequency of calls for each function
- Memory allocation (in some cases)
- Call stack visualization
Current Meteor Profiling vs Native CPU Profiling
Meteor’s Built-in Profiling System
Meteor has a built-in profiling system that can be activated by setting the METEOR_PROFILE
environment variable:
METEOR_PROFILE=1 meteor <command>
This profiler uses a timer-based measurement approach and provides reports like:
| (#1) Profiling: ProjectContext prepareProjectForBuild
| ProjectContext prepareProjectForBuild..........9,207 ms (1)
| _initializeCatalog.............................24 ms (1)
| files.readFile 7 ms (2)
| unJavaScript package.js 2 ms (1) files.rm_recursive 4 ms (4)
| other _initializeCatalog 11 ms
| _resolveConstraints.........................6,702 ms (1)
| bundler.readJsImage.........................42 ms (1)
| (#1) Total: 9,544 ms (ProjectContext prepareProjectForBuild)
Native CPU Profiling with Node.js Inspector
Recent versions of Meteor support native CPU profiling using Node.js’s inspector
module. This generates .cpuprofile
files analyzable via Chrome DevTools or cpupro.
Enable profiling with:
METEOR_INSPECT=bundler.bundle,compiler.compile meteor <command>
Advantages:
- Complete execution view
- Interactive analysis
- Hidden bottleneck discovery
- Superior precision
- Multiple metrics
How to Use Native CPU Profiling in Meteor
Basic Configuration
# Profile a single function
METEOR_INSPECT=bundler.bundle meteor build ./output-build
# Profile multiple functions
METEOR_INSPECT=bundler.bundle,compiler.compile meteor build ./output-build
Available Functions for Profiling
bundler.bundle
compiler.compile
Babel.compile
_readProjectMetadata
initializeCatalog
_downloadMissingPackages
_saveChangeMetadata
_realpath
package-client
Advanced Configuration Options
METEOR_INSPECT_CONTEXT=context_name
METEOR_INSPECT_OUTPUT=/path/to/directory
METEOR_INSPECT_INTERVAL=500
METEOR_INSPECT_MAX_SIZE=1000
For Large Applications
NODE_OPTIONS="--max-old-space-size=4096" METEOR_INSPECT=bundler.bundle meteor <command>
Analyzing CPU Profiling Results
With Chrome DevTools
- Open Chrome DevTools
- Go to the “Performance” or “Profiler” tab
- Click “Load Profile” and select the generated
.cpuprofile
file
With cpupro
- Visit https://discoveryjs.github.io/cpupro/
- Drag and drop your
.cpuprofile
file - Explore the visualizations
Practical Use Cases
Identifying Bottlenecks in Template Compilation
METEOR_INSPECT=compiler.compile meteor run
Diagnosing Slow Build Problems
METEOR_INSPECT=bundler.bundle meteor build ./output
Optimizing Package Loading
METEOR_INSPECT=_downloadMissingPackages,package-client meteor update
When to Use Each Type of Profiling
Use METEOR_PROFILE
when:
- You need a quick overview
- You’re analyzing Meteor-specific internals
- You want simple text output
- You’re short on memory
Use METEOR_INSPECT
when:
- You need deep performance insights
- You’re debugging bundler or compiler
- You want interactive visualizations
- You suspect Node.js or third-party code issues
Real-Life Demo
Case 1: Babel as the Main CPU Consumer
This is being addressed in PR #13675 and PR #13674, replacing Babel with SWC for improved performance while preserving Meteor features.
Case 2: Bottleneck in _linkJS
The _linkJS
function consumes a lot of CPU time due to:
- Multiple
await
calls - JSON stringification
- SHA-1 hash generation
This becomes a major bottleneck in large projects.
Conclusion
While Meteor’s built-in profiling is useful for quick diagnostics, native CPU profiling unlocks a much deeper level of insight. Try both approaches to elevate your performance debugging workflow.