App runs 2x slower when not launched by Xcode debugger

Hi! I have a macOS app and it uses around 2x more CPU when it's not launched by the Xcode debugger. This is not about build optimization or anything. I can use the exact same executable, and when I launch it directly by double clicking it in Finder it's uses 2x as much CPU as when I launch the executable through the Debug > Debug Executable... option in Xcode.

I did some CPU profiling in Instruments, and there is only one difference I could find so far when the app is launched by the Xcode debugger:

There is an entry in the trace named CA::Context::commit_transaction(CA::Transaction*, double, double) | Quartz Core. This entry always appears. But - If, and only if the app is not launched by the Xcode debugger, this entry has a child entry _dispatch_async_f_slow which itself has children that seem to be related to dispatch queue "introspection". It looks kind of like this:

v CA::Context::commit_transaction(CA::Transaction*, double, double)
    v _dispatch_async_f_slow
        > _dispatch_introspection_queue_item_enqueue_hook
          _dispatch_trace_item_push_internal
          dispatch_introspection_queue_item_get_info
        > _dispatch_introspection_queue_item_enqueue

Now this alone doesn't account for the huge difference in CPU usage, but my best theory is that this dispatch queue introspection stuff is done all over the app, and cumulatively leads to the 2x slowdown.

I'm running
Xcode Version 15.1 (15C65)
macOS 14.1.2 (23B92)
M1 MacBook Air

If you have any clue how to prevent this slowdown when the Xcode debugger is not attached please let me know. Thank you so much!

Replies

What is the frame rate in each case?

Does the app actually appear slower, or is it just the reported CPU usage that you are concerned about?

  • On my computer it's still running at full speed. But I heard about frame drops from users, and I think it might be related. Also it's an app that constantly runs in the background so it should be as efficient as possible to conserve battery etc.

Add a Comment

Sorry, please disregard — I read your question backwards. If you uncheck the “Enable backtrace recording” checkbox under Product → Scheme → Edit Scheme… → Run → Options → Queue Debugging, does that reduce the CPU usage? If you’re using GCD heavily, you might see some overhead from this feature, which automatically records back traces whenever a block is submitted to a dispatch queue. When debugging, this can be useful since you can see where the currently-running block on a queue was enqueued from.

  • Thanks for chiming in! I am using GDC pretty heavily. If I can do anything to help get to the bottom of this, please let me know.

  • As far as I can tell, the only thing that could be doing this is the backtrace recording feature of Xcode (or another similar debugging tool). The function call that sets up these slower function calls is dispatch_introspection_hooks_install — maybe there’s something on your end that’s calling that?

  • I searched my project source and there is nothing on my end that is calling dispatch_introspection_hooks_install. I don't think I ever heard about dispatch introspection before today, so I don't think I wrote any code that interacts with it. I also played around some more with the CPU profiler and it looks like almost everything just takes way more CPU cycles when the app isn't launched by Xcode debugger. I'm not totally sure if I'm reading the stuff right.

Add a Comment

I discovered that the environment variables are different when the app is launched by the Xcode debugger. Maybe the culprit is there?

Environment vars when the app is launched by double clicking in Finder:

Process environment: {

    "COMMAND_MODE" = unix2003;

    HOME = "/Users/Noah";

    LOGNAME = Noah;

    PATH = "/usr/bin:/bin:/usr/sbin:/sbin";

    SHELL = "/usr/local/bin/fish";

    "SSH_AUTH_SOCK" = "/private/tmp/com.apple.launchd.Wsb9vfQDBY/Listeners";

    TMPDIR = "/var/folders/8n/g6k5rgwx1cb4nbkj6zczsvvm0000gn/T/";

    USER = Noah;

    "XPC_FLAGS" = 0x0;

    "XPC_SERVICE_NAME" = "application.com.nuebling.mac-mouse-fix.helper.125350572.125350577";

    "__CFBundleIdentifier" = "com.nuebling.mac-mouse-fix.helper";

    "__CF_USER_TEXT_ENCODING" = "0x1F5:0x0:0x0";

}

