Is it performance improving to lazy implement ever...
# announcements
h
Is it performance improving to lazy implement every field? example:
Copy code
class Polygon(val vertices: List<Point>) {
    val height by lazy { vertices.maxBy(Point::y)!!.y - vertices.minBy(Point::y)!!.y }
    val width by lazy { vertices.maxBy(Point::x)!!.x - vertices.minBy(Point::x)!!.x }

    val edges by lazy { (points + points.first()).zipWithNext(::Line) }

    val area by lazy { edges.sumByDouble { (cur, next) -> cur.x * next.y - cur.y * next.x } / 2 } // shoelace method
    
    val centroid by lazy {
        edges.reduce(Point.Origin) { acc, (cur, next) ->
            acc + ((cur + next) * (cur.x * next.y - next.x * cur.y))
        }
    }
    
    // etc
}
Afaik Advantages: • faster instantiation • instance fields that are never accessed are never computed Disadvantages: • instance fields computed when first accessed, which may be a performance heavy time General questions: • Does
lazy<T>
occupy more space than
T
? Answered: yes, it creates an object and a lambda in memory • Is there some kind of performance impact at initialization for
lazy
that may make for a negligible difference in impact for many fields? • Am I an idiot, and unless something is rarely accessed, I should just completely avoid using
lazy
?
d
Yes,
lazy<T>
creates a
Lazy<T>
object, it also uses atomics by default which might be impactful.
The lambda you pass in is also allocated.
h
I should probably learn to use tools to measure performance impact. I wonder what complexity of field initial value computation exceeds the complexity of lazy?
so it is almost certainly less memory efficient for fields of primitive or simple types
m
faster instantiation
Not necessarily, at least in any meaningful fashion. Consider:
Copy code
val foo by lazy { 2 + 2 }
I would not assume that calling
lazy()
and instantiating the
Lazy<Int>
will be less time than adding two
Int
values. While this is an obviously-contrived example, there is some minimum threshold for which
lazy { }
will be faster.
unless something is rarely accessed, I should just completely avoid using
lazy
?
The other scenario that I have seen is using
lazy { }
as an alternative to
lateinit var
for things that you cannot initialize up front but are sure[1] to be able to initialize when you first need them. In Android app development, for example, you will see some people use
val button by lazy { findViewById<Button>(R.id.button) }
, because we cannot call
findViewById()
when the activity is being constructed, but by the time we would reference
button
,
findViewById()
certainly[1] works. [1] for certain values of "sure" and "certainly"
a
yeah I don't recommend using lazy for that sort of thing. At best you get a slightly less cryptic exception when things go wrong, and they'll still go wrong under the same circumstances for the same reasons as similar patterns with lateinit
h
thanks. i hope this wasn't a stupid question!
a
not at all! both property delegates and
lazy
look like magic at first
t
Regardless of performance: don't do this unless the variable actually has to be lazy.
2
Not to mention that it makes the performance characteristics of a program much harder to reason about. Take a look at Haskell for example.
h
A place I believe I've been misusing
lazy
is when I need to compute the initial value of a field across multiple lines of code. Instead, do you prefer
init
or
run
? I prefer
run
a
yes don't use lazy for that
probably favor init if you're finding yourself needing complex initialization
d
Using lazy for primitives also allocates an extra box object
j
Groovy has @Lazy(softref=true) which acts same, but allows the heap to kill the value when pressure demands
d
Kotlin has that in kotlin-reflect, dont know if it's public api
h
Another place to use lazy would be expensive-to-compute and not certainly accessed, but otherwise immutable values. E.g. many properties of a matrix: eigenvalues, pseudo-inverse, non-singularity, etc.
Correct me if I'm wrong
j
lazy makes a good singleton and configs. lazy is a great gateway to reduced locality objects.

http://i.imgur.com/k0t1e.png

... lazy wrappers, weak/soft references (aka discardable memory) , and threadlocal are sort of the far opposite quadrants of atomics and realtime.