Is there a way to avoid object instantiation for a...
# getting-started
m
Is there a way to avoid object instantiation for a simple class like
data class Point(val x: Int, val y: Int)
? In an API with lots of operations on such `Point`s, there is lots of
Point
objects being created. Changing the API to operate on x and y parameters separately without a
Point
class avoids the overhead, but makes the API less clean. Is there some language feature that can help here? I am looking for something like inline classes, but for two member properties.
j
Value classes for multiple properties are not supported yet. In your specific case, you could work around it if you know the full dimension of the grid (and if it fits) by using a 1-dimensional index to represent the position in the 2D grid. EDIT: you could also just store these 2
Int
in the low and high bits of a
Long
, which doesn't require knowing the max width
Something like this for instance :
Copy code
const val GRID_WIDTH = 200 // or whatever is your grid size

value class Point(private val index: Int) {
    val x: Int get() = index % GRID_WIDTH
    val y: Int get() = index / GRID_WIDTH
}
m
Although I used
Int
in my example for simplicity, my
Point
actually takes
Double
coordinates. So I cannot do such an operation.
Maybe I can find another way to fit two
Double
values into a single variable somehow.
j
Although I used 
Int
 in my example for simplicity, my 
Point
 actually takes 
Double
 coordinates. So I cannot do such an operation.
And here goes the workaround.. out the window 😄
This workaround only works with int, where you can enumerate accurately those possibilities "row by row", but I'm not sure you can do something equivalent with doubles. Did you measure the impact of the extra
Point
objects though?
m
It's used during animations of multiple objects, there could be hundreds of
Point
objects created every frame. (But even if there was no impact this would be interesting to solve)
j
It's used during animations of multiple objects, there could be hundreds of 
Point
 objects created every frame
Still doesn't mean it's a problem. It really depends on how you use these objects (what their lifecycle is). For instance, if you use them as temporary local variables, they will be allocated on the TLAB and will be very cheap
But even if there was no impact this would be interesting to solve
I think multi-property value classes will come at some point (so the Kotlin team is already working on it). This will be especially true when project Valhalla comes in. In the mean time, we're stuck with regular classes or workarounds that manage to cram everything in a single field 😄 of course you can also pass the 2 values as argument to all functions, but that doesn't solve the problem for return values
m
Are you sure that the instantiation will be a performance problem? Modern VMs are pretty good at reusing small objects like that.
Of course you could create them all through a factory method and add some sort of cache in there, either caching all the points or just the most recently used.
But in essence, like I said in the previous comment, the VM is already doing something like that for all objects. My advice would be to go ahead with the data class and then, if performance becomes an issue, profile the application.
m
@Michael Böiers You are right, this is not a real issue but I was interested in the problem itself.
I managed to make a
Point(x: Double, y: Double)
value class. See it at your own risk: https://pl.kotl.in/camTTpjz5
😆 1
j
Btw I realized my initial solution could have just been storing the two ints in the high and low bits of a single long (without even knowing the grid size and using
%
and
/
)
I managed to make a 
Point(x: Double, y: Double)
 value class
Now the question is: aren't all those operations actually slower than just allocating and garbage-collecting
Point
instances?
m
Only a profiler can give you that answer.
👍 2
m
I'm not actually going to use it, but it was fun to implement.
I think multi-property value classes will come at some point
@Joffrey Is there a KEEP for that?
m
You can still try it later, I like that it‘s fully compatible with the straight-forward implementation.
j
There is this issue to track it: https://youtrack.jetbrains.com/issue/KT-1179 The initial KEEP for value classes also contains some information. You can start from this section: https://github.com/Kotlin/KEEP/blob/master/notes/value-classes.md#compiling-value-classes-the-single-field-restriction And carry on to the Valhalla part and the actual section about multiple fields just below.
👍 1
e
you can mux two Float into one Long, e.g.
value class FloatPair
generated by https://github.com/ephemient/kotlin-numeric but unfortunately Double is 64-bits and there is no 128-bit primitive so you can't pull the same trick
IMO Float is much more reasonable than "double with limited precision" that you wrote there
1
m
https://androidx.tech/artifacts/compose.ui/ui-geometry/1.0.0-beta02-source/androidx/compose/ui/geometry/Offset.kt.html I just found this while working on something unrelated, looks like what I did (except better). Maybe I wasn't so crazy after all.
e
https://github.com/androidx/androidx/blob/androidx-main/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/InlineClassHelper.kt packFloats and unpackFloat[12] do the same thing I do in FloatPair, which is to use the raw bit representation of the Float
❤️ 1
what you have with Point is some sort of self-invented floating point format but with decimal base and less precision…