dyld takes too long to load frameworks

Originator:stepan.hruda
Number:rdar://22920190 Date Originated:30-Sep-2015
Status:Open Resolved:
Product:iOS Product Version:
Classification: Reproducible:
 
Summary:
Frameworks shipped with an app bundle take too long to load on first launch. This makes them unusable for apps with more than a handful of dependencies – a huge issue since you can only ship Swift dependencies using frameworks, not static libraries.

I created a repo with a sample app and a comprehensive description of the whole problem: https://github.com/stepanhruda/dyld-image-loading-performance

Steps to Reproduce:
See sample repo.

Expected Results:
Frameworks don't add several seconds to a bundle's launch time.

Actual Results:
Frameworks add several seconds to a bundle's launch time.

Version:
Happens in iOS 8, it's even worse in iOS 9

Notes:


Configuration:
If the executable includes the same symbols linked statically, it there's no performance hit.

Comments

More investigation

I did some more investigation to pin the issue down.

dyld can profile the initialization time when passed DYLD_PRINT_APIS and DYLD_PRINT_STATISTICS environment variables. In the example run below, 88.5% of the time (by the way, 3.8 seconds to launch on an iPad Air 2!) is attributed to “total images loading time”.

total time: 3.8 seconds (100.0%) total images loaded: 284 (222 from dyld shared cache) total segments mapped: 185, into 4911 pages with 636 pages pre-fetched total images loading time: 3.4 seconds (88.5%) total dtrace DOF registration time: 0.11 milliseconds (0.0%) total rebase fixups: 154,832 total rebase fixups time: 74.53 milliseconds (1.9%) total binding fixups: 265,534 total binding fixups time: 230.05 milliseconds (5.9%) total weak binding fixups time: 0.47 milliseconds (0.0%) total bindings lazily fixed up: 0 of 0 total time in initializers and ObjC setup: 136.78 milliseconds (3.5%) libSystem.B.dylib : 90.35 milliseconds (2.3%) libBacktraceRecording.dylib : 8.23 milliseconds (0.2%) libc++.1.dylib : 0.02 milliseconds (0.0%) CoreFoundation : 0.76 milliseconds (0.0%) CFNetwork : 0.14 milliseconds (0.0%) vImage : 0.00 milliseconds (0.0%) libGLImage.dylib : 0.10 milliseconds (0.0%) QuartzCore : 0.01 milliseconds (0.0%) libViewDebuggerSupport.dylib : 0.01 milliseconds (0.0%) libswiftCore.dylib : 1.59 milliseconds (0.0%) libTelephonyUtilDynamic.dylib : 0.00 milliseconds (0.0%) CoreTelephony : 0.02 milliseconds (0.0%)

I profiled/tracked the “total images loading time” in dyld’s source, and it seems that what’s taking the vast majority of time is phase 6 of loading a Mach-O image, more specifically the ImageLoaderMachO::loadCodeSignature function, even more specifically loading the signature from disk using fcntl:

fsignatures_t siginfo; siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of CD in mach-o file siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD int result = fcntl(fd, F_ADDFILESIGS, &siginfo);

This seems like something that could be made more efficient?

By stepan.hruda at Oct. 9, 2015, 1:27 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!