Hi, are there any plans to provide an opt-in memor...
# kotlin-native
e
Hi, are there any plans to provide an opt-in memory management based on ownership, similar to Rust ? I guess this could be important for high-performance systems programming, am I right?
d
Isn't
memScoped {}
what you're looking for?
e
Hmm... will take a look at this... Thanks!
d
Based on ownership.. I don't know what that means
e
oh, let me explain. On Rust, when you assign a value to a variable, like in
let my_var = String::from("Hello World")
, some memory was allocated, and once
my_var
goes out of scope, its associated memory is automatically deallocated - the compiler knows the var is out of scope, so it automatically insert a call for the deallocation
so the variable owns that piece of memory, and once the variable dies, the memory owned by that variable dies with it.
Swift will implement this ownership-based memory management model.
l
It currently works similarly in Kotlin/Native, but there's also a cycle collector for cyclic references (Swift's Achilles heel)
👍 1
e
From what I know, Kotlin/Native has ARC just like Swift, but has no ownership-based memory management yet. Something that Swift wants to offer.
k
I would call some of what's happening "Rust-like". Andrey talked about their future plans for concurrency at KotlinConf (my little post about thoughts: https://medium.com/@kpgalligan/saner-concurrency-74b0bf8ed446). K/N has reference counting with cycle collector (as mentioned right above this), but I think the critical thing to understand with regards to concurrency is that mutable state can only be touched by one thread at a time, which is effectively similar (From a concurrency perspective)
e
oh, so Swift's ARC has no garbage collector then?
k
Swift's arc can leak memory with reference cycles
K/N also checks for this, in a process I've never sat down to understand, but removes the issue
e
hmm.. that's bad. So that explains why Swift wants to provide an alternate memory management model
k
I think it has more to do with concrrency than memory
Swift (and Java, and many others) rely on the developer to be safe with concurrency. Rust bakes that safety into the language. K/N has some of that in the runtime, and maybe in the future Kotlin in general will have some of that at compile time (watch the keynote). However, that's down the road and highly speculative.
e
Anyways, the idea that the compiler can insert calls to free the memory associated with a variable that is out of scope feels so natural to me.
k
That is what's happening in K/N, but it lacks the language level ownership stuff.
e
hmmm
k
It's not a "garbage collector" like JVM
e
yep, it's not GC
k
The memory gets freed if your var goes out of scope and there are no more references to it.
Swift (and objc) are like that too
Minus the cycles, of course
e
good 🙂
This bit: if (container->decRefCount<Atomic>() == 0) { FreeContainer(container); }
The bit after gets into the cycle collector, and my mental model of how all this works might be inaccurate, but I think it's pretty close to correct.
e
But the decision to free the memory happens at runtime, right? In Rust, it happens at compile time.
d
So is the difference between KN and rust, that in rust, ownership can be transferred?
Don't quote me on that, it's just what I remember from checking it out
e
I'd say that are more differences, like the one above (Rust compiler inserts a call to free memory at compile time, instead of deciding to free memory at runtime)
k
e
hmm
d
Oooh okay. Runtime should be less efficient. Is that the problem?
e
Yes, and I suppose that, by using Rust's memory management model, you don't need to garbage-collect cyclic references
d
I think it might be possible in the future, if they make inlined function blocks with receivers more powerful
k
From a memory perspective, you're mostly dealing with compile time vs runtime, but if we're trying to get at a system with inherently safer concurrency, they have similar concepts (or at least intended outcomes). As for counting and performance, for non-frozen state, we're talking non-atomic integer math, so I'm not sure how much performance will differ in practice.
d
memScoped
is just a library function I believe. But if they make it so the compiler can generate extra stuff (at the end if the block) when you call a certain function on the receiver, that could work nicely.
k
Not advocating one or the other, but I don't think it's a safe assumption that Rust's model would be significantly faster (if only considering ref counting. Stack/heap allocation, etc, are likely a different story)
e
Looks like performance with ownership-based MM would be more predictable, since there's no GC at all.
k
Yeah, but "more" is tricky
Seems like a good opportunity for a lengthy blog post and tests
e
you mean that the special, cyclic-refs-only GC in K/N is already more predictable than Java's GC ?
d
Would it possibly be weird that there is a GC and a system like you describe in the same vm?
k
I meant that "more" is relative. Saying MM is more predictable than the cyclic thing is true, but we should define "more". I would guess that cyclic ref GC is way more predictable than Java's GC, except that Java has had multiple decades to refine it
I do think the details of the cyclical checking need a deep dive.
e
Would you know if the cyclic-gc code needed in K/N means the final executable will be bigger than the equivalent Rust executable? I guess so, at least a bit (not sure about the code size of the cyclic GC)
k
That would be a really good conf talk and/or blog post. In my experience with Objc and swift, cyclical references aren't super common, except in certain cases. If the cyclic checking is only happening in exception cases, maybe it's minor, maybe it's not.
I don't know. If you need to inline memory management, you could argue Rust would be bigger 🙂 It's all academic without testing it out, and I would also guess any language runtime would have a bigger impact
d
You mean speculation
k
I spent a lot of time doing J2objc, which would add 10m out of the gate because it had a lot of JRE implementation. Swift has a big runtime. K/N produces a pretty tight output, but I'm sure Rust does as well. I have far less Rust experience.
Yes, speculation
Also, probably mostly academic. There aren't a lot of situations where the size difference is going to have a huge impact. K/N's output is relatively small (in my experience)
I would be interested in seeing tests, but unlikely to start thinking either is better from that alone. You know what I mean?
The cycle checker is a bigger maybe, though. I have no idea what the impact is.
o
high-performance systems programming is not the niche we believe Kotlin and Kotlin/Native are suited best for. Moreover, in dichotomy between making easy to think/program and highest performance primitives, we always take the easy, not fast path.
g
If somebody talking about "performance". Would be good to ask yourself what performance means for your particular case and how to measure it. Very often all speculative assumptions based only on theory are wrong on practice (approach A is always better/faster/has less overhead than approach B)
👍 1