Part 2 could definitely use some parallelism to sp...
# advent-of-code
e
Part 2 could definitely use some parallelism to speed up destroying the units and what-not, but I can't figure out how to do it. I've never worked with coroutines, and this parallel map function I found and pasted in my code doesn't seem to work, everything still happens sequentially. https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/
a
I think that the best set of examples of coroutines are possibly on this page: https://www.baeldung.com/kotlin-coroutines For the parallel processing of elements, have a look at Section 5 on this page.
j
I have made a parallel version of my solution using coroutines, you can find it here: https://pastebin.com/GRG3zvZE The inital run is actually slower, but after JVM warmup it seems about twice as fast on my machine
The interesting part is:
Copy code
('a'..'z')
    .map { unit ->
        GlobalScope.async {
            firstPolymer.reduce(unit).count()
        }
    }
    .map { it.await() }
    .min()
    .print { "Minimum length : $it" }
Here we first map the alphabet to a list of
Job
instances, with each Job taking care of ignoring a character. (Note that using
GlobalScope
is bad form in general, but good enough for now.) In the second map, we wait for each
Job
in turn to finish, giving us our list of Ints
👍 1
This uses the default dispacher, which uses as much threads as you have cores
e
Thanks! I tried this and it worked (nearly 4 times faster). All I did was add
GlobalScope.
to the async command from the post I linked. Why did it not work before, though?
j
The example in the blog post uses an older version of coroutines, which didn't have the notion of structured concurrency that the latest version uses/requires. (https://medium.com/@elizarov/structured-concurrency-722d765aa952)
Oh wait, that's not true actually, the code in the example does use structured concurrency because the
runBlocking
defines a scope
Ah, I know - by default,
runBlocking
uses a single thread for execution (the current calling thread), which means all Jobs would still use the same thread. By using
GlobalScope
, the Jobs are started on a different scope, which does use the default dispatcher and therefore multiple threads. The same could be achieved by changing the code in the blog post to
runBlocking(Dispatchers.Default) { ... }
, which makes
runBlocking
use the default dispatcher as well. I suspect the original code worked fine before the introduction of structured concurrency, but should be updated now.
e
Gotcha, thanks! I should read up more on this, sounds pretty fun. 😄