Is there any reason for using Pair or Triple instead of custom classes for performance reasons? In particular, if I pass back an instance of Pair or Triple from a function and decompose it at the calling site, will this be more likely to get inlined by the Kotlin compiler?
fun doIt(): Pair<Float, Float> {
return Pair(4.2f, 42.0f)
}
fun callIt() {
val (x, y) = doIt()
println(x, y)
}
Is there a guide for performance considerations when developing a performance-critical application in Kotlin/JVM?
Not really, no. I don’t think they do anything that a custom class couldn’t also do. And a data class already gives you pretty much all the same features.
Reasons to use Pair and Triple include:
They’re already in the standard library, so you don’t have to write them yourselves. (Though, as an alternative could be a one-liner, that’s not much of a benefit.)
Most Kotlin developers will know them, reducing the learning curve very slightly.
They may be used by other code, potentially encouraging the JVM to compile/optimise them a little sooner.
There may be cases where you need them for interoperability with the standard library or third-party libraries.
Reasons to use a custom class include:
It can be self-documenting. You can probably find property names that will be much more meaningful in context than ‘first’ and ‘second’; you can probably find a more meaningful class name too.
It’s more flexible. You can easily add things like logging if you need to. You can also add computed properties or other behaviour where that makes sense.
So no significant performance impact either way.
Though if you’re really concerned about performance, you’re usually far better off looking at things like reducing or parallelising network calls, picking efficient algorithms (both in terms of CPU and memory), and making the code as simple and clear as possible. Then, if needed, profiling to see where it’s taking time. Reducing memory allocations (such as temporary strings) can help too (even if the allocation is cheap, garbage collection isn’t) — though I think modern JVMs lower the impact. IME, it’d be pretty rare for low-level optimisations such as you’re seeking to make a significant impact.
If you’re really really sure that perf matters here though, here’s a trick you can do with inline functions:
import kotlin.contracts.*
inline fun doIt(result: (Float, Float) -> Unit) {
contract {
callsInPlace(result, InvocationKind.EXACTLY_ONCE)
}
result(4.2f, 42.0f)
}
fun main() {
val x: Float
val y: Float
doIt { a, b -> x = a; y = b }
println(x)
println(y)
}
Thanks for the replies. The big problem in my project is garbage collection pressure, so I want to reduce the number of temporary objects allocated. If the returned Pair or Triple could be inlined such that the Pair or Triple is never even allocated, that might be a benefit, or at least that was what I was thinking.
I’m also wondering whether the JVM can allocate things on the stack. For instance, if a class is instantiated and the instance stays local and the reference is never passed on, it should be safe to allocate it on the stack instead of on the heap, right? Knowing whether the JVM can do this or not would give me an idea what’s crucial for lowering garbage collection pressure. For instance, I sometimes keep temporary variables as private member variables inside the class in order that I can reuse the same objects later, but I wonder if this actually helps much.
Yes, temporary objects can be a significant performance issue.
And yes, I understand that some modern JVMs can do escape analysis and allocate objects (or rather, their properties) on the stack instead of the heap when safe to do so. But I haven’t done any analysis of how aggressively they do so, nor how much difference it might make in practice.
For reference, the last time I did some serious optimisation, the biggest wins I found were:
Replacing methods that returned a string (such as toString()) with methods that accepted a StringBuilder and appended their text to that. That can hugely reduce the number of temporary strings, especially when you’re generating a string from a whole object tree.
Similarly, replacing methods that returned a List with ones that append to a given MutableList.
Reducing the number of HashMaps, and especially ConcurrentHashMaps. Don’t get me wrong; those are hugely useful classes — but in our case, we had millions of them in memory, all with the same tiny set of keys. I ended up writing my own Map implementation based on a shared List of keys and an individual List of values, and it saved many 100s of MBs.
But of course, this all depends on the nature of your application and how it’s written.
If you’re concerned about memory usage, it’s well worth taking some full heap dumps and then analysing them to see what your memory is actually being used for and where it’s being allocated. Then you can use that to look for ways to reduce it.