Consider the following C and Objective-C programs that perform 64 KiB allocations in a tight loop: ``` #include <JavaScriptCore/JavaScriptCore.h> int main () { JSContextRef context = JSGlobalContextCreate(NULL); JSStringRef source = JSStringCreateWithUTF8CString( "for (let i = 0; i < 1e6; i++) new Uint8Array(1024 * 64)" ); JSEvaluateScript(context, source, NULL, NULL, 1, NULL); } ``` ``` #include <JavaScriptCore/JavaScriptCore.h> int main () { JSContext *context = [[JSContext alloc] init]; [context evaluateScript:@"for (let i = 0; i < 1e6; i++) new Uint8Array(1024 * 64)"]; } ``` When running these on my machine, I'm observing an 8x slowdown compared to running the same allocation loop in either Safari or using the jsc helper from the builtin JavaScriptCore framework (/System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Helpers/jsc) and I cannot seem to figure out why. Both programs were compiled with clang using -O3.
Does your test program allow JIT? https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-jit
I think so, yes, but I'm unsure how to verify that's it's actually working: ``` $ codesign --display --entitlements - a.out Executable=<cwd>/a.out [Dict] [Key] com.apple.security.cs.allow-jit [Value] [Bool] true ``` Code signed with the following command: ``` $ codesign --force --options runtime --sign <key> --entitlements entitlements.plist a.out ``` I don't know if it provides any clues, but reducing the size of each allocation to just 1 KiB instead of 64 KiB makes my test program perform similar to Safari and jsc. What kind of optimisations would the JIT be able to do for larger allocations?
Not sure, CC'ing some folks who would know. FWIW, the jsc binary on macOS has more JIT related entitlements, but others don't seem to be documented.
Tried matching the jsc entitlements with the exception of com.apple.private.verified-jit, but no dice I'm afraid: ``` $ codesign --display --entitlements - a.out Executable=<cwd>/a.out [Dict] [Key] com.apple.security.cs.allow-jit [Value] [Bool] true [Key] com.apple.security.cs.jit-write-allowlist [Value] [Bool] true [Key] com.apple.security.cs.single-jit [Value] [Bool] true ```
<rdar://problem/105001996>
Can you run this with `JSC_dumpDisassembly=1` environment variable? If JIT code is generated from that, then JIT is enabled. If not, JIT is not enabled.
Created attachment 465047 [details] Output generated by JSC_dumpDisassembly
I've uploaded an attachment with the generated output, let me know if there's anything else I can provide!
Do you need anything more from me on this?
Hi Kasper, your dump shows that you are getting JIT support. So, this is not an issue with the JIT being disabled. Next steps: 1. How are you measuring the 8x slowdown? Your description below didn't describe this. Please describe this so that we can make sure that you're comparing equivalent things. 2. My gut feeling is that there might be GC activity at play. Try running your executable and jsc with JSC_logGC=1, and see if there's a difference in GC activity. 3. Run Instruments profiler and collect CPU profiles for runs with your executable and with jsc. Then compare to see if there's a difference in the profiles. Based on the data you get from those, we may be able to give you more advice. Thanks.
Created attachment 466474 [details] CPU profile of `JavaScriptCore.framework/Versions/Current/Helpers/jsc`
Created attachment 466475 [details] CPU profile of `a.out`
Created attachment 466476 [details] GC logs for `JavaScriptCore.framework/Versions/Current/Helpers/jsc`
Created attachment 466477 [details] GC logs for `a.out`
Thanks for detailing the additional steps! 1. I've run both my compiled programs (a.out) and the jsc helper (/System/Library/Frameworks/JavaScriptCore.framework/Versions/Current/Helpers/jsc) through `time` and the slowdown I reported was based on those timings. For the jsc helper, I stuffed the loop in a JavaScript file and passed that to jsc. 2. I've attached the GC logs for both the jsc helper and one of my programs. I think it's safe to say that the GC is on overdrive in my programs; I'm counting 215,577 occurrences of "GC END" for my programs, but only 5,874 occurrences in the case of jsc. 3. I've attached the CPU profiles for both the jsc helper and one of my programs. They look very similar to me, except for the total durations, and I'm unsure where to look specifically for GC activity.
Is there anything else I can do to help identify the cause of this?
This went away after upgrading to macOS 14 so I assume whatever caused this was fixed. Marking as resolved.