ursus
12/06/2025, 5:57 PMclass Particles(
val positions: Array<androidx.compose.ui.geometry.Offset>,
val velocities: Array<androidx.compose.ui.geometry.Offset>
)
For educational purposes, I tried a SoA of Offsets, but looking at byte code it autoboxes, so no performance gains.
Is there a way to convince the compiler to give me LongArray under the hood? (The type is convenient, ideally I'd not like to lose it)Sergey Y.
12/06/2025, 6:18 PMLongArray with direct raw values of Offset. To access them, you’ll need to use packing and unpacking functions, which are publicly available.ursus
12/06/2025, 6:22 PMOffset(val packedValue: Long), where the instantiation would be optimized away?Sergey Y.
12/06/2025, 6:23 PMSergey Y.
12/06/2025, 6:23 PMursus
12/06/2025, 6:24 PMclass Particles(
private val positions: LongArray
) {
fun positionAt(index): Offset = Offset(positions[index])
}
something like thisSergey Y.
12/06/2025, 6:26 PMromainguy
12/06/2025, 6:27 PMursus
12/06/2025, 6:28 PMromainguy
12/06/2025, 6:28 PMursus
12/06/2025, 6:28 PMromainguy
12/06/2025, 6:28 PMromainguy
12/06/2025, 6:29 PMromainguy
12/06/2025, 6:29 PMromainguy
12/06/2025, 6:29 PMursus
12/06/2025, 6:31 PMoffset + offset, makes math neater
but yes ultimately I'll decompose it, just seeing how much a difference a packed value class vs decomposed float arrays makeromainguy
12/06/2025, 6:32 PMromainguy
12/06/2025, 6:32 PMromainguy
12/06/2025, 6:33 PMromainguy
12/06/2025, 6:34 PMursus
12/06/2025, 6:34 PMOffsetArray would be nice as well 😄 to avoid boxing and actually benefitromainguy
12/06/2025, 6:35 PMromainguy
12/06/2025, 6:36 PMursus
12/06/2025, 6:36 PMVelocity 😄 ehromainguy
12/06/2025, 6:36 PMursus
12/06/2025, 6:37 PMromainguy
12/06/2025, 6:38 PMursus
12/06/2025, 6:39 PMfastXYZ() functions, why? will they get upstreamed to kotlin if they're simply faster?romainguy
12/06/2025, 6:42 PMromainguy
12/06/2025, 6:42 PMursus
12/06/2025, 6:42 PMromainguy
12/06/2025, 6:43 PMromainguy
12/06/2025, 6:43 PMromainguy
12/06/2025, 6:44 PMursus
12/06/2025, 6:44 PMfaster sqrt . is that the best jvm can do?
if I dont sqrt in my math, I get 20fps more, sqrt is so heavy its surprisingromainguy
12/06/2025, 6:45 PMromainguy
12/06/2025, 6:46 PMromainguy
12/06/2025, 6:46 PMromainguy
12/06/2025, 6:47 PMursus
12/06/2025, 6:48 PMromainguy
12/06/2025, 6:49 PMursus
12/06/2025, 6:49 PMromainguy
12/06/2025, 6:49 PMursus
12/06/2025, 6:49 PMromainguy
12/06/2025, 6:49 PMromainguy
12/06/2025, 6:49 PMromainguy
12/06/2025, 6:50 PMromainguy
12/06/2025, 6:50 PMursus
12/06/2025, 6:51 PMromainguy
12/06/2025, 6:53 PMromainguy
12/06/2025, 6:54 PMromainguy
12/06/2025, 6:54 PMursus
12/06/2025, 6:55 PMwithFrameNanos ?romainguy
12/06/2025, 6:57 PMromainguy
12/06/2025, 6:57 PMursus
12/06/2025, 6:57 PMursus
12/06/2025, 6:57 PMromainguy
12/06/2025, 6:58 PMromainguy
12/06/2025, 6:58 PMromainguy
12/06/2025, 6:58 PMursus
12/06/2025, 7:00 PMursus
12/06/2025, 7:00 PMursus
12/06/2025, 7:10 PMSergey Y.
12/06/2025, 7:16 PMromainguy
12/06/2025, 7:27 PMromainguy
12/06/2025, 7:28 PMursus
12/06/2025, 7:29 PMromainguy
12/06/2025, 7:29 PMromainguy
12/06/2025, 7:30 PMursus
12/06/2025, 7:31 PMCanvas seems to optimize away off screen draw calls
so yea youre rightromainguy
12/06/2025, 7:31 PMromainguy
12/06/2025, 7:32 PMursus
12/06/2025, 7:33 PMursus
12/06/2025, 7:34 PMvar fps by remember { mutableFloatStateOf(0f) }
// var dummyCounter by remember { mutableStateOf(0) } // To cause redraw (also needs to be accessed in canvas)
LaunchedEffect(Unit) {
var lastFrameTimestamp = 0L
var frameCount = 0
var elapsedTimeNanos = 0L
while (isActive) {
val frameTimestamp = withFrameNanos { it }
val deltaTimeNanos = if (lastFrameTimestamp == 0L) {
0
} else {
frameTimestamp - lastFrameTimestamp
}
val deltaTimeSeconds = deltaTimeNanos / 1_000_000_000f
updateParticles(particles, deltaTimeSeconds)
fps = frameCount / elapsedTimeNanos.toFloat() * 1_000_000_000f
frameCount++
elapsedTimeNanos += deltaTimeNanos
lastFrameTimestamp = frameTimestamp
// dummyCounter++
}
}romainguy
12/06/2025, 7:44 PMwithFrameNanos() triggers a vsync (don't know if they made it work on Desktop), so the framerate should always be your display refresh rateromainguy
12/06/2025, 7:47 PMursus
12/06/2025, 7:56 PMursus
12/06/2025, 8:04 PMval xForce = ff / distanceCubed * xDeltaPosition
val yForce = ff / distanceCubed * yDeltaPosition
vs
val xForce = ff * xDeltaPosition / distanceCubed
val yForce = ff * yDeltaPosition / distanceCubed
the first one is faster, as if it saw the redundant calculation
but im running in debug (no r8), so..who is doing the optimizing? is it jit?
would you expect this, or is it some side effect of my bad measurement?romainguy
12/06/2025, 8:20 PMromainguy
12/06/2025, 8:20 PM