Can it be that using KotlinX Immutable might actua...
# getting-started
d
Can it be that using KotlinX Immutable might actually slow down an api that I'm trying to avoid mutable structures in? Maybe with less large collections/maps it's more efficient to just use regular lists and maps...?
d
What scale are you working in? For example, basic UI work that isn't compute heavy, it probably doesn't matter. For large scale high-throughput low-latency processing, you'd be better off profiling both approaches to see which is better.
In other words, either it doesn't matter, or try both šŸ˜‰
d
An api that used to take under a second using mutable structures now can take about 3 seconds with kotlinx immutable... but the truth is, I haven't really tried with regular not mutable lists/maps... I was just wondering if the overhead of KotlinX Immutable is not always worth it...
d
What kind of work is that API doing?
Use a profiler to find your bottlenecks
d
Syncing previous state vs. expected state on a biggish data structure...
c
Immutable structures are truly immutable, which means any updates to them copies every element to a new structure. Typically, immutable structures are faster for reads, but significantly slower for performing any updates because of the heavy cost of copying data.
d
@Casey Brooks not all immutable data structures work that way. Some don’t copy, but instead mask, for instance. I don’t know how the kotlinx one is implemented though.
d
but significantly slower for performing any updates because of the heavy cost of copying data.
You're saying that even for KotlinX Immutable @Casey Brooks?
e
kotlinx.immutable has multiple kinds of data structures
c
I’m not 100% certain because I don’t personally use the Kotlinx.Immutable library, as the normal read-only
List
,
Map
, etc. collections provide a strong enough contract for my needs. But from when I’ve looked into it before, I believe the
Immutable
library does use a copy-on-write approach to maintaining immutability, which is a relatively expensive operation. Note that operators which wrap a structure in a ā€œviewā€ are still limited by the performance of the underlying collection.
e
immutable is basically a whole new copy. persistent doesn't copy but references parts of the previous version. which one is faster or takes less memory depends on your usage patterns
if it's internal to your library I'd just use normal lists and maps. they're safe to treat as immutable if you never downcast or expose them
c
I’d suggest that you don’t need the Kotlinx.Immutable library if you’re writing everything fully in Kotlin and respecting the read-only interfaces (i.e. not doing an unsafe cast to make the collection mutable, not using reflection, etc.). This library is most useful when you need immutability guarantees when interop-ing with Java systems which do not have that same contract and might accidentally treat a Kotlin
List
(read-only) as a Java
List
(read-write).
e
eh… if your API accepts a
List
from a caller, you cannot know that it will have the same contents if iterated twice. not only
mutableListOf() is List
but also custom types exist
Compose is for Kotlin and doesn't treat
List
as stable, but it does
ImmutableList
d
So really, Kotlinx immutable is not about efficiency, but rather about true immutability?
c
Yeah, like I said, I’m certainly not an expert on the use-cases of truly immutable structures, and there probably are many things I’m missing. But in general, going back to the original question, truly immutable data structures in general do perform more slowly than read-only (but technically mutable) ones. Everything is always a tradeoff. In this case, you trade safety for performance.
d
I don't really have a concern for true immutability in my use case (I do have control over the structures, they're all internal to resolve the new expected state)... so from what I'm understanding a full copy of a big map or list is more efficient than even the persistent implementation that references the old map/list @ephemient?
c
Performance is tricky to pin down exactly. It can be so heavily dependent on a number of different things that aren’t immediately apparent in your code, such as garbage collection, or internal optimizations the framework performs on known types. This is why it’s usually best to perform performance benchmarks yourself on your exact use-cases to get an understanding of what exactly is causing the bottlenecks in your code, rather than just trying to think through it logically.
e
the persistent data structures in the kotlinx.immutable library will share parts of previous versions. whether that is better or worse than the immutable data structures in the same library, or a plain copy of the standard collections, depends on your usage
d
Profile. Don’t speculate, measure.
āž• 2
d
Yeah, then I'll try. It's not really that easy to switch back and forth like that, but from what you all said, it's worth the try, thanks!
e
d
Profiling doesn’t necessarily mean you need to swap back. You might find a simple to fix change in some loop somewhere that can be applied regardless of which data structure you use.
e
I would also like to say that blindly profiling isn't the right thing to do either. you should have a reasonable prediction from the start, to help inform you whether you're even measuring the right thing.
āž• 3