Environment vars when the app is launched by the Xcode debugger: (With all the debugging options I could find in the build scheme turned off, to hopefully make this a bit more readable.)

Process environment: {

    "CA_ASSERT_MAIN_THREAD_TRANSACTIONS" = 0;

    "CA_DEBUG_TRANSACTIONS" = 0;

    "CFLOG_FORCE_DISABLE_STDERR" = 1;

    "COMMAND_MODE" = unix2003;

    "DYLD_FRAMEWORK_PATH" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release:/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release/PackageFrameworks";

    "DYLD_LIBRARY_PATH" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release";

    HOME = "/Users/Noah";

    "IDE_DISABLED_OS_ACTIVITY_DT_MODE" = 1;

    "LLVM_PROFILE_FILE" = "/dev/null";

    LOGNAME = Noah;

    LaunchInstanceID = "6A4DBAB9-D955-4D89-A2FC-17B2BA3E7D1A";

    MallocNanoZone = 1;

    NSUnbufferedIO = YES;

    "OS_ACTIVITY_TOOLS_OVERSIZE" = YES;

    "OS_ACTIVITY_TOOLS_PRIVACY" = YES;

    PATH = "/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin";

    PWD = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release/Mac Mouse Fix.app/Contents/Library/LoginItems";

    SECURITYSESSIONID = 186ac;

    SHELL = "/usr/local/bin/fish";

    "SQLITE_ENABLE_THREAD_ASSERTIONS" = 1;

    "SSH_AUTH_SOCK" = "/private/tmp/com.apple.launchd.Wsb9vfQDBY/Listeners";

    TMPDIR = "/var/folders/8n/g6k5rgwx1cb4nbkj6zczsvvm0000gn/T/";

    USER = Noah;

    "XPC_FLAGS" = 0x0;

    "XPC_SERVICE_NAME" = "application.com.apple.dt.Xcode.122494560.122619508";

    "__CFBundleIdentifier" = "com.apple.dt.Xcode";

    "__CF_USER_TEXT_ENCODING" = "0x1F5:0x0:0x0";

    "__XCODE_BUILT_PRODUCTS_DIR_PATHS" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release";

    "__XPC_DYLD_FRAMEWORK_PATH" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release";

    "__XPC_DYLD_LIBRARY_PATH" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release";

    "__XPC_LLVM_PROFILE_FILE" = "/dev/null";

}

