Data update

This commit is contained in:
Ingy döt Net 2023-12-16 21:33:55 -08:00
parent 35bcdeebf8
commit 74c69a0df6
2427 changed files with 31826 additions and 3468 deletions

View file

@ -1,23 +1,23 @@
#| Recursive, single-thread, mergesort implementation
sub mergesort ( @a ) {
return @a if @a <= 1;
return @a if @a <= 1;
# recursion step
my $m = @a.elems div 2;
my @l = samewith @a[ 0 ..^ $m ];
my @r = samewith @a[ $m ..^ @a ];
# recursion step
my $m = @a.elems div 2;
my @l = samewith @a[ 0 ..^ $m ];
my @r = samewith @a[ $m ..^ @a ];
# short cut - in case of no overlapping left and right parts
# short cut - in case of no overlapping in left and right parts
return flat @l, @r if @l[*-1] !after @r[0];
return flat @r, @l if @r[*-1] !after @l[0];
# merge step
return flat gather {
take @l[0] before @r[0]
?? @l.shift
!! @r.shift
while @l and @r;
return flat gather {
take @l[0] before @r[0]
?? @l.shift
!! @r.shift
while @l and @r;
take @l, @r;
}
take @l, @r;
}
}

View file

@ -1,26 +1,29 @@
#| Recursive, naive parallel, mergesort implementation
proto mergesort-parallel-naive(| --> Positional) {*}
multi mergesort-parallel-naive(@unsorted where @unsorted.elems < 2) { @unsorted }
multi mergesort-parallel-naive(@unsorted where @unsorted.elems == 2) {
@unsorted[0] after @unsorted[1]
?? (@unsorted[1], @unsorted[0])
!! @unsorted
}
multi mergesort-parallel-naive(@unsorted) {
my $mid = @unsorted.elems div 2;
my Promise $left-sorted = start { flat samewith @unsorted[ 0 ..^ $mid ] };
my @right-sorted = flat samewith @unsorted[ $mid ..^ @unsorted.elems ];
#| Recursive, naive multi-thread, mergesort implementation
sub mergesort-parallel-naive ( @a ) {
return @a if @a <= 1;
await $left-sorted andthen my @left-sorted = $left-sorted.result;
my $m = @a.elems div 2;
return flat @left-sorted, @right-sorted if @left-sorted[*-1] !after @right-sorted[0];
return flat @right-sorted, @left-sorted if @right-sorted[*-1] !after @left-sorted[0];
# recursion step launching new thread
my @l = start { samewith @a[ 0 ..^ $m ] };
return flat gather {
take @left-sorted[0] before @right-sorted[0]
?? @left-sorted.shift
!! @right-sorted.shift
while @left-sorted.elems and @right-sorted.elems;
take @left-sorted, @right-sorted;
# meanwhile recursively sort right side
my @r = samewith @a[ $m ..^ @a ] ;
# as we went parallel on left side, we need to await the result
await @l[0] andthen @l = @l[0].result;
# short cut - in case of no overlapping left and right parts
return flat @l, @r if @l[*-1] !after @r[0];
return flat @r, @l if @r[*-1] !after @l[0];
# merge step
return flat gather {
take @l[0] before @r[0]
?? @l.shift
!! @r.shift
while @l and @r;
take @l, @r;
}
}

View file

@ -1,47 +1,31 @@
constant $BATCH-SIZE = 2**10;
my atomicint $worker = $*KERNEL.cpu-cores;
#| Recursive, batch tuned multi-thread, mergesort implementation
sub mergesort-parallel ( @a, $batch = 2**9 ) {
return @a if @a <= 1;
#| Recursive, parallel, tuned, mergesort implementation
proto mergesort-parallel(| --> Positional) {*}
multi mergesort-parallel(@unsorted where @unsorted.elems < 2) { @unsorted }
multi mergesort-parallel(@unsorted where @unsorted.elems == 2) {
@unsorted[0] after @unsorted[1]
?? (@unsorted[1], @unsorted[0])
!! @unsorted
}
multi mergesort-parallel(@unsorted) {
my $mid = @unsorted.elems div 2;
my $m = @a.elems div 2;
# atomically decide if we run left side on a new thread
my $left-sorted =$worker > 0 &&
$mid > $BATCH-SIZE
?? (
$worker--;
start {
LEAVE $worker++;
samewith @unsorted[ 0 ..^ $mid ]
}
)
!! samewith @unsorted[ 0 ..^ $mid ];
# recursion step
my @l = $m >= $batch
?? start { samewith @a[ 0 ..^ $m ], $batch }
!! samewith @a[ 0 ..^ $m ], $batch ;
# recursion on the right side using current thread
my @right-sorted = samewith @unsorted[ $mid ..^ @unsorted.elems ];
# meanwhile recursively sort right side
my @r = samewith @a[ $m ..^ @a ], $batch;
# await calculation of left side
await $left-sorted andthen $left-sorted = flat $left-sorted.result
if $left-sorted ~~ Promise;
my @left-sorted = flat $left-sorted;
# if we went parallel on left side, we need to await the result
await @l[0] andthen @l = @l[0].result if @l[0] ~~ Promise;
# short cut - in case of no overlapping left and right parts
return flat @left-sorted, @right-sorted if @left-sorted[*-1] !after @right-sorted[0];
return flat @right-sorted, @left-sorted if @right-sorted[*-1] !after @left-sorted[0];
return flat @l, @r if @l[*-1] !after @r[0];
return flat @r, @l if @r[*-1] !after @l[0];
# merge step
return flat gather {
take @left-sorted[0] before @right-sorted[0]
?? @left-sorted.shift
!! @right-sorted.shift
while @left-sorted.elems and @right-sorted.elems;
take @left-sorted, @right-sorted;
take @l[0] before @r[0]
?? @l.shift
!! @r.shift
while @l and @r;
take @l, @r;
}
}

View file

@ -1,31 +1,18 @@
say "x" x 10 ~ " Testing " ~ "x" x 10;
use Test;
my @functions-under-test = &mergesort, &mergesort-parallel-naive, &mergesort-parallel;
my @testcases =
() => (),
<a>.List => <a>.List,
<a a> => <a a>,
<a b> => <a b>,
<b a> => <a b>,
("b", "a", 3) => (3, "a", "b"),
<h b a c d f e g> => <a b c d e f g h>,
(2, 3, 1, 4, 5) => (1, 2, 3, 4, 5),
<a 🎮 3 z 4 🐧> => <a 🎮 3 z 4 🐧>.sort
;
my @implementations = &mergesort, &mergesort-parallel, &mergesort-parallel-naive;
plan @testcases.elems * @implementations.elems;
for @implementations -> &fun {
plan @testcases.elems * @functions-under-test.elems;
for @functions-under-test -> &fun {
say &fun.name;
is-deeply &fun(.key), .value, .key ~ " => " ~ .value for @testcases;
}
done-testing;
use Benchmark;
my $elem-length = 8;
my @unsorted of Str = ('a'..'z').roll($elem-length).join xx 10 * $worker * $BATCH-SIZE;
my $runs = 10;
say "Benchmarking by $runs times sorting {@unsorted.elems} strings of size $elem-length - using batches of $BATCH-SIZE strings and $worker workers for mergesort-parallel().";
say "Hint: watch the number of Raku threads in Activity Monitor on Mac, Ressource Monitor on Windows or htop on Linux.";
for @implementations -> &fun {
print &fun.name, " => avg: ";
my ($start, $end, $diff, $avg) = timethis $runs, sub { &fun(@unsorted) }
say "$avg secs, total $diff secs";
}

View file

@ -0,0 +1,27 @@
use Benchmark;
my $runs = 5;
my $elems = 10 * Kernel.cpu-cores * 2**10;
my @unsorted of Str = ('a'..'z').roll(8).join xx $elems;
my UInt $l-batch = 2**13;
my UInt $m-batch = 2**11;
my UInt $s-batch = 2**9;
my UInt $t-batch = 2**7;
say "elements: $elems, runs: $runs, cpu-cores: {Kernel.cpu-cores}, large/medium/small/tiny-batch: $l-batch/$m-batch/$s-batch/$t-batch";
my %results = timethese $runs, {
single-thread => { mergesort(@unsorted) },
parallel-naive => { mergesort-parallel-naive(@unsorted) },
parallel-tiny-batch => { mergesort-parallel(@unsorted, $t-batch) },
parallel-small-batch => { mergesort-parallel(@unsorted, $s-batch) },
parallel-medium-batch => { mergesort-parallel(@unsorted, $m-batch) },
parallel-large-batch => { mergesort-parallel(@unsorted, $l-batch) },
}, :statistics;
my @metrics = <mean median sd>;
my $msg-row = "%.4f\t" x @metrics.elems ~ '%s';
say @metrics.join("\t");
for %results.kv -> $name, %m {
say sprintf($msg-row, %m{@metrics}, $name);
}