runarorama / runarorama.github.com

9 stars 3 forks source link

Easy performance wins with Scalaz #17

Open runarorama opened 9 years ago

duduk commented 9 years ago

Hello Rúnar,

Isn't it that, since ackermannF already returns Future[Int], there should be no Future.apply calls in last 2 cases? So, there should be:

        case (m, 0) => ackermannF (m - 1, 1)
        case (m, n) => ackermannF (m, n - 1).flatMap (x => ackermannF (m - 1, x))

instead of:

        case (m, 0) => Future (ackermannF (m - 1, 1))
        case (m, n) => Future (ackermannF (m, n - 1)).flatMap { x => Future (ackermannF (m - 1, x)) }

Regards, Ivan

runarorama commented 9 years ago

@duduk You're totally right. Fixed!

flicken commented 9 years ago

Shouldn't the base case use Future#successful (analogous to Task#now):

case (0, _) => Future.successful(n + 1)

Instead of the current Future#apply?

case (0, _) => Future(n + 1)
magro commented 9 years ago

With futures I could specify an execution context that runs the callback in the same thread, like this one: https://github.com/inoio/solrs/blob/master/src/main/scala/io/ino/concurrent/Execution.scala

Do you see a disadvantage with this approach?

flicken commented 9 years ago

@magro The sameThreadContextexecutes the Futureinstances in the same call stack, thus suffering from the same stack overflows as the original version (only much worse, because the ExecutionContext calls are also on the stack).

yanns commented 9 years ago

Playframework has a "trampoline" Execution Context: https://github.com/playframework/playframework/blob/3f478541fcaa1ca298b948bd969d3b54be8c8414/framework/src/iteratees/src/main/scala/play/api/libs/iteratee/Execution.scala#L31

What do you think about it?

tbfangel commented 8 years ago

Hello Rúnar,

Very interesting article which touches upon some the performance issues we are currently investigating on an Akka project with a basic design strategy to do everything in a non-blocking fashion using Futures. In one of the components of the system we see a very, very high amount of context switches (~ 100k) which makes us a bit nervous. We have been trying to figure out if there's some blocking code hiding around in a corner of any 3rd party Java library used, but without success yet. So, we are now looking for other explanations than blocking code, and I'm wondering whether mapping on Futures can be part of the explanation.

The component in question does batch processing of millions of records and for each chunk it does some lookup of static data in a cached API (returned in a Future) subsequently some processing of this data and finally writes it to a report file using Camel. (And we have ruled out the I/O as a source of the context switches - it's still there if we disable writing the data to disk). Everything is done by hefty mapping on Futures. So, I would be really interested in seeing a graph of the number of context switches done for each implementation of the Ackermann algorithm in your post. Is the poor performance of the implementation using Scala's own Future directly reflected in the number of context switches? And likewise, can the better performance of the optimized algorithm using scalaz' Task be seen in the number of context switches?

What do you think?

Thanks,

runarorama commented 8 years ago

@tbfangel The performance difference here is precisely because of the number of context switches. The trampoline involves no context switches at all.

trylks commented 7 years ago

Hello, I like this post a lot, it helps to understand quite a few concepts, thank you for the explanations. IMHO it is a bit too dense and packed and it should be possible to make easier to understand a few things. In particular, there are four options evaluated, Future, Task, Trampoline, and Optimized, but there are only three code snippets corresponding to only three of these options. I think that the one missing may be the one corresponding to Task, and it may be obvious that it is a small modification from Future, but it would be nice to have it anyway, or at least an explanation of how it should be, or at the very least a mention acknowledging that it is missing and left as an exercise to the reader.

As I said, maybe it is obvious, but I am not familiar with the Task monad and I spent some time trying to figure out that such snippet was missing and in fact its place is taken by the Trampoline snippet.

Thank you again.

yanns commented 7 years ago

For info, I wrote about the trampoline EC here: http://yanns.github.io/blog/2016/02/10/trampoline-execution-context-with-scala-futures/