Kotlin Coroutines and (upcoming) Java Loom

Obviously project loom isn’t in the official JVM & JDK yet but I’m wondering (and it might be too early to discuss this really) … I’m wondering if developers running on the JVM that has Loom would use Kotlin coroutines.

Has anyone got thoughts on that?

Thanks, Rob.

1 Like

You should use search on this forum. The question was raised multiple times. The basic answer from JB team is usually: “we’ll see”. Depending on how it will look like, Loom could be added as one of possible dispatchers. Some other changes could be done as well. The general consensus is that loom and coroutines solve different problems and they do not contradict to one another.

3 Likes

Hi, @rbygrave,

it might be too early to discuss this really

JDK’s coroutine and Kotlin’s coroutine are really different implementations, the first is stackful, the other stackless.

The key metrics for a good choice will be development facilities (including debugging, error rate and time to fix) and performances.
I propose you Fibers under the magnifying glass, some Nishanov’s considerations can be useful for your thoughts.

I wrote about Fibers and coroutine integration in similar threads.

1 Like

Yes, apologies. The discussion would be -

Ok. Personally at the moment I see a lot of overlap so sorting that out is my homework. Thanks.

Interesting. Unfortunately wrt Loom there are some statements in this document that are just plain misleading/wrong (for Looms case) so that taints this document a fair bit for me. It might be terminology issues though as I currently understand that “Fiber” is a pretty overloaded term and will mean different things to different people (hence Looms use of “Virtual Threads”).

The relative cost of context switching “Virtual threads” is Looms key implementation detail as I see it.

Apologies, I should have just added to the other discussion thread.

1 Like

Here is a recent good article about Loom: Will Project Loom obliterate Java Futures? | by Adam Warski | SoftwareMill Tech Blog. It also has some considerations about how Loom will interact with coroutines.

2 Likes

Loom’s continuation is fundamentally better than Kotlin’s, but I think Kotlin coroutines provide better facilities for composing asynchronous work than Loom’s fibers do.

It would be nice if Kotlin could leverage Loom’s continuations on new JVMs, instead of doing the conversion to continuation passing state machines.

Loom continuations wouldn’t solve the red/blue function problem for Kotlin, but they would reduce the relative cost of suspending functions enormously, making developers less resistant to using them in more situations.

Also, Loom provides a fantastic benefit to Kotlin even without any language changes, because Loom continuations will let you wrap any blocking java call to transform it into a suspending Kotlin call. No longer will we have to rewrite libraries to make them compatible with Kotlin coroutines.

1 Like

I might have misunderstood your comments but my thoughts were:

Loom fans would debate that - a; b; vs a.then(b) + coloured methods

Kotlin apps on the JVM that does not use Kotlin continuations will leverage Loom as will every jvm language. Maybe I missed your point there.

It will solve that problem for apps using Kotlin on the JVM that are not using Kotlin continuations (which are optional).

Loom fans might also say that developers also would no longer care specifically where blocking/suspension occurs (as “Virtual Thread yield” is cheap and is handled for us as part of the changes being made to the JDK).

I’m not sure we know exactly what the relative costs of Loom vs CPS is yet.

I heard different opinions about that. Loom does things on a different level, but it does not mean it is “better”. Some argumentation is welcome. I am not specialist in compiler inner workings, but I am interested to hear different opinions.

1 Like

Kotlin is a;b + coloured methods, but really this is very reductive. Kotlin is also an extensive library that combines with language features to provide the whole structured concurrency system, with coroutine scopes, builders, contexts, dispatchers, etc. etc.

I granted initially that Loom’s continuation trick was better, but that’s not the whole story.

Yes. Nobody’s apps will suddenly become asynchronous when they upgrade Java with Loom. You will still have to write asynchronous code to make that happen.

You can write it in Java with fibers or you can write it in Kotlin with coroutines.

If you write it in Java, it will use Loom continuations to implement fibers. If you write it in Kotlin, it should use the same underlying continuation mechanism to implement coroutines, if you target a Loom JVM. The Kotlin Continuation implementations would wrap Loom continuations, and the suspend keyword would have no effect on generated method code.

(If JVM was the only target environment, then Loom would eventually mean that the suspend keyword could simply be removed from the Kotlin language… but it’s not)

There are valid differences of opinion on coroutines vs fibers, but when it comes to the underlying continuation mechanism, Loom’s is just better.

The big difference is that Loom imposes no cost to it until you actually suspend, whereas Kotlin makes you pay whenever one suspending function calls another. This cost is the whole reason for introducing coloured functions instead of just making all functions suspendable.

Loom continuations should also end up faster when you do suspend, should use less memory, and of course produce more compact binaries.

But as I said, Kotlin can use this stuff, too.

I strongly suppose that you are mixing apples and oranges.
Loom’s Frame and Kotlin’s Continuation aren’t interchangeable.

Every Fiber requires a stack, it isn’t so free as you suppose.

This cost can be avoided in some cases, but in all cases the Thread’s stack is released.

It should and should not, it is not possible to test these considerations.
However, this kind of speculation is unuseful without any valid measurements.

Loom is just unavailable.

3 Likes

The question is not whether or not they’re interchangeable. The question is whether or not you can implement Kotlin continuations using Loom’s native facilities instead of CPS rewriting, and whether or not it would be beneficial.

I think you can and I think it would. I hope Kotlin’s developers are open to that sort of thing instead of being focused on a Kotlin vs. Loom narrative. I don’t think it helps anyone to make this a contest.

2 Likes

I don’t think anyone here is focused on anything like Kotlin vs Loom. @darksnake asked you about reasons why you think that Loom is supperior and you tried to back this up, but as @fvasco pointed out some of your arguments, don’t have any data to back them up. Maybe there are proper comparissons, but I couldn’t find any and so far you didn’t provide them either.
I think this part of fvasoc’s answer sums up the problem quite nicely:

This reminds me of disscussions about project Valhalla and how it can be merged with inline classes. In theory this sounds like something that should maybe be considered, but right now there isn’t really anything that can be done about it.
We don’t know anything about how a final version of Loom or Valhalla will look like or when it will be released so speculating about what is better or how they can be combined is just pointless.
Kotlin tries to give the best tools now. If there are better alternatives in the future, kotlin will have to adapt.

I don’t have any real input for this discussion. I’m not a great user of kotlin coroutines and my understanding of it is basic. My understanding of Loom is even less developed, but based on the discussion here, all I can conclude is that it’s to early to decide whether or not Loom would be a good fit/replacement for coroutines.

2 Likes

But you know it’s not that early, right? There’s enough written right here to basically understand how Loom continuations work (you have to scroll down to the implementation section): Main - Main - OpenJDK Wiki

AND, you can actually get a working prototype version right now AND you can look at the source.

It’s really plenty to develop a pretty good mental model of the costs, for anyone who really wants to do so.

Well, I’m pretty old, and what I hear is people rationalizing their emotional investments in their positions.

The same people who are perfectly happy to insist that a growing ArrayList is always better than a LinkedList are refusing to buy that a growing array stack, allocated on demand, along with the relaxation of some annoying restrictions, is better than a linked stack, allocated in advance (even when you won’t need it), that carries the same data.

If you care to read a little bit more into the subject, you will find that it is not yet all clear about the loom. Yes, it should allow to run Java thread-based code in a coroutine style, but it is not tested on real life systems and there definitely will be problems there. As for dispatch itself, you did not provide any valid reference. From other discussions I understood that Loom will still copy stack on each context switch, meaning it could give an overhead over simple reference copy in coroutines.

If you are telling, that there are already buildable samples, could you please build it and write an article with the comparison?

1 Like

The Argumentum ad hominem is the best way to declare that there isn’t any valid topic to support your own argue.
Frankly it seems to me that your ideas are hard to understand.

The first time a Loom coroutine suspends, the portion of the call stack from the top down to the coroutine entry is copied out into an array (actually 2 arrays). In Kotlin, the corresponding data will already have been copied into the linked chain of Continuation objects that get built by suspending function calls. The cost is of the same order for the data that is actually copied, but Kotlin will have performed more allocations, and Kotlin will also have built and discarded these continuation objects for calls to suspending function that didn’t actually suspend. This cost that you pay when you don’t suspend is the one that bothers me, but even neglecting that you can see that a loom suspension will have a lower amortized cost.

When a Loom coroutine is resumed, at least its top-most frame needs to be copied back to the call stack. The return address of this frame will be set to a handler so that when the top-most call returns, it will return into some code that will copy back the next-topmost-frame, etc., incrementally as they are required. Kotlin doesn’t have a directly corresponding cost here, but of course any frame copied back to the call stack must have been copied out at some point, and it’s copied back only once, so we can count this in the amortized cost of suspension.

When a Loom coroutine suspends again, its stack arrays will still contain any frames that will not resumed, so these do not need to be re-copied. It will make space at the end of the arrays if required and copy in any new frames. Again, Kotlin will have made Continuation objects for these frames, etc., etc., so the operation will be cheaper in Loom.

However, the very top-most frame in this case may be a frame that was copied out and in before. Kotlin will only make a Continuation object for a frame once, so this one frame represents an extra cost for Loom that Kotlin doesn’t have. A single stack frame is not a big thing, however, especially since Java doesn’t have any big value types, so this is essentially a small constant cost per suspend/resume that is dwarfed by all the other constant costs involved in that.

So, in terms of actual suspend/resume operations, Loom’s system is more efficient. Added to that, you have more efficient byte code, because it doesn’t have to indirect through a Continuation object, more compact byte code, and no red/blue function implementations.

The basic continuation mechanism in Loom is just better. This is not because the Kotlin guys made any mistakes, of course. It’s just the benefit of being able to mess with the VM.

But again, you know, Kotlin delivers more than just continuations. It’s a whole language that, among other things, provides a practical and easy to use coroutine model based on those continuations. Java has a long way to go before it matches Kotlin in that.

2 Likes

Which says, among other things:

While they are different constructs from the Loom fibers, Kotlin coroutines and Scala fibers will be able to leverage the native implementation. Custom mechanisms for scheduling multiple tasks will be most probably replaced by the native mechanisms. That way they’ll get access not only to better performance (as they’ll be using a native construct), but also to meaningful stack traces and other improvements.

2 Likes

Regarding performance, I saw a lot of discussions but no benchmark numbers, so I decide to do a simple benchmark with the same ‘concurrent prime sieve’ algorithm golang uses on its homepage to demonstrate its goroutine performance.

The result shows kotlin coroutines (on JVM16) is ~2.5x slower than go(1.16.3), while java loom(ea jdk build 17-loom+6-225) is ~4.5x slower. That’s a little surprising to me, loom is not as performant as I was expecting. :frowning:

1 Like