One Off the Slack: Is It Safe to Allocate?

Anton Shilov asked:

In Android Views world creating new objects in onDraw callback is considered a bad practice. In compose official docs we see creating instances of Offset, Path, etc. in DrawScope. Is it going to be impacting draw performance the same way as in views? Or we shouldn’t care about such things in compose world?

Canvas(modifier = Modifier.fillMaxSize()) {
  val canvasWidth = size.width
  val canvasHeight = size.height

  drawLine(
    start = Offset(x = canvasWidth, y = 0f),
    end = Offset(x = 0f, y = canvasHeight),
    color = Color.Blue
  )
}

If you’re running an animation it could recompose on every frame and become problematic

Albert Chang was less concerned:

Firstly, many of these (e.g. Offset, Color) are inline classes, which are essentially primitive types, so there isn’t any object allocations when using these classes. Secondly, that rule was proposed in the early years of Android, when it was still using Dalvik. ART is much better in this respect, and the performance of mobile chips has also improved a lot. Generally I don’t think this should be a big problem now. You can start to think about optimization when you actually see some performance issues.

Google’s Adam Powell agreed, to a point:

ART and hardware have improved but putting pressure on the GC when you don’t need to comes at a cost. In particular, on modern versions of ART the GC runs concurrently without the kinds of stop the world pauses you might be used to, but the trade-off is that while it’s running, field reads involve full memory barriers, making all of the rest of your code on other threads run slower.

If we didn’t worry about it at all then we wouldn’t have bothered making the classes mentioned above inline. You’ll see that in some very frequent code paths we also make sure to use O(1) indexed collection classes and forEach variants that avoid creating iterators.

All that said, we’ve done this in the deeper compose internals so that in the common case, you don’t have to go this far yourself in your own code unless you’ve measured a problem. Hardware and software have gotten better here. Keep it in mind, don’t be unnecessarily wasteful, but don’t tie your code completely in knots for something you haven’t measured either.

Modifier.drawWithCache is a good example of an API designed for helping you with this; you get an outer block for creating things you might reuse over time and an inner block for drawing using those same object instances


Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!