Skip to content

Conversation

@maatheusgois-dd
Copy link

@maatheusgois-dd maatheusgois-dd commented Sep 2, 2025

📋 Problem

The existing pbxproj parser crashed with heap out of memory errors on large projects like Test (94.81MB, 125K+ objects). The original JsonVisitor used expensive object spread operations ({...prev, ...item}) causing O(n²) complexity and excessive memory allocation.

Solution

Implemented multi-tier parsing optimizations with automatic strategy selection based on file size:

1. Optimized JsonVisitor (OptimizedJsonVisitor.ts)

  • Eliminated object spread operations - Replaced with direct property assignment (O(n) complexity)
  • Pre-allocated objects with known sizes to reduce memory allocations
  • Cached common identifiers (isa, children, name, etc.) to reduce string creation
  • Progress reporting for large files with memory pressure detection

2. Streaming Parser (StreamingJsonVisitor.ts)

  • Chunked processing - Handles massive objects in 1K item chunks
  • Memory management - Automatic garbage collection hints when memory pressure detected
  • Deferred object processing to prevent memory buildup

3. Memory-Efficient Analyzer (MemoryEfficientVisitor.ts)

  • Metadata extraction only - No full object tree construction
  • Constant memory usage regardless of file size
  • Essential statistics - File types, object counts, target names

4. Enhanced XcodeProject API

  • XcodeProject.openLazy() - Skip expensive full object inflation
  • Automatic strategy selection - Picks optimal parser based on file size
  • Progress callbacks and memory monitoring

📊 Performance Results

image

Before vs After:

Project Size Before After Improvement
0.10MB (AFNetworking) 7.80ms 3.84ms 51% faster 🚀
94.81MB (Test) Crashes (OOM) 3.5s ∞x improvement

Test Project Success:

📊 Processing large object with 125,709 items
🌊 Streaming JSON parsing: 473.998ms
✅ Found 125,709 objects  
💾 Final memory: 2498MB (managed, no crash)
🚀 Root object inflation: 283.785ms
✅ Optimized parsing successful

🔧 API Usage

Automatic optimization (recommended):

const project = XcodeProject.openLazy(path, {
  skipFullInflation: true,
  progressCallback: (message) => console.log(message)
});

Manual strategy selection:

const result = parseOptimized(contents, {
  progressCallback: (processed, total, stage) => { ... }
});

🎯 Parsing Strategy Selection

  • <5MB: Original JsonVisitor (fastest for small files)
  • 5-50MB: OptimizedJsonVisitor (eliminates object spread bottlenecks)
  • >50MB: StreamingJsonVisitor (chunked processing, memory management)

Breaking Changes

None - all optimizations are backward compatible. Existing XcodeProject.open() continues to work unchanged.

🧪 Testing

  • ✅ All existing tests pass
  • ✅ Successfully handles Test project (125K+ objects)
  • ✅ Performance improvements verified on small, medium, and large projects
  • ✅ Memory usage optimized across all file sizes

This enables parsing of previously impossible large Xcode projects while significantly improving performance for all project sizes! 🎉

🚀 Key Improvements:
- Eliminated object spread operations in JsonVisitor (40-50% speed boost)
- Added streaming parser for massive files (handles 94MB+ projects)
- Memory-efficient analysis that extracts metadata without full object tree
- Automatic strategy selection based on file size
- Enhanced XcodeProject.openLazy() with progress reporting
- Comprehensive test coverage for all optimization strategies

📊 Performance Results:
- 0.10MB files: 51% faster (7.80ms → 3.84ms)
- 94.81MB files: ∞x improvement (crash → 3.5s success)
- Memory usage: Managed 4GB peak for DoorDash project

✅ Backward compatible - all existing APIs work unchanged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant