|
| 1 | +# File Detection Performance Comparison |
| 2 | + |
| 3 | +This benchmark compares the performance of different approaches for detecting project configuration files in directory trees, specifically comparing the current shell-based implementation with custom Bun/TypeScript alternatives. |
| 4 | + |
| 5 | +## 🎯 Key Findings |
| 6 | + |
| 7 | +The Bun-based approaches are **dramatically faster** than the current shell implementation: |
| 8 | + |
| 9 | +- **Bun Direct (sync)**: **99.2% faster** on average |
| 10 | +- **Bun Glob (async)**: **98.9% faster** on average |
| 11 | + |
| 12 | +## 📊 Detailed Results |
| 13 | + |
| 14 | +| Test Scenario | Bun Direct | Bun Glob | Shell (Current) | Speed Improvement | |
| 15 | +|---------------|------------|----------|-----------------|-------------------| |
| 16 | +| Shallow (3 levels) | 0.12ms | 0.27ms | 24.85ms | **99.5% faster** | |
| 17 | +| Medium (7 levels) | 0.31ms | 0.74ms | 49.96ms | **99.4% faster** | |
| 18 | +| Deep (15 levels) | 0.72ms | 0.94ms | 89.68ms | **99.2% faster** | |
| 19 | +| Very Deep (25 levels) | 1.39ms | 1.53ms | 143.67ms | **99.0% faster** | |
| 20 | + |
| 21 | +## 🔍 Analysis |
| 22 | + |
| 23 | +### Current Shell Approach |
| 24 | +```bash |
| 25 | +files=$(ls -1a "$dir" 2>/dev/null | grep -E '^(dependencies|deps|pkgx|launchpad)\.(yaml|yml)$|^package\.json$|...' | head -1) |
| 26 | +``` |
| 27 | + |
| 28 | +**Pros:** |
| 29 | +- ✅ Works in any environment (no runtime dependencies) |
| 30 | +- ✅ Leverages optimized shell commands (`ls`, `grep`) |
| 31 | +- ✅ Consistent performance regardless of directory depth |
| 32 | + |
| 33 | +**Cons:** |
| 34 | +- ❌ **Significant process overhead** (spawning shell, pipes, regex) |
| 35 | +- ❌ **Poor scalability** - gets slower with deeper directory trees |
| 36 | +- ❌ **25-140ms per lookup** depending on depth |
| 37 | + |
| 38 | +### Bun Direct Approach (Recommended) |
| 39 | +```typescript |
| 40 | +for (const file of PROJECT_FILES) { |
| 41 | + const filePath = join(currentDir, file) |
| 42 | + if (existsSync(filePath)) { |
| 43 | + return currentDir |
| 44 | + } |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +**Pros:** |
| 49 | +- ✅ **Fastest performance** (0.12-1.39ms) |
| 50 | +- ✅ **No async overhead** |
| 51 | +- ✅ **Simple, readable implementation** |
| 52 | +- ✅ **Excellent scalability** |
| 53 | +- ✅ **Direct file system calls** |
| 54 | + |
| 55 | +**Cons:** |
| 56 | +- ❌ Requires Node.js/Bun runtime |
| 57 | +- ❌ Multiple `existsSync` calls (though still faster) |
| 58 | + |
| 59 | +### Bun Glob Approach |
| 60 | +```typescript |
| 61 | +const glob = new Glob(`{${PROJECT_FILES.join(',')}}`) |
| 62 | +for await (const file of glob.scan({ cwd: currentDir, onlyFiles: true })) { |
| 63 | + return currentDir // Found a match |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +**Pros:** |
| 68 | +- ✅ **Very fast performance** (0.27-1.53ms) |
| 69 | +- ✅ **Flexible pattern matching** |
| 70 | +- ✅ **Single glob operation per directory** |
| 71 | + |
| 72 | +**Cons:** |
| 73 | +- ❌ **Async overhead** (slightly slower than direct approach) |
| 74 | +- ❌ Requires Node.js/Bun runtime |
| 75 | +- ❌ More complex implementation |
| 76 | + |
| 77 | +## 🚀 Performance Characteristics |
| 78 | + |
| 79 | +### Scaling with Directory Depth |
| 80 | + |
| 81 | +| Depth | Shell Time | Bun Direct | Bun Glob | Shell Overhead | |
| 82 | +|-------|------------|------------|----------|----------------| |
| 83 | +| 3 levels | 24.85ms | 0.12ms | 0.27ms | **207x slower** | |
| 84 | +| 7 levels | 49.96ms | 0.31ms | 0.74ms | **161x slower** | |
| 85 | +| 15 levels | 89.68ms | 0.72ms | 0.94ms | **124x slower** | |
| 86 | +| 25 levels | 143.67ms | 1.39ms | 1.53ms | **103x slower** | |
| 87 | + |
| 88 | +**Key Observations:** |
| 89 | +- Shell approach has **linear degradation** with depth |
| 90 | +- Bun approaches scale much better |
| 91 | +- The deeper the directory tree, the more pronounced the performance difference |
| 92 | + |
| 93 | +## 💡 Recommendations |
| 94 | + |
| 95 | +### 1. **Primary Recommendation: Hybrid Approach** |
| 96 | + |
| 97 | +Implement a hybrid solution that uses Bun when available, falls back to shell: |
| 98 | + |
| 99 | +```typescript |
| 100 | +function findProjectRoot(startDir: string): string | null { |
| 101 | + // Try Bun approach first (when in Node.js/Bun environment) |
| 102 | + if (typeof process !== 'undefined') { |
| 103 | + return findProjectRootBunSync(startDir) |
| 104 | + } |
| 105 | + |
| 106 | + // Fallback to shell approach |
| 107 | + return findProjectRootShell(startDir) |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +### 2. **For Pure Performance: Bun Direct** |
| 112 | + |
| 113 | +If you can guarantee a Node.js/Bun runtime, use the direct file system approach: |
| 114 | +- **99.2% faster** than current implementation |
| 115 | +- Simple, maintainable code |
| 116 | +- Excellent scalability |
| 117 | + |
| 118 | +### 3. **For Flexibility: Bun Glob** |
| 119 | + |
| 120 | +If you need more complex pattern matching or plan to extend file detection: |
| 121 | +- **98.9% faster** than current implementation |
| 122 | +- More flexible for future enhancements |
| 123 | +- Slightly more overhead due to async nature |
| 124 | + |
| 125 | +## 🔧 Implementation Impact |
| 126 | + |
| 127 | +### Current Usage Patterns |
| 128 | +The file detection is used in: |
| 129 | +- Shell integration (`__lp_find_deps_dir`) |
| 130 | +- Project root discovery |
| 131 | +- Development environment setup |
| 132 | + |
| 133 | +### Migration Considerations |
| 134 | +1. **Backward Compatibility**: Keep shell version for environments without Node.js |
| 135 | +2. **Runtime Detection**: Detect available runtime and choose appropriate method |
| 136 | +3. **Caching**: Both approaches benefit from the existing caching mechanism |
| 137 | +4. **Error Handling**: Ensure graceful fallback between approaches |
| 138 | + |
| 139 | +## 🧪 Running the Benchmark |
| 140 | + |
| 141 | +```bash |
| 142 | +cd packages/launchpad/benchmark |
| 143 | +bun install |
| 144 | +bun run file-detection-comparison.ts |
| 145 | +``` |
| 146 | + |
| 147 | +The benchmark creates temporary directory structures at various depths and measures the time to find project files, providing comprehensive performance data across different scenarios. |
| 148 | + |
| 149 | +## 📈 Conclusion |
| 150 | + |
| 151 | +The performance difference is **dramatic and consistent**: |
| 152 | +- Bun approaches are **~100x faster** than the shell approach |
| 153 | +- The performance gap **increases** with directory depth |
| 154 | +- Implementation complexity is **minimal** for the Bun direct approach |
| 155 | + |
| 156 | +**Recommendation**: Implement the hybrid approach with Bun Direct as primary and shell as fallback for maximum performance while maintaining compatibility. |
0 commit comments