Hacker News new | ask | show | jobs
by pcwalton 923 days ago
I generally feel that for a language like Java/C#, which Swift is, you really need a JIT and a tracing, moving GC to get optimal performance. Apple has pushed the COM-like model of static code generation and non-moving reference-counted GC about as far as it can go at this point (impressively far--the compiler heroics in Swift are incredible), and it still can't quite make it to Java/C#, which end up having a simpler implementation than that of Swift in the end. The fact is that the ability to dynamically observe the behavior of the program and recompile with optimizations on the fly is just too powerful to give up.

Perhaps aggressive PGO could help to close some of the gap. The problem is that PGO requires effort on the part of developers to write comprehensive test cases and it's not clear how to scale that workflow. Large companies can write representative test cases and scale PGO on their performance-sensitive services, but your average iOS app developer won't be willing to do that.

2 comments

Initially I was kind of disappointed of how AOT evolved on Android verus Windows Phone, then I came to realise Google was actually right.

Whereas Windows Phone would use Windows Store to AOT compile the application, Android would AOT on device.

Thus initially, it felt like using the tiny phones for that would be a bad decision, and it was, as the JIT was reintroduced 2 versions later (Android 7).

However, it was a mix and match of all modes, interpreter hand written in Assembly for quick startup, a JIT with PGO data gathering, AOT compiler with feedback loop from PGO data, latter on, sharing PGO data across devices via Play Store services.

This mix of JIT/AOT with PGO sharing across everyone, brings the optimal execution flow that a given application will ever get, allows reflection and dynamic loading to still be supported, and AOT compiler toolchain can have all time of the world to compile on the background.

It's most likely just reference counting and the way abstractions work in Swift (dynamic dispatch?). In particular, it still loses to C# even if you use AOT for the latter, especially in multi-threaded scenarios.

HotSpot C2 and .NET Dynamic PGO-optimized compilations first and foremost help to devirtualize heavy abstractions and inline methods that are unprofitable to inline unconditionally under JIT constraints, with C2 probably doing more heavy lifting because JVM defaults to virtual calls and .NET defaults to non-virtual.

With that said, I am not aware of any comprehensive benchmarking suites that would explore in-depth differences between these languages/platforms for writing a sample yet complex application and my feedback stems mostly from microbenchmark-ish workloads e.g. [0][1].

Performance aside, I do want to compliment Swift for being a pleasant language to program in if you have C# and Rust experience.

[0] https://github.com/ixy-languages/ixy-languages (2019)

[1] https://github.com/jinyus/related_post_gen (2023)

On the case of Java, it isn't only HotSpot, there are several other options, and in the case of OpenJ9 and Azul, cloud JIT also plays a role for cloud workloads.

I also like Swift, if anything it helped to bring back the pressure that AOT compilation also matters.