Update for last comment: I tried to manually apply all the environment variables that are present when the app is launched via the Xcode debugger, to see if that helps. (It didn't) To do this, I created a bash script which exports all the environment variables listed above, and then ran the bash script with the source command. Then I opened the app with the open -a pathToApp command. Here are the resulting environment variables retrieved from inside the app:

Process environment: {
    0 = "";
    0x0 = "";
    1 = "";
    186ac = "";
    "CA_ASSERT_MAIN_THREAD_TRANSACTIONS" = 0;
    "CA_DEBUG_TRANSACTIONS" = 0;
    "CFLOG_FORCE_DISABLE_STDERR" = 1;
    COLORFGBG = "15;0";
    COLORTERM = truecolor;
    "COMMAND_MODE" = unix2003;
    HOME = "/Users/Noah";
    "IDE_DISABLED_OS_ACTIVITY_DT_MODE" = 1;
    "ITERM_PROFILE" = "p10k Custom Darkmode Noah's version(2)";
    "ITERM_SESSION_ID" = "w0t0p0:9561FA44-EB67-4A70-87FD-CDE817C2142D";
    "LC_CTYPE" = "UTF-8";
    "LC_TERMINAL" = iTerm2;
    "LC_TERMINAL_VERSION" = "3.4.22";
    "LLVM_PROFILE_FILE" = "/dev/null";
    LOGNAME = Noah;
    LaunchInstanceID = "6A4DBAB9-D955-4D89-A2FC-17B2BA3E7D1A";
    MallocNanoZone = 1;
    NSUnbufferedIO = YES;
    Noah = "";
    "OMF_CONFIG" = "/Users/Noah/.config/omf";
    "OMF_PATH" = "/Users/Noah/.local/share/omf";
    "OS_ACTIVITY_TOOLS_OVERSIZE" = YES;
    "OS_ACTIVITY_TOOLS_PRIVACY" = YES;
    PATH = "/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin";
    "PNPM_HOME" = "/Users/Noah/Library/pnpm";
    PWD = "/Users/Noah/Desktop/mmf-stuff/mac-mouse-fix";
    SECURITYSESSIONID = 186ac;
    SHELL = "/usr/local/bin/fish";
    SHLVL = 1;
    "SQLITE_ENABLE_THREAD_ASSERTIONS" = 1;
    "SSH_AUTH_SOCK" = "/private/tmp/com.apple.launchd.Wsb9vfQDBY/Listeners";
    TERM = "xterm-256color";
    "TERM_PROGRAM" = "iTerm.app";
    "TERM_PROGRAM_VERSION" = "3.4.22";
    "TERM_SESSION_ID" = "w0t0p0:9561FA44-EB67-4A70-87FD-CDE817C2142D";
    TMPDIR = "/var/folders/8n/g6k5rgwx1cb4nbkj6zczsvvm0000gn/T/";
    USER = Noah;
    "XPC_FLAGS" = 0x0;
    "XPC_SERVICE_NAME" = "application.com.nuebling.mac-mouse-fix.helper.125376249.125376254";
    YES = "";
    "__CFBundleIdentifier" = "com.nuebling.mac-mouse-fix.helper";
    "__CF_USER_TEXT_ENCODING" = "0x1F5:0x0:0x0";
    "__XCODE_BUILT_PRODUCTS_DIR_PATHS" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release";
    "__XPC_DYLD_FRAMEWORK_PATH" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release";
    "__XPC_DYLD_LIBRARY_PATH" = "/Users/Noah/Library/Developer/Xcode/DerivedData/Mouse_Fix-ahqbyzbmudhlrygcyeksnrzoaapt/Build/Products/Release";
    "__XPC_LLVM_PROFILE_FILE" = "/dev/null";
    unix2003 = "";
}

As mentioned, this didn't help. I'm just documenting what I tried to hopefully give some ideas as to what might be going wrong.

My conclusion from this is that the problem is probably not about environment variables. I have no clue what else it might be though.

I did some more testing, and there seems to be a randomness involved. When the app is launched normally it always takes 40% CPU, but on some launches it will take up 35% CPU. This might be just measuring fluctuations. But when the app is launched via Xcode debugger, for almost all launches, it consistently takes up 20% CPU, but for some launches I've also seen it take up 35% CPU or even 40% CPU.

I don't think this is just measuring fluctuations, because for testing I'm doing the same action continuously for like 20 seconds, and during that time it will consistently take up 20%, or 40% CPU. And these CPU usages only seem to change when the app is relaunched.

It's very confusing. There's either some randomness or I don't understand the pattern. The best idea I have right now is that the app will sometimes run on the efficiency cores of my Mac and sometimes run on high performance cores. And if it runs on efficiency cores, the CPU usage will be shown as higher because the cores aren't as powerful.

But I have no clue really. I will give up on this now, unless it causes any definite usability issues. Thank you for your feedback.

I have one more update just to document things:

The concrete thing where the issues I discussed in this thread occurred was the Click and Drag to 'Scroll & Navigate' feature in Mac Mouse Fix 3 Beta 7. The source code for the app is freely available if anybody wants to try and reproduce this.

My tests consisted of launching the 'Mac Mouse Fix Helper' app via the Xcode debugger, or alternatively by double clicking it in Finder or toggling the 'Enable Mac Mouse Fix' switch in the 'Mac Mouse Fix' app. (If you want to reproduce this, make sure that the 'Mac Mouse Fix Helper' app is properly embedded in the Mac Mouse Fix app. It won't run correctly if it's launched from outside the 'Mac Mouse Fix' bundle).

After launching the 'Mac Mouse Fix Helper' app, I held down the button assigned to the 'Scroll & Navigate' feature, and then wiggled the mouse back and forth at a consistent speed for an extended period of time. Then I looked at the CPU usage in Activity Monitor.

When 'Mac Mouse Fix Helper' was launched by the Xcode debugger, the CPU usage was around 20%, when it was launched another way, the CPU usage was around 40%. A huge difference!


Since the last reply I've done some more testing. My findings were that it seems like certain code runs faster when launched by the Xcode debugger, and other code runs slower.

I did the same tests I did for the "Scroll & Navigate" feature and repeated them for the Click and Drag for "Mission Control & Spaces" feature. Here, things were slightly faster when not launched by the debugger. Wiggling the mouse used around 8% CPU when launched by the debugger, and around 7% when launched normally. As a reminder: the "Scroll & Navigate" feature used around 20% CPU when launched by the debugger and 40% CPU when launched normally. So the performance characteristics were completely different! (And I’ve reproduced this dozens of times on my end)

Now this was interesting to test, because both 'Scroll & Navigate' as well as 'Mission Control & Spaces' use partially the same code to process mouse-moved inputs. So What I did was progressively disable different modules of code from both the 'Scroll & Navigate' input processing chain as well as the 'Mission Control & Spaces' input processing chain, until the were doing the exact same thing and calling the exact same code. I did this to try and analyze which of the modules of the input processing chain cause this weird performance characteristic.

And the result is: It seems like the initial processing module (which both of the 'Mission Control & Spaces' as well as the 'Scroll & Navigate' feature have in common) runs around 50% faster when launched by the Xcode debugger vs when it is launched normally. This module doesn't do much, except receive mouse-moved events from a CGEventTap, dispatch to a queue, and do a few simple operations that should be fast. At this point, the processing chains for the 'Mission Control & Spaces' feature and the 'Scroll & Navigate' feature diverge. The further processing for the 'Mission & Spaces' feature seems to run faster when not launched by the debugger, which seems to balance out initial processing module running 50% slower when not launched by the debugger. Overall, 'Mission Control & Spaces' is slightly faster when not launched by the debugger, which is how it should be. However, for 'Scroll & Navigate', it looks different. All the processing modules after the initial one only seem to exacerbate the slowness of not being not launched by the debugger. To the extent, where the whole processing chain for 'Scroll & Navigate' runs 2x slower when not launched by the debugger.

So it seems that only certain code runs slower when the debugger is not attached. I don't know what code exactly has these weird performance characteristics or why, but I can consistently reproduce this on my end.

Next I tried to find a minimal amount of code that has these weird performance characteristics. I found that when I simply create an event tap that listens to mouse moved events (but does absolutely nothing with them) inside applicationDidFinishLaunching:, then that simple code already runs way faster when app is launched by the debugger vs when it's launched normally. When wiggling the mouse, it was using around 2.5% CPU when launched by the debugger, and around 5% CPU, when launched another way. HOWEVER, when I made a new, empty Xcode project and did the same thing - just set up an eventTap inside applicationDidFinishLaunching: that listens to mouseMoved events - then these weird issues didn’t occur. In the fresh project, wiggling the mouse used around 5% in Activity Monitor in every situation.

So I thought, ‘maybe it’s about the build settings’ so I tried deleting everything from my project except for the code that sets up the eventTap in applicationDidFinishLaunching:. I deleted everything from the project, except the build settings and the event tap. And then the weird performance characteristics went away! It was always using 5% CPU when wiggling the mouse. Just like the fresh project.

I have absolutely no clue what’s going on. But if anybody knows more or wants to try to reproduce this I’d be very grateful.

Sorry for spamming up this thread, I guess I'm always deciding that I will quit investigating now, then I documenting my findings, but then I keep investigating in the end, so this is a bit messy.

Were you unintentionally compiling for ARM and running on x86?

  • Nope I'm on ARM, also not running through Rosetta or anything

Add a Comment