Coming from android I'm surprised there's no optim...
# ktor
u
Coming from android I'm surprised there's no optimizer like
R8
applied when creating a release binary of ktor server Why? Isn't it free gains?
e
I would say libraries use obfuscation for hiding things in the first place on Android. Most optimisations would not be efficient without the final binary stage. And you don’t need an optimiser to find and delete dead code or resources.
u
I dont follow. Most of the work the optimizer does is removing unused code from libraries (so less classloading) and performance optimizations. Do you do this manually? I'd recon no. Also, what final stage binary?
h
Do you want to create an minified application or a minified library?
e
How do you know which code is not used in the library before adding it to a particular project? I might have a library that reads and writes to the server but you only use it for getting internet time (bad library design).
m
Such optimizers are used automatically when you create a release distribution. For desktop apps ProGuard is used and for web (wasmJs) binaryen is used. So, what are you actually missing?
u
@Eugen Martynov what? figuring it out is exactly what's the optimizer's job
@Michael Paus I'm talking about the server, sorry I'm just noticing I didn't specify
h
How? The api surface is big. Sure, R8 could just keep all public members, but that would not make a big difference for a library. R8 makes sense for applications with only a few (fun main) entrypoints that needs to include all dependencies, like a fat/uber jar.
u
I'm surprised this is news to you all, R8 has hardcore stuff in it and works very well. Only pitfall is reflection, as that cannot be analyzed statically, so one needs to provide keep rules, bit thats usually libraries job
h
It's not news.
u
Unless we are talking about different things
R8 keeps everything thats used amd removes all that is not. Whats the issue?
h
There is no issue
You only need to run r8 manually
With a custom Gradle task or a plugin
u
I know. My original question is why is this not common place on server - where with ktor the sales pitch is that it's small/starts fast/perfromant So where's the catch
h
Servers don't have a restricted environment, in the past. You deployed your big war/ear file on a server that also needs to have the big JVM, so there is no reason to shrink the jar from 100 MB to 80 MB because the JVM is/was about 150 MB or more. You can shrink your jar with r8 without any problems, but you also should shrink your JVM distribution, if you provide it, otherwise what's the point? When you create an Docker image, you should use jib instead. It does not shrink your jar nor your image, but it creates layered images so updating your application code only updates the classes layer and it is really fast instead of running a full R8 run invaliding all cached layers.
Also, there is no such huge performance impact. Yes there is one, but if you need to open the database connection or if you need to read some other remote resources your startup might be blocked anyway. A user application should feel responsive, but when your server startup takes 3 instead of 2 seconds, it's not a real deal (unless using a lambda function)
u
Well startup speed is just one aspect, there's memory usage and page trashing - but also just runtime perf, i.e. makes it go faster. Or do people just handwave it and say hotspot will fix it at runtime?
h
Well, you could just profile it, but I would not expect a big runtime performance impact.
u
on android it makes big difference
h
And the JVM/hotspot is very good with inlining
u
yea okay maybe that argument is obsolete but still, there's people trying native images for obvious reasons..it's just strange people would not alteast use this, I'm looking for R8g gralde plugin right now and can't find any, i.e. nobody cares
h
It is my plugin to create an r8 minified jar for an JVM application.
u
oh 😄
nice - have you ever tried it with ktor? I'd not dare to use it with spring boot because of all the reflection, but afaik ktor uses none (?)
a
Having run kotlin server applications for superbowls and such I can vouch for most optimizers not making any measurable difference against hotspot and well-written code. Granted we currently use a framework based on Vert.x for our server apps and ktor as http client. Graalvm native images do make a significant difference on memory footprint in particular but also performance. You also have to remember that apps have a huge mass of UI libraries/dependencies that are much larger than a server app with MANY libraries. And that code benefits much more from most optimizations.
h
Not with the server, but I planned to do it in the next days. I only use r8 to minified a client application using Ktor client. But Ktor does not really use reflection except same very rare cases.
u
@Anonymike but it does take warm up time doesn't it? Or that not an issue? or do you maybe use AOT profiles?
a
If you're app is truly busy and benefitting from the optimizations the warm up is so fast its not even noticeable. And hotspot kicks in much quicker than it used to. Start your web app up and literally refresh one page 20 times and you can watch it shrink. In a superbowl app that represents about 0.000001 seconds
Maybe less
At 50 - 60 page loads you'll likely be close to optimal for that code path
u
The superbowl solution, was that graalvm?
a
Not the primary web applications...we are really careful with where we use that because the reachability-metadata is a pain in the ass and can introduce runtime errors if its not perfect. We mainly use graalvm for smaller services where there is very little reflection and a lot of cpu work.
u
I see, so runtime perf optimizations are done at runtime, so the only difference maker would be startup time, and I'd apply R8 only if I care about it a lot - at which point you're saying I should go graalvm?
because tbh r8 and graalvm sounds like is the same thing in spirit, or trying to be
a
If your app is predictable, graalvm is awesome. The challenge with web apps is frameworks like Vert.x have a lot under the hood and you have to really be confident you've got that metadata covered. Quarkus is a great example as its meant for graalvm builds and a good way to go.
Ktor is much better in that regard as well.
For us, we have Jackson involved still which provides a lot of convenience but also means we have a lot of reflection going on. It sucks when you update a small library and a dependency of that 3 layers down changed something and is now missing metadata.
u
that's what I meant, that both need reflection guidance metadata in order not to break stuff, both make builds longer but that was the case on android as well, it used to be massive pain to build with proguard but over time the community adjusted, got used to it, and now libraries provide the metadata automatically, I have like 1 keep rule in my 1M LOC app
a
Exactly, which is why we don't use it for most apps, especially during the superbowl. We ran on a fraction of the servers that most teams required with straight jvm, hotspot, etc.
u
also graalvm is rather a extreme solution, now I have to think about cpu architectures and what not, r8 sounds like a nice middle ground
👍 1
but yea seeing java app launch in 20ms makes me happy 😄
a
Make sure you are using the -server flag and such though.
-server
tells the jvm to use hotspot more aggresively and I can't tell you how many apps I see not using it
Graalvm is fantastic for CLI application now too.
u
that's a flag of what exactly?
u
oh to run, I see
btw do you have intuition about graalvm'd java app vs say rust? (in usecases where rust servers are relevant)
a
haha yes, I'm sorry, I should have specified
We actually use rust and go for several things application
m
We use R8 for a JVM cli app where download size matters and I have considered trying it for the ktor backend but probably the perf/size gains are not worth the effort
The JVM build in Gradle is a bit cumbersome for this as well, there’s no debug-release buildtype split like there is on Android
u
but is using it on ktor an effort? does it still have reflection?
z
I use R8 for CLIs, it could also be used for Ktor. There are several reasons for not using it for Server JARs in general: • Diminishing Returns: server environments lack the strict memory constraints that make R8’s shrinking essential for mobile/client platforms. • Operational Friction: obfuscated code makes debugging and stack trace re-tracing unnecessarily difficult for the backend team. • Lack of Benefit: unlike client apps, server-side code does not require the obfuscation that R8 provides. You can try R8 for your Ktor with
-dontobfuscate
flag.