Hello, I'm tinkering with arrow.fx. I'm exploring ...
# arrow
t
Hello, I'm tinkering with arrow.fx. I'm exploring the Effect and Fibers and I'm struggling to understand what the difference is between ... other than one runs quicker than the other! I'm a few days into arrow so apologies I'm if doing something that doesn't make sense or if my code below is naive 🙂 To explore, I've written the following functions:
Copy code
fun doWorkWithIO() {
        val time = measureTime {
            IO.fx {
                val effects = mutableListOf<Kind<ForIO, Int>>()
                for (i in 0..1_000_000) {
                    val thing = effect { i }
                    effects.add(thing)
                }
                effects.forEach { it.bind() }
            }.unsafeRunSync()
        }
        println("Default IO took $time")
    }

    fun doWorkWithFork() {
        val time = measureTime {
            IO.fx {
                val effects = mutableListOf<Fiber<ForIO, Int>>()
                for (i in 0..1_000_000) {
                    val thing = effect { i }.fork().bind()
                    effects.add(thing)
                }
                effects.forEach { it.join() }
            }.unsafeRunSync()
        }
        println("Fibers took $time")
    }
doWorkWithIO
completes in about 1 second and
doWorkWithFork
runs in about 3 seconds. Are fibers using coroutines under the hood or threads? If so, whats the difference between them and effect? I assume they're for different things otherwise the wouldn't exist 🤷‍♂️ Is my code completely wrong!?
p
fork
is a way of eagerly starting a computation in
IO
, and as Bob said, churning 1M executions in a CoroutineContext does come with some overhead, even if they resolve immediately
it's up to the CContext whether each execution happens in a thread, a threadpool, or immediate. IIRC the default here is a CommonPool we build because we target Java 6.
t
Thanks for the explanation. I've modified the code to save the tread names and introduced delay(50) as bob suggested. In that case fibres is more preformant for high concurrency. When printing the thread name i get:
Copy code
IO thread.id ForkJoinPool-1-worker-23
IO thread.id ForkJoinPool-1-worker-19
IO thread.id ForkJoinPool-1-worker-5
IO thread.id ForkJoinPool-1-worker-27
IO thread.id ForkJoinPool-1-worker-9
IO thread.id ForkJoinPool-1-worker-13
Fibers took 143ms

IO thread.id kotlinx.coroutines.DefaultExecutor
IO thread.id kotlinx.coroutines.DefaultExecutor
IO thread.id kotlinx.coroutines.DefaultExecutor
IO thread.id kotlinx.coroutines.DefaultExecutor
IO thread.id kotlinx.coroutines.DefaultExecutor
IO thread.id kotlinx.coroutines.DefaultExecutor
Default IO took 304ms
Is it safe to say here then, that
doWorkWithIO
is executing serially whereas
doWorkWithFork
is concurrent?
p
it’s sequential/serial or concurrent/independent/parallel depending on the API you use
IO.parMap
is also concurrent