Skip to content

Commit 6edd9f3

Browse files
Write a Ractor benchmark that mirrors the process structure
The previous ractor knucleotide benchmark ran the whole benchmark in parallel inside multiple ractors. This is a good test that the benchmark can be parallelised but isn't directly comparable to the original Knucleotide implementation which uses processes to partition the work done by the benchmark itself. This implementation uses ractors to parallelise the work in the same way that the original uses Process.fork so it's more directly comparable. This needs to be run with the regular benchmark harness instead of the ractor harness, otherwise the ractor harness will attempt to wrap this benchmark run in multiple ractors too.
1 parent e43471e commit 6edd9f3

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

benchmarks.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ graphql-native:
9191
desc: GraphQL gem parsing a large file, but using a native parser
9292
knucleotide:
9393
desc: k-nucleotide from the Computer Language Benchmarks Game - counts nucleotide frequencies using hash tables in parallel using Process.fork
94+
knucleotide-ractor:
95+
desc: k-nucleotide from the Computer Language Benchmarks Game - counts nucleotide frequencies using hash tables in parallel using Ractors
9496
lee:
9597
desc: lee is a circuit-board layout solver, deployed in a plausibly reality-like way
9698
matmul:
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# The Computer Language Benchmarks Game
2+
# https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
3+
#
4+
# k-nucleotide benchmark - Ractor implementation
5+
# Mirrors the Process.fork version: spawns 7 ractors (one per task)
6+
7+
Warning[:experimental] = false
8+
9+
require_relative '../../harness/loader'
10+
11+
def frequency(seq, length)
12+
frequencies = Hash.new(0)
13+
last_index = seq.length - length
14+
15+
i = 0
16+
while i <= last_index
17+
frequencies[seq.byteslice(i, length)] += 1
18+
i += 1
19+
end
20+
21+
[seq.length - length + 1, frequencies]
22+
end
23+
24+
def sort_by_freq(seq, length)
25+
n, table = frequency(seq, length)
26+
27+
table.sort { |a, b|
28+
cmp = b[1] <=> a[1]
29+
cmp == 0 ? a[0] <=> b[0] : cmp
30+
}.map { |seq, count|
31+
"#{seq} #{'%.3f' % ((count * 100.0) / n)}"
32+
}.join("\n") + "\n\n"
33+
end
34+
35+
def find_seq(seq, s)
36+
_, table = frequency(seq, s.length)
37+
"#{table[s] || 0}\t#{s}\n"
38+
end
39+
40+
def generate_test_sequence(size)
41+
alu = "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA" +
42+
"GGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGG" +
43+
"TGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTT" +
44+
"GCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"
45+
46+
sequence = ""
47+
full_copies = size / alu.length
48+
remainder = size % alu.length
49+
50+
full_copies.times { sequence << alu }
51+
sequence << alu[0, remainder] if remainder > 0
52+
53+
sequence.upcase
54+
end
55+
56+
TEST_SEQUENCE = Ractor.make_shareable(generate_test_sequence(100_000))
57+
58+
run_benchmark(5) do
59+
freqs = [1, 2]
60+
nucleos = %w(GGT GGTA GGTATT GGTATTTTAATT GGTATTTTAATTTATAGT)
61+
62+
ractors = freqs.map { |i|
63+
Ractor.new(TEST_SEQUENCE, i) { |seq, len| sort_by_freq(seq, len) }
64+
}
65+
ractors += nucleos.map { |s|
66+
Ractor.new(TEST_SEQUENCE, s) { |seq, nucleo| find_seq(seq, nucleo) }
67+
}
68+
69+
results = ractors.map(&:take)
70+
results
71+
end

0 commit comments

Comments
 (0)