|
| 1 | +# CPU Profile Analysis Report |
| 2 | + |
| 3 | +- **Profile**: `x-cpuprofile-3000679-20251207-0.cpuprofile` |
| 4 | +- **Date**: 2025-12-07 |
| 5 | +- **Duration**: 180.02 seconds (3 minutes) |
| 6 | +- **Profile Type**: `xprofiler-cpu-profile` |
| 7 | + |
| 8 | +## Executive Summary |
| 9 | + |
| 10 | +This CPU profile was captured from a cnpmcore production instance. The profile shows the application is mostly idle (90% of the time), with about 6% active CPU usage during the 3-minute sampling period. |
| 11 | + |
| 12 | +### Key Findings |
| 13 | + |
| 14 | +1. **Leoric ORM is the #1 CPU consumer** - 24% of active CPU time is spent in leoric (ORM library) |
| 15 | +2. **The `Bone` constructor is the main hotspot** - Taking 15.38% of active CPU time alone |
| 16 | +3. **deep-equal operations in leoric are expensive** - Type checking functions (`is-string`, `is-number-object`, `is-array-buffer`) consume significant CPU |
| 17 | +4. **Application code is very efficient** - Only 2.18% of CPU time is in application code |
| 18 | + |
| 19 | +## CPU Time Distribution |
| 20 | + |
| 21 | +| Category | Samples | % of Total | |
| 22 | +|----------|---------|------------| |
| 23 | +| Idle | 151,070 | 90.09% | |
| 24 | +| GC (Garbage Collection) | 4,888 | 2.91% | |
| 25 | +| Active/User Code | 10,098 | 6.02% | |
| 26 | +| Program | 1,641 | 0.98% | |
| 27 | + |
| 28 | +## Active CPU Time Breakdown |
| 29 | + |
| 30 | +| Category | Samples | % of Active | |
| 31 | +|----------|---------|-------------| |
| 32 | +| NPM Packages | 4,085 | 40.45% | |
| 33 | +| Native/V8 | 2,975 | 29.46% | |
| 34 | +| Node.js Core | 2,818 | 27.91% | |
| 35 | +| Application Code | 220 | 2.18% | |
| 36 | + |
| 37 | +## Top Performance Bottlenecks |
| 38 | + |
| 39 | +### 1. Leoric ORM - `Bone` Constructor (15.38%) |
| 40 | + |
| 41 | +The single biggest CPU consumer is the `Bone` constructor in leoric ORM. |
| 42 | + |
| 43 | +**Location **: `node_modules/[email protected]@leoric/lib/bone.js:150` |
| 44 | + |
| 45 | +**Call paths**: |
| 46 | +- Database query results → `instantiate()` → `dispatch()` → `Bone()` |
| 47 | +- Entity creation → `create()` → `Bone()` |
| 48 | + |
| 49 | +**Recommendation**: |
| 50 | +- Consider lazy instantiation for bulk queries |
| 51 | +- Review if all Bone properties need to be initialized upfront |
| 52 | +- Consider upgrading leoric if newer versions have optimizations |
| 53 | + |
| 54 | +### 2. Deep Equality Checks in Leoric (2.5%) |
| 55 | + |
| 56 | +The `changes()` function in leoric uses `deep-equal` which triggers expensive type checking: |
| 57 | + |
| 58 | +| Function | Samples | % | |
| 59 | +|----------|---------|---| |
| 60 | +| tryStringObject (is-string) | 68 | 0.67% | |
| 61 | +| isArrayBuffer | 51 | 0.50% | |
| 62 | +| tryNumberObject | 45 | 0.45% | |
| 63 | +| booleanBrandCheck | 51 | 0.50% | |
| 64 | +| isSharedArrayBuffer | 37 | 0.37% | |
| 65 | + |
| 66 | +**Recommendation**: |
| 67 | +- Check if leoric has an option to skip change detection |
| 68 | +- For bulk inserts, consider using raw SQL queries |
| 69 | +- Review if `deep-equal` can be replaced with faster comparison |
| 70 | + |
| 71 | +### 3. MySQL2 Driver (2.72%) |
| 72 | + |
| 73 | +MySQL2 operations including result parsing: |
| 74 | + |
| 75 | +| Function | Samples | % | |
| 76 | +|----------|---------|---| |
| 77 | +| column_definition.get | 56 | 0.55% | |
| 78 | +| query.start | 49 | 0.49% | |
| 79 | +| keyFromFields | 30 | 0.30% | |
| 80 | + |
| 81 | +**Recommendation**: |
| 82 | +- These are normal database operations - no immediate action needed |
| 83 | +- Consider connection pooling optimization if not already configured |
| 84 | + |
| 85 | +### 4. Network I/O (writev/writeBuffer) - 10.1% |
| 86 | + |
| 87 | +Significant time spent in network I/O operations: |
| 88 | + |
| 89 | +| Function | Samples | % | |
| 90 | +|----------|---------|---| |
| 91 | +| writev (native) | 1,037 | 10.27% | |
| 92 | +| writeBuffer (native) | 437 | 4.33% | |
| 93 | + |
| 94 | +**Recommendation**: |
| 95 | +- This is expected for a registry that serves packages |
| 96 | +- Consider response compression if not enabled |
| 97 | +- Review if large payloads can be streamed |
| 98 | + |
| 99 | +### 5. urllib JSON Parsing (0.31%) |
| 100 | + |
| 101 | +**Location **: `node_modules/[email protected]@urllib/dist/esm/utils.js:25` |
| 102 | + |
| 103 | +**Recommendation**: |
| 104 | +- Normal operation for HTTP client responses |
| 105 | +- Consider if some responses don't need JSON parsing |
| 106 | + |
| 107 | +## Application Code Analysis |
| 108 | + |
| 109 | +The application code is highly efficient. Top application hotspots: |
| 110 | + |
| 111 | +| Function | File | Samples | % | |
| 112 | +|----------|------|---------|---| |
| 113 | +| syncPackage | PackageSearchService.js:16 | 22 | 0.22% | |
| 114 | +| convertModelToEntity | ModelConvertor.js:74 | 38 | 0.38% | |
| 115 | +| syncPackageWithPackument | PackageSyncerService.js:926 | 14 | 0.14% | |
| 116 | +| findBinary | BinaryRepository.js:27 | 7 | 0.07% | |
| 117 | + |
| 118 | +**Observation**: The application code is well-optimized. Most CPU time is in third-party dependencies. |
| 119 | + |
| 120 | +## Recommendations Summary |
| 121 | + |
| 122 | +### High Priority |
| 123 | + |
| 124 | +1. **Investigate Leoric Bone Constructor** |
| 125 | + - This is consuming 15.38% of active CPU time |
| 126 | + - Check if leoric has batch instantiation options |
| 127 | + - Consider lazy loading of entity properties |
| 128 | + - Profile specific queries to identify the most expensive ones |
| 129 | + |
| 130 | +2. **Review deep-equal Usage** |
| 131 | + - The `changes()` function triggers expensive type checks |
| 132 | + - For bulk operations, consider skipping change detection |
| 133 | + - Explore if leoric supports simpler comparison strategies |
| 134 | + |
| 135 | +### Medium Priority |
| 136 | + |
| 137 | +3. **GC Optimization** |
| 138 | + - GC is at 2.91% which is reasonable but could be improved |
| 139 | + - Review object allocation patterns in hot paths |
| 140 | + - Consider object pooling for frequently created objects |
| 141 | + |
| 142 | +4. **Network I/O Review** |
| 143 | + - writev operations are expected but at 10% worth monitoring |
| 144 | + - Ensure response streaming is properly configured |
| 145 | + - Review large payload handling |
| 146 | + |
| 147 | +### Low Priority |
| 148 | + |
| 149 | +5. **Keep Application Code Lean** |
| 150 | + - Application code is only 2.18% of CPU - excellent |
| 151 | + - Continue following current coding patterns |
| 152 | + |
| 153 | +## Tools Created |
| 154 | + |
| 155 | +The following analysis scripts have been created in `benchmark/profiler/`: |
| 156 | + |
| 157 | +1. **analyze-profile.js** - Comprehensive CPU profile analyzer |
| 158 | + ```bash |
| 159 | + node benchmark/profiler/analyze-profile.js path/to/profile.cpuprofile |
| 160 | + ``` |
| 161 | + |
| 162 | +2. **hotspot-finder.js** - Find specific hotspots with filtering |
| 163 | + ```bash |
| 164 | + node benchmark/profiler/hotspot-finder.js profile.cpuprofile --filter=leoric --top=20 |
| 165 | + ``` |
| 166 | + |
| 167 | +3. **flamegraph-convert.js** - Convert to folded stack format for flame graphs |
| 168 | + ```bash |
| 169 | + node benchmark/profiler/flamegraph-convert.js profile.cpuprofile > stacks.txt |
| 170 | + ``` |
| 171 | + |
| 172 | +## Viewing the Profile |
| 173 | + |
| 174 | +The `.cpuprofile` file can be viewed in: |
| 175 | + |
| 176 | +1. **Chrome DevTools**: Open `chrome://inspect` → Open dedicated DevTools → Performance tab → Load |
| 177 | +2. **speedscope.app**: Upload the file directly at https://www.speedscope.app/ |
| 178 | +3. **VS Code**: Install "vscode-js-profile-flame" extension |
| 179 | + |
| 180 | +## Conclusion |
| 181 | + |
| 182 | +The cnpmcore application is well-optimized with only 2.18% of CPU time in application code. The main optimization opportunity is in the leoric ORM layer, specifically the `Bone` constructor which consumes 15.38% of active CPU time. The GC time at 2.91% is reasonable for a Node.js application of this complexity. |
0 commit comments