4 ms·
Virtual threads, real threads, and structured concurrency is quite literally what co-routines are about. From a Kotlin co-routine point of view Loom is about t
by jillesvangurp 4y ago
Virtual threads, real threads, and structured concurrency is quite literally what co-routines are about.
From a Kotlin co-routine point of view Loom is about the JVM gaining some low level optimization features that will slightly enhance co-routine performance on newer JDKs (not that this was actually much of a problem) but will otherwise not really add anything new in terms of features as co-routines already had pretty much all of the features.
Of course it's nice for Java to gain a few new APIs for this. But it's not like there was a shortage of asynchronous programming APIs. If you intend to use this as a drop in replacement for Threads, read up on blocking vs. non blocking IO or you'll be finding out the hard way why Java uses a lot of real threads when dealing with blocking IO. If on the other hand you were already using non blocking IO, using Threads would not have been a thing you'd be doing. Hint, virtual threads in the same thread will all block if you use blocking IO.
With Kotlin, co-routines, the compiler will issue warnings if you call into blocking Java stuff from a co-routine and nudge you to deal with that by of-loading to a threaded co-routine context.
- mwcampbell 4y ago> Hint, virtual threads in the same thread will all block if you use blocking IO. This is apparently not the case in the new JDK implementation of virtual threads. According to JEP 425 (linked in the OP): > Application code in the thread-per-request style can run in a virtual thread for the entire duration of a request, but the virtual thread consumes an OS thread only while it performs calculations on the CPU. The result is the same scalability as the asynchronous style, except it is achieved transparently: When code running in a virtual thread calls a blocking I/O operation in the java.* API, the runtime performs a non-blocking OS call and automatically suspends the virtual thread until it can be resumed later. To Java developers, virtual threads are simply threads that are cheap to create and almost infinitely plentiful. Hardware utilization is close to optimal, allowing a high level of concurrency and, as a result, high throughput, while the application remains harmonious with the multithreaded design of the Java Platform and its tooling. So new Java virtual threads really are meaningfully different from Kotlin coroutines.
- jillesvangurp 4y agoNice. With Loom, you have to use a thing called continuations, which includes a method called suspend. So, under the hood, it does very much the same thing as Kotlin does with its suspend functions. Which isn't surprising if you understand how light weight/green thread schedulers work. Basically, you allow each "thread" to run until it suspends in some kind of loop. What Loom likely does for blocking IO is implement some continuation wrappers around that do the right thing; which is indeed nice. With Kotlin co-routines, you'd mostly avoid using blocking IO in favor of using non blocking IO. With the exception of legacy code that uses blocking IO which up until now you'd simply park on a threaded co-routine context for that reason. So, yes this is a very nice feature that I wasn't aware of and thanks for pointing that out. It won't massively change my life because there's not a whole lot of blocking IO happening in most things that I use. But it's great for transitioning legacy code to more modern frameworks without a lot of invasive changes.
- mwcampbell 4y agoAs I understand it, the point of Loom/virtual threads (same thing) is that blocking APIs, the kind that you can easily debug, profile, and otherwise use with standard JVM tooling, shouldn't be "legacy". Loom fixes the scaling problem so we can go back to doing things the older, simpler, now better way. In time, the explicitly non-blocking APIs, and Kotlin's compiler-based coroutines, will be legacy.
- adra 4y agoAfter recently taking on a relatively hefty Kotlin coroutine project for the express purpose of dealing with many suspends from io, I can say coroutines is pretty good at what it does. There are things that loom does better that I've been waiting years to get back into the JVM, but there are facets of coroutines that are just really straight forward that simply green threads can't easily resolve. My hope is that the java and Kotlin crews take the best of both worlds and create some.compelling frameworks, because in the end, a better set of products is good for everyone. Q
- lokedhs 4y agoKotlin adds a huge overhead for async code. I measured over 50% slower for function heavy code. The otherwise problem is that you end up with two worlds, the non-async ad the async ones, and they can't really call each other without a lot of problems. The new JVM features will allow you to write async code while still being able to call any functions, that'll make it actually useful.
- jillesvangurp 4y agoMy servers mostly idle waiting for IO (like most servers do), so I never saw any slowdowns with this; nor would I expect to. Also this is not really an issue on e.g. Android where a lot of UI code is inherently asynchronous and co-routines are used a lot. And it's a complete non issue on kotlin-js as well where co-routines and js promises are essentially the same thing. I use it; it's fine. Kotlin's co-routines are simply syntactic sugar for simple callbacks but without the boiler plate. If it has some kind of success / fail callback mechanism, you can create a co-routine from it. Futures, Promises, a Flux, whatever. That's all that happens and the co-routine library comes with extension functions for all these. I'm not sure what you measured relative to what. But I bet it is some blocking/expensive code actually taking a while before it suspends or something similarly sub-optimal. Of course the key bottleneck with virtual threads would be the scheduler loop that runs all these co-routines. The main contribution of Loom is moving that stuff into the JDK and possibly optimizing the hell out of that. But other than that it does something very similar to what other green/light weight threads have to do necessarily. Basically any time any virtual thread does something expensive/blocking, all the other virtual threads suffer. If you see a browser app freeze for a bit, that's usually what happened. Loom adds a thing called continuations which literally have a suspend method that tell Loom that it is now safe to switch to another Fiber. Failure to use suspend correctly, will result in the same stuttering behavior on Loom: things will freeze or block until whatever is running calls suspend: only 1 virtual thread executes at the same time. That's the whole point. For non blocking IO this is fine. If you are doing computationally intensive stuff, you either need to suspend frequently or use some thread pool. Otherwise the virtual thread will hog the CPU whenever it runs. As for the two worlds, I can call any function from a suspend function. Just not the other way around without creating a co-routine context. What Loom adds is basically a better light weight thread scheduler. Under the hood it likely does many of the same things that Kotlin already did.
- lyson 4y agoExcept performance improvement already mentioned there are two more benefits I can think of: * clean call stacks - stack traces returned by current async libraries are very difficult to understand * no need to colour functions, no need to worry if your function is suspendable or not and in which context you are using it
- deleted 4y ago[deleted]
- PaulHoule 4y agoThere are some applications where coroutines are a comfortable way to code even if you don't care about concurrency. For instance there are many cases where you write an Iterator that wraps an Iterator, like https://paulhoule.github.io/pidove/apidocs/com/ontology2/pidove/iterables/Iterables.html#pairwise(java.lang.Iterable) https://paulhoule.github.io/pidove/apidocs/com/ontology2/pid... that involve a slightly awkward state machine (and some complexity around initialization and teardown) that can be avoided with coroutines such as Python's generators.
- kaba0 4y agoThat’s a huge misunderstanding. With a tiny caveat, loom magically transforms every blocking Java call to non-blocking. The caveat is native FFI, which is extremely rare in case of the JVM. Kotlin’s coroutines are cooperative by definition, while Loom allows for non-cooperative concurrency.