Home > Raku > Spawnratelimiting

Spawnratelimiting

IRC keeps insisting on being a good source of thought. A few days ago rcmlz wished to parallelise quicksort. Raku doesn’t sport a facility to make recursion “just work” with oodles of cores. Quicksort is a little special in that regard, as it has a double tail-call.

quicksort(@before), $pivot, quicksort(@after)

Just wrapping the two calls into start-block will spawn way to many threads. Ergo, I need a function that takes another function and it’s arguments and limits the use of start-blocks.

my atomicint $spawn-limit = 8;
sub spawn(&c, |args) {
    # dd ⚛$spawn-limit;
    ⚛$spawn-limit > 0
        ?? ($spawn-limit⚛-- ;start { LEAVE $spawn-limit⚛++; c(|args) })
        !! c(|args)
}

This Routine will either return whatever c returns or a Promise. I need a way to convert the latter to values.

sub collect(*@things) {
   slip @things.map: -> $thing { $thing ~~ Promise ?? await $thing !! $thing }
}

Now I can change quicksort without getting into trouble with oversharing objects.

multi quicksort([]) { () }
multi quicksort([$pivot, *@rest]) {
    my @before = @rest.grep(* before $pivot);
    my @after  = @rest.grep(* !before $pivot);

    flat collect spawn(&?ROUTINE.dispatcher, @before), $pivot, spawn(&?ROUTINE.dispatcher, @after)
}

my @a = ('a'..'z').roll(12).join xx 2**16;
say quicksort @a;

# OUTPUT:
# Flattened array has 65536 elements, but argument lists are limited to 65535
#   in sub quicksort at tmp/2021-03-08.raku line 2601
#   in block <unit> at tmp/2021-03-08.raku line 2611

Well, I could if Rakudo would be production ready. This bug is old and awful. Test driven programmers like to program test-driven but don’t fancy to wait for hours to see tests complete. As a result very few test with large datasets. (And 65536 elements is not much these days.) It’s fairly easy to start with a project in production that slowly grows it’s database and eventually fails with a runtime error.

At least for now, destructuring is best done by hand.

Categories: Raku