typelevel / cats-effect

The pure asynchronous runtime for Scala
https://typelevel.org/cats-effect/
Apache License 2.0
2k stars 513 forks source link

Experiment with Loom #1057

Open RaasAhsan opened 3 years ago

RaasAhsan commented 3 years ago

I'm not really sure how feasible it is to run Scala on the Loom Early Access JDK builds, but we should start thinking about what IO would look like in that world. Some questions I'm interested in:

  1. How necessary is Async going to be?
  2. How will Loom virtual threads interact with different forms of tracing? i.e. stack tracing, thread tracing, fiber dumps
  3. Will a Fiber correspond to a Loom virtual thread?
  4. Will we need to support both native platform threads and virtual threads? i.e. should IO work seamlessly across different kinds of schedulers
djspiewak commented 3 years ago

I've put quite a bit of thought into this! I'll try to brain dump here when I can. I think this would be a great thing to get started on. It's in a similar vein to the fiber-aware work-stealing scheduler concepts, since a Loom scheduler would be Thread-aware. It also has the potential to make blocking things much less of a hassle for users, though we can't make them go away entirely since native blocking exists.

Anyway, more thoughts when I have time.

djspiewak commented 3 years ago

First as a table-setting, let's be clear about what Loom is and what it currently offers.

Loom converts java.lang.Thread into an abstraction (well, more of an abstraction). Whereas at present, a Thread corresponds to exactly one underlying pthread, which itself is a real kernel thread, Loom divorces Thread from pthreads, so that a running Thread will have an underlying pthread, but that underlying pthread is not guaranteed to be stable across the lifetime, and many other Threads may also share that pthread if the first one yields.

That's a long-winded way of saying that Loom converts Threads into Fibers backed by an Executor. Optionally. You need to create Threads using a slightly different mechanism in order to get these benefits. But beyond that it's just normal java.lang.Thread.

At any rate, this has some subtle impacts on existing APIs. Most notably, Thread#yield actually does the right thing (it's basically equivalent to a cede) and Unsafe#park (most commonly accessed via other APIs like Object#wait or Thread.sleep) will deschedule the Thread rather than hard-blocking the kernel thread. This descheduling is represented explicitly to the Executor, who receives the continuation as an object which they can stick back into a task queue.

Terminology: real kernel threads are referred to as "carrier threads", while fiber-like threads are referred to as "virtual threads". We sometimes use the "carrier thread" terminology in CE's implementation as well.

Since carrier threads are never blocked by Unsafe#park, it's tempting to say that this entirely removes the need for explicit asynchrony and CPS. There are some very important practical caveats though. Running down the list:

In some sense, you can actually just see virtual threads as being a fancy way of interacting with an Executor, and you wouldn't be wrong. The only thing that is added above and beyond fancy ways of interacting with an Executor is the magic support for Unsafe#park, which unlocks two potential benefits for Cats Effect:

There's also a third potential benefit if some of the low level APIs are more accessible than I think they are, which is that we could get better metrics and introspection over fiber evaluation, which could in turn give us better mechanisms for tracing and other goodies for users.

Overall, I think all of this is worth experimenting with. If we can get useful implementations out of it, gated by a static version check, then we could potentially commit them into the series/3.x branch and spin up a matrix build that uses one of the Loom-enabled OpenJDK builds. That would be pretty cool.

djspiewak commented 1 year ago

For posterity, @vasilmkd has done some experimentation on this branch: https://github.com/vasilmkd/cats-effect/tree/loom

vasilmkd commented 1 year ago

To avoid setting high expectations, the branch that Daniel has linked to above is a very stupid implementation where every "schedule a fiber on a thread pool" operation (including resuming from async points, not just when starting new fibers) is executed on a new virtual thread, which has huge overhead.

There is no 1:1 mapping between fibers and virtual threads on that branch, like a real Loom implementation would.

yuriy-yarosh commented 11 months ago

@vasilmkd spring folks figured it out, maybe it worth back-porting something similar ?

armanbilge commented 11 months ago

Cross-linking Daniel's Reddit post which goes into detail why Loom doesn't matter so much for Cats Effect.

https://www.reddit.com/r/scala/comments/sa927v/comment/htsoydn/

yuriy-yarosh commented 11 months ago

Thank you @armanbilge, you're very helpful, as always...

armanbilge commented 6 months ago

Cross-linking this Loom-related PR for anyone following along here :)

Honestly I feel inclined to close this issue. At the moment I personally cannot foresee any other Loom-related changes, but of course we are always open to experiments!