Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add benchmarks from HN blog post #260

Merged
merged 2 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ rubykon:
desc: Ruby solver for Go (the boardgame.) Runs 1,000 iterations forward from an initial starting board.
tinygql:
desc: TinyGQL gem parsing a large file in pure Ruby
nqueens:
desc: solver for the N-Queens problem
sudoku:
desc: sudoku solver
matmul:
desc: matrix multiplication benchmark

#
# MicroBenchmarks
Expand Down
43 changes: 43 additions & 0 deletions benchmarks/matmul.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require_relative '../harness/loader'

def matgen(n)
tmp = 1.0 / n / n
a = Array.new(n) { Array.new(n) { 0 } }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an example of something suboptimal and somewhat not idiomatic, Array.new(n, 0) is much faster.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whereas I looked at that and thought, "oh, sometimes I see people use the default value like this to avoid having to do early initialization, I'm glad we're getting a benchmark for it".

But you're right, it's the exception and not the rule.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outer Array.new needs to use a block, so that form is getting benchmarked anyway :)

for i in 0 .. n-1
for j in 0 .. n-1
a[i][j] = tmp * (i - j) * (i + j)
end
end
return a
end

def matmul(a, b)
m = a.length
n = a[0].length
p = b[0].length
c = Array.new(m) { Array.new(p) { 0 } }
for i in 0 .. m-1
ci = c[i]
for k in 0 .. n-1
aik = a[i][k]
bk = b[k]
for j in 0 .. p-1
ci[j] += aik * bk[j]
end
end
end
return c
end

n = 300
if ARGV.length >= 1
n = ARGV[0].to_i
end
n = n / 2 * 2

run_benchmark(20) do
a = matgen(n)
b = matgen(n)
c = matmul(a, b)
#puts c[n/2][n/2]
end
44 changes: 44 additions & 0 deletions benchmarks/nqueens.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require_relative '../harness/loader'

def nq_solve(n)
a = Array.new(n) { -1 }
l = Array.new(n) { 0 }
c = Array.new(n) { 0 }
r = Array.new(n) { 0 }
y0 = (1<<n) - 1
m = 0
k = 0
while k >= 0 do
y = (l[k] | c[k] | r[k]) & y0
if (y ^ y0) >> (a[k] + 1) != 0 then
i = a[k] + 1
while i < n and (y & 1<<i) != 0 do
i += 1
end
if k < n - 1 then
z = 1<<i
a[k] = i
k += 1
l[k] = (l[k-1]|z)<<1
c[k] = c[k-1]|z
r[k] = (r[k-1]|z)>>1
else
m += 1
k -= 1
end
else
a[k] = -1
k -= 1
end
end
return m
end

n = 12
if ARGV.length() > 0 then
n = ARGV[0].to_i
end

run_benchmark(40) do
nq_solve(n)
end
135 changes: 135 additions & 0 deletions benchmarks/sudoku.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
require_relative '../harness/loader'

def sd_genmat()
mr = Array.new(324) { [] }
mc = Array.new(729) { [] }
r = 0
(0...9).each do |i|
(0...9).each do |j|
(0...9).each do |k|
mc[r] = [ 9 * i + j, (i/3*3 + j/3) * 9 + k + 81, 9 * i + k + 162, 9 * j + k + 243 ]
r += 1
end
end
end
(0...729).each do |r|
(0...4).each do |c2|
mr[mc[r][c2]].push(r)
end
end
return mr, mc
end

def sd_update(mr, mc, sr, sc, r, v)
min, min_c = 10, 0
(0...4).each do |c2|
if v > 0 then sc[mc[r][c2]] += 128
else sc[mc[r][c2]] -= 128 end
end
(0...4).each do |c2|
c = mc[r][c2]
if v > 0 then
(0...9).each do |r2|
rr = mr[c][r2]
sr[rr] += + 1
if sr[rr] == 1 then
p = mc[rr]
sc[p[0]] -= 1; sc[p[1]] -= 1; sc[p[2]] -= 1; sc[p[3]] -= 1
if sc[p[0]] < min then min, min_c = sc[p[0]], p[0] end
if sc[p[1]] < min then min, min_c = sc[p[1]], p[1] end
if sc[p[2]] < min then min, min_c = sc[p[2]], p[2] end
if sc[p[3]] < min then min, min_c = sc[p[3]], p[3] end
end
end
else
(0...9).each do |r2|
rr = mr[c][r2]
sr[rr] -= 1
if sr[rr] == 0 then
p = mc[rr]
sc[p[0]] += 1; sc[p[1]] += 1; sc[p[2]] += 1; sc[p[3]] += 1
end
end
end
end
return min, min_c
end

def sd_solve(mr, mc, s)
ret, sr, sc, hints = [], Array.new(729) { 0 }, Array.new(324) { 9 }, 0
(0...81).each do |i|
a = (s[i].chr >= '1' and s[i].chr <= '9')? s[i].ord - 49 : -1
if a >= 0 then sd_update(mr, mc, sr, sc, i * 9 + a, 1); hints += 1 end
end
cr, cc = Array.new(81) { -1 }, Array.new(81) { 0 }
i, min, dir = 0, 10, 1
loop do
while i >= 0 and i < 81 - hints do
if dir == 1 then
if min > 1 then
(0...324).each do |c|
if sc[c] < min then
min, cc[i] = sc[c], c
if min < 2 then break end
end
end
end
if min == 0 or min == 10 then cr[i], dir, i = -1, -1, i - 1 end
end
c = cc[i]
if dir == -1 and cr[i] >= 0 then sd_update(mr, mc, sr, sc, mr[c][cr[i]], -1) end
r2_ = 9
(cr[i]+1...9).each do |r2|
if sr[mr[c][r2]] == 0 then r2_ = r2; break end
end
if r2_ < 9 then
min, cc[i+1] = sd_update(mr, mc, sr, sc, mr[c][r2_], 1)
cr[i], dir, i = r2_, 1, i + 1
else cr[i], dir, i = -1, -1, i - 1 end
end
if i < 0 then break end
o = []
(0...81).each do |j| o.push(s[j].ord - 48) end
(0...i).each do |j|
r = mr[cc[j]][cr[j]]
o[r/9] = r % 9 + 1
end
ret.push(o)
i, dir = i - 1, -1
end
return ret
end

hard20 = [
"..............3.85..1.2.......5.7.....4...1...9.......5......73..2.1........4...9",
".......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6...",
".2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9..",
"........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........",
"12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8",
"1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1",
".......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7.....",
"12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8",
"..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5.",
"1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6.",
"..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7..",
"....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7",
"4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........",
"7.8...3.....2.1...5.........4.....263...8.......1...9..9.6....4....7.5...........",
"3.7.4...........918........4.....7.....16.......25..........38..9....5...2.6.....",
"........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3",
".......1.4.........2...........5.4.7..8...3....1.9....3..4..2...5.1........8.6...",
".......12....35......6...7.7.....3.....4..8..1...........12.....8.....4..5....6..",
"1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1",
".....1.2.3...4.5.....6....7..2.....1.8..9..3.4.....8..5....2....9..3.4....67....."
]
mr, mc = sd_genmat()

run_benchmark(20) do
n = 4
(0...n).each do |i|
hard20.each do |l|
ret = sd_solve(mr, mc, l)
ret.each do |s| s.join end
end
end
end
Loading