polytypic / scala-toys

1 stars 0 forks source link

Comparison with Hopac #1

Open ArbilGit opened 3 years ago

ArbilGit commented 3 years ago

Long time (and enthusiastic) Hopac user here. This Hopacy async with a Scalaish effect flavour is illuminating! I think it's already the fastest async story on Scala, even though it's back-of-the-envelope.

It's about 50% slower than Hopac on my laptop in forking. Do you think it's because of fundamental design differences vs Hopac, because of JVM, or because of worse optimisation?

Also, if you'd ever want to blog about what the stronger type system vs F# allows us to do, I'd be extremely interested to read that.

polytypic commented 3 years ago

Please note that the FIO stuff in this repo is just a toy/experiment I wrote for learning Scala. I haven't put that much effort into it and don't currently have plans to do so. Hmm... Actually I think I did some more work on this. I recall I had a toy benchmark or two comparing with ZIO, but I don't have that code anymore - I wiped the laptop that I had that code on (well, it wasn't that much code). But if someone is interested in developing or experimenting with this approach further, I could certainly try to explain the code if needed.

Long time (and enthusiastic) Hopac user here. This Hopacy async with a Scalaish effect flavour is illuminating! I think it's already the fastest async story on Scala, even though it's back-of-the-envelope.

I believe the main performance advantage comes from using a custom scheduler that doesn't try to be fair and is designed/programmed in a low level manner to support async code. The way continuations are put together is also based on an approach similar to what I used in Hopac to try to minimize allocations.

Note that while a custom scheduler can help with performance it also makes interop with existing Scala/JVM stuff potentially less efficient and more difficult to do well.

It's about 50% slower than Hopac on my laptop in forking. Do you think it's because of fundamental design differences vs Hopac, because of JVM, or because of worse optimisation?

The .NET Hopac implementation doesn't support passing an environment, doesn't support having a custom error type, uses some features like structs to squeeze some last bits of performance, and .NET sometimes generates tail calls. Those might already explain a good chunk of the performance difference. OTOH, there might be ways to tweak the approach to perform better on the JVM.

Also, if you'd ever want to blog about what the stronger type system vs F# allows us to do, I'd be extremely interested to read that.

I think that there are already a lot of blog posts and articles like that. 😄

Perhaps I'll write some blog posts on my Fωμ hobby project at some point. It is not really meant to be a programming language for humans (it might make a nice compilation target), but it supports impredicative higher-rank polymorphism, higher-kinded types, existential types, structural sums and products, and equirecursive types.

ArbilGit commented 3 years ago

Thanks for the thorough reply. I did my own basic tests vs ZIO, that's why I'm saying it's probably the fastest on Scala. parallelFib(30) is ~50ms on FIO and ~9000ms on ZIO, not in the same league performance-wise. Of course, ZIO can do a lot more, but sometimes you'd rather have the speed.

I'll be experimenting with this code further.

As an aside, is this general approach to concurrency at all feasible in a non-GC language?

polytypic commented 3 years ago

As an aside, is this general approach to concurrency at all feasible in a non-GC language?

You can always allocate continuations from the heap (when necessary), but it might get rather cumbersome depending on the language.

In a low enough level language you might have primitives to control the stack and that might give more interesting alternatives. One thing I've been meaning to take a deeper look into, but haven't yet taken the time, is libmprompt.