Hello,
I have an iOS app that does some heavy processing.
On a specific data set, the run time in iOS simulator is ~240 seconds.
When I run the same code, on the same data, in a profiler, it only takes ~20 seconds.
I have verified that the processed output is the same, so the same work was performed.
The Schema for Run
is set to Release
, the same as for Profile
.
What could explain such a difference? Did I mess up some options? Where should I look?
This is pretty hard to answer without knowing more. If you're absolutely sure that in both cases your code is compiled in release mode and you've eliminated that as a factor, is there any code can you share?
If you’re using Task
or related APIs, its thread pool only has one thread on the Simulator.
This hasn't been true since Xcode 14.3 or so.
Good to know, though I don’t use the simulator much anymore for other reasons.
Have you tried running the binary directly, without going through Xcode and attaching a debugger? One example of why this could matter: your debugger could have breakpoints with conditions set up which never actually break, but which slow down execution as they're executed.
The big difference in runtime could be due to the iOS simulator vs. real device execution, the simulator doesn’t always reflect real-world performance, especially for CPU-intensive tasks. Also, when running in Profile mode, extra optimizations like LLVM optimizations and Just-In-Time (JIT) compilation may kick in, making execution much faster.
i didn’t know LLVM optimizations happened at runtime, or that Swift had Just In Time compilation. how effective are these optimizations today?
It does not.
I had a similar problem with a macOS app recently. I found the only difference between Release and Profile builds were these compiler flags:
-profile-generate -profile-coverage-mapping
Those were set on Release builds but not Profile builds even though I had ENABLE_CODE_COVERAGE = NO in the build settings.
I haven't found a solution, but I ended up building for Profiling and then running without building. Archived builds are fine.
Solved, but unexplained.
Took me many hours trying to move pieces of code to a test project to reproduce the defect, with no luck.
Finally, created a new project and move ALL of my many files there. NO performance degradation!
I've opened up the *.xcodeproj
files and diffed all of its parts. I suspected project.pbxproj
that did have some differences because of the way I grouped my files, but resolving all of them didn't change anything.
I had a Test
target in the original project, but removing it also didn't change anything.
Finally I noticed <Testables>
element in <TestAction>
in ProjName.scscheme
file. Removing it fixed everything!
I am surprised that performance difference was 10 to 20 times, depending on the data sample.
Can anyone explain or guess what was the issue?
Here is that XML fragment:
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "73A28E972D67C93F004C268E"
BuildableName = "ProjNameTests.xctest"
BlueprintName = "ProjNameTests"
ReferencedContainer = "container:ProjName.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
You might want to check out this thread if it is applicable for your case:
(FB13431597)
Thank you @hassila ! That was quite a story to read.
Building only for the active Arch save me 0.2 seconds, eliminating these two lines from the build log:
ExecuteExternalTool /Applications/Xcode.app/Contents/Developer/usr/bin/actool --version --output-format xml1
ExecuteExternalTool /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc --version
Disabling Code Coverage Support for Release saved another 0.1
I have reduced the <Testables>
element shown in my previous post to:
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference>
</BuildableReference>
</TestableReference>
</Testables>
to cause 10 TIMES slowdown of the Release run on the Simulator.
This is, again, in the <TestAction>
element of the scheme, so I have no idea why it affects Run.