Kotlin Native vs. GraalVM

What are the pros and cons of Graal and Kotlin Native for such a scenario?

I’ve only used Graal’s native-image and Kotlin/Native for some small test programs and benchmarks. For a recent example that’s pretty typical of my tests:

| Lang            | Opts              | Time    | Speed | Memory |
|-----------------+-------------------+---------+-------+--------|
| Kotlin (JVM)    |                   | 4441 ms |    1x | 102 MB |
| Kotlin (Native) | -opt              | 86.8 s  | 19.5x | 5.5 MB |
| Kotlin (Graal)  | -O3               | 27.7 s  | 6.24x | 261 MB |

So the Kotlin (JVM) does the best, by far, in all respects except for memory usage for low-memory programs, where the JVM gives you a +100MB (or +50MB if you try to limit it) just to pass ‘Go’. There’s a startup speed hit which would disqualify the JVM for CGI tasks, but for me it’s more like 100ms than 1s.

After that, Graal lets you keep your JVM-reliant code and it has shockingly bad performance (vs. my experience with native-image’s treatment of normal Java, even Java that’s transpiled from other languages), but still better performance than Kotlin/Native. At the cost of worse memory usage.

Kotlin/Native’s memory consumption here is exactly what you’d expect from a normal native-compiling language, like C or C++, but its performance is awful. And its performance is awful probably because it’s doing absolutely crazy stuff with memory. Just look at this ltrace output:

calloc(1, 29)                                                            = 0x21c2470
memcpy(0x21c2488, "He", 2)                                               = 0x21c2488
free(0x21c24a0)                                                          = <void>
calloc(1, 33)                                                            = 0x21c24a0
memcpy(0x21c24b8, "Hell", 4)                                             = 0x21c24b8
free(0x21c2470)                                                          = <void>
calloc(1, 41)                                                            = 0x21c24d0
memcpy(0x21c24e8, "Hello, 1", 8)                                         = 0x21c24e8
free(0x21c24a0)                                                          = <void>
calloc(1, 40)                                                            = 0x21c24a0
memcpy(0x21c24b8, "Hello, 126", 10)                                      = 0x21c24b8
free(0x21c24d0)                                                          = <void>
calloc(1, 24)                                                            = 0x21c2390
calloc(1, 19)                                                            = 0x21c2510

That’s coming from puts("Hello, $it"). Yes that’s a static string, "Hello, ", that is getting chopped up and repeatedly memcpy’d for some reason.

So if you want to use Kotlin for CGI, I’d suggest you write a server instead, but anyway the JVM configuration is still probably be the best option for you, even for CGI. At least for now. In the future Kotlin/Native will probably be the best option, with minimal start-up costs and the most reasonable memory usage, at the cost of your having to use different libraries.

8 Likes