Reputation: 393
I have a class with around 20 methods which take no parameters and will always return the same values. I am fairly sure that these can be refactored and defined as lazy val
and I am wondering about the pros and cons of that change. This is
lazy val | def |
---|---|
reduced runtime cost for repeated access of the same data | reduced space complexity (data isn't cached) |
My conclusion based on that is that if the class is frequently instantiated and fields are rarely accessed def should be used, if it is rarely instantiated and the fields are frequently used it should be lazy val.
If they are both frequently instantiated and accessed what would be the preferred strategy to fall back on? Also, I don't know what optimization the language inherently implements so I could be totally wrong. Let me know if that is the case
Upvotes: 1
Views: 456
Reputation: 20551
In general, the only case where a lazy val
should definitely be used (aside from the obvious "when it's needed to get the code to compile") is when correctness depends on a given computation executing at-most-once (which implies some impurity in the computation).
Beyond that, there's non-trivial performance overhead (which varies from Scala version to Scala version and JVM to JVM) in using lazy val
, especially if multiple threads might access the value, so there's a surprisingly high threshold for "expensive-to-compute" below which def
recomputing every time can be faster than a lazy val
. For values which are particularly cheap-to-compute, a regular val
may not need that many expected accesses to provide a performance win over a lazy val
.
def
, even if it's allocating an object graph every time, might even be better in GC terms if the callers are quickly discarding the result (as those objects are unlikely to stay live long enough to get into old-gen, which is where problems can arise).
As with any optimization, profile and benchmark.
EDIT: If you don't actually need the hard guarantee that a value will be computed at-most-once (i.e. an at-least-zero-times guarantee) something like this (sparingly...)
private var _thing: Type = null
def thing: Type = {
if (_thing eq null) {
_thing = ???
}
_thing
}
might actually be worth trying and fit the single-threaded intuition about how lazy val
works.
Upvotes: 2