nibarius
nibarius

Reputation: 4117

Why does using by lazy make my Kotlin code slower

I have some code that generates large amounts of instances of a small data class. When I added one lazy property to the class I noticed that creating instances of this class became much slower even if the lazy property is never accessed. How come this happens? I was expecting that there would be no difference if the lazy property is never accessed. Is there any way of using lazy properties without taking this kind of performance hit?

Here's a minimal example:

import kotlin.system.measureTimeMillis

class LazyTest(val x: Int) {
    val test: Int by lazy { 9 }
}

fun main(){
    val time = measureTimeMillis { List(500_000) {LazyTest(it) }}
    println("time: $time")
}

When running this on on play.kotlinlang.org it takes 500-600 milliseconds, if I comment out the line val test: Int by lazy { 9 } it instead takes around 40 milliseconds to run.

Upvotes: 4

Views: 2405

Answers (3)

Richard
Richard

Reputation: 968

When you KNOW the access will be thread safe, you can use this to improve performance:

fun <T> unsafeLazy(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer)

Upvotes: 2

cactustictacs
cactustictacs

Reputation: 19554

lazy delegates default to using synchronization for thread safety:

By default, the evaluation of lazy properties is synchronized: the value is computed only in one thread, and all threads will see the same value. If the synchronization of initialization delegate is not required, so that multiple threads can execute it simultaneously, pass LazyThreadSafetyMode.PUBLICATION as a parameter to the lazy() function. And if you're sure that the initialization will always happen on the same thread as the one where you use the property, you can use LazyThreadSafetyMode.NONE: it doesn't incur any thread-safety guarantees and the related overhead.

Synchronization incurs a bunch of overhead, and if you're doing a lot of it (with lots of lazy properties) you're probably going to see some slowdown. If you know you're only accessing the property on one thread, or if you know what you're doing and can handle the thread safety yourself, you can pass those other parameters to change the synchronization behaviour.

This doesn't actually apply to your example since you're not actually accessing your properties (you're just creating half a million objects holding half a million delegates, that takes some time!) but it's a good thing to be aware of when you use them.

Upvotes: 6

Louis Wasserman
Louis Wasserman

Reputation: 198143

Using by lazy creates a second object to "hold the laziness," implementing the lazy calculation. This is most likely responsible for the slowdown.

Upvotes: 5

Related Questions