Cats-effect: Consider providing optimized versions of `IO.sequence`

Created on 20 Sep 2018  路  2Comments  路  Source: typelevel/cats-effect

Default implementations of sequence-like operations are very inefficient and I don't think there is a way to overload their definition but we could provide alternative optimized versions.

Monix does it for TraversableOnce, e.g.

def gather[A, M[X] <: TraversableOnce[X]](in: M[Task[A]])
    (implicit cbf: CanBuildFrom[M[Task[A]], A, M[A]]): Task[M[A]]

which is optimized version of parSequence. This is implementation.

There is also very simple Task.sequence. We also have unordered versions and traverse (Task.wander) equivalent.

Do you think it makes sense to introduce it in either Cats-Effect or separate library?

I have a benchmark:

[info] Benchmark                                Mode  Cnt      Score     Error  Units
[info] SequenceBenchmark.catsParSequenceIO     thrpt   20    609.564 卤  14.155  ops/s
[info] SequenceBenchmark.catsParSequenceTask   thrpt   20    447.448 卤   8.034  ops/s
[info] SequenceBenchmark.catsSequenceIO        thrpt   20   6680.011 卤 524.972  ops/s
[info] SequenceBenchmark.catsSequenceTask      thrpt   20   7474.614 卤 670.785  ops/s
[info] SequenceBenchmark.monixGatherTask       thrpt   20    994.218 卤  11.195  ops/s
[info] SequenceBenchmark.monixGatherUnordered  thrpt   20   1094.078 卤  29.580  ops/s
[info] SequenceBenchmark.monixSequenceTask     thrpt   20  18090.827 卤 285.865  ops/s

Note the difference between catsSequenceTask (default sequence from cats) and monixSequenceTask (Task.sequence)
source code

Most helpful comment

I'm trying to decide which side of this holy crusade I fall down on. I mean, CanBuildFrom is awful, but it's the awful we have (if not the awful we deserve).

All 2 comments

Usage of TraversableOnce and CanBuildFrom will never fly in Cats-Effect, I have a feeling about it 馃檪

A sequence implementation would have to use Traverse. The problem with Traverse is that it relies on folds (foldLeft) and on Applicative to work. In other words there isn't much optimization potential as long as you use Traverse, the current implementation is kind of imposed on you.

And that's why Traverse sucks compared with TraversableOnce + CanBuildFrom. But that's a holy crusade I don't want to get into.

I'm trying to decide which side of this holy crusade I fall down on. I mean, CanBuildFrom is awful, but it's the awful we have (if not the awful we deserve).

Was this page helpful?
0 / 5 - 0 ratings