typelevel / typelevel/cats-effect
Use Scala Native RTTI class IDs for switch statement in `IO` runloop
Nobody has claimed this yet.
- Dominant language
- Scala
- Stars
- 2.2k
- Forks
- 576
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 18
Description
In Scala Native, at linking time every class is assigned an ID, as part of its RTTI (runtime type info?). Furthermore, the id of every subclass of a class is sequential i.e. every subclass of IO occupies some ID in a sequential range of IDs.
This suggests we can try an optimization similar to https://github.com/typelevel/cats-effect/pull/3471, where we avoid the virtual dispatch on the IO#tag method in the IO runloop. The difference is that we don't need to increase the memory footprint of every IO instance, although we do have to chase a pointer to find the tag.
A small demo program below prints out the IDs for several IO subclasses. Unfortunately it reveals a tricky issue that they seem to be assigned out-of-order. I'm not sure if there's anything we can do about that, because it might completely tank this idea. 😕
//> using dep org.typelevel::cats-effect::3.5.1
//> using platform native
//> using nativeVersion "0.4.14"
import cats.effect.IO
import scala.concurrent.duration.*
import scalanative.runtime.Intrinsics.*
import cats.effect.kernel.Cont
import cats.arrow.FunctionK
import cats.effect.kernel.MonadCancel
def getId(io: IO[_]): Int =
loadInt(elemRawPtr(loadRawPtr(castObjectToRawPtr(io)), 8))
@main def main =
println(getId(IO.pure(())))
println(getId(IO.raiseError(new Exception)))
println(getId(IO.delay(())))
println(getId(IO.realTime))
println(getId(IO.monotonic))
println(getId(IO.executionContext))
println(getId(IO.unit.map(_ => ())))
println(getId(IO.unit.flatMap(_ => IO.unit)))
println(getId(IO.unit.attempt))
println(getId(IO.unit.handleErrorWith(_ => IO.unit)))
println(getId(IO.canceled))
println(getId(IO.unit.onCancel(IO.unit)))
println(getId(IO.unit.uncancelable))
println("id for `Uncancelable.UnmaskRunLoop`")
println(getId(IO.cont(new Cont[IO, Unit, Unit] {
def apply[G[_]](implicit G: MonadCancel[G, Throwable]) = (_, _, _) => G.unit
})))
println("id for `IOCont.Get`")
println(getId(IO.cede))
println(getId(IO.unit.start))
println(getId(IO.racePair(IO.unit, IO.unit)))
println(getId(IO.sleep(1.second)))
println(getId(IO.unit.evalOn(null)))
println(getId(IO.blocking(())))
381
384
383
395
396
391
380
390
389
398
394
392
397
id for `Uncancelable.UnmaskRunLoop`
388
id for `IOCont.Get`
382
386
393
385
387
390
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the Scala Native demo in the issue and compare the printed RTTI class IDs for the IO subclasses. Read the IO runloop and the linked cats-effect pull request to assess whether RTTI IDs can replace virtual dispatch without relying on sequential ordering. Done would require a viable switch-based optimization and evidence that the runloop remains correct and improves performance.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- scala
- Domain
- backend, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100