I was quite surprised that Truffleruby (23.1.1) was so much faster than Crystal (1.10.1) for this task.
Apparently the Graal VM is doing some very good stuff here.
I installed Truffleruby on my Linux system via rvm (ruby version manager) – https://rvm.io/
I first wrote this in Ruby – residue_gaps.rb
def residue_gaps(pm)
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43]
i = primes.index(pm)
modpn = primes[0..i].reduce :* # modulus is primorial pm#
res_gaps = Hash.new(0) # create hash of residue gaps
res_gaps[2] = 1; res_gaps[4] = 1 # initial gap counts of 2 and 4
r1, r2 = 1, 2 + pm # initial first two residues
while r2 < modpn / 2 # for first half modulus residues
if r2.gcd(modpn) == 1 # if r2 is a residue
res_gaps[r2 - r1] += 2 # compute gap size, double cnt for mirror pair
r1 = r2 # set old residue to current residue
end
r2 += 2 # set next odd value to current residue
end
res_gaps.to_a.sort! # output array of [gap size, gap count]
end
pm = ARGV[0].to_i # read prime value from terminal
print residue_gaps(pm) # display array of ai gap values
puts
Then wrote it in Crystal – residue_gaps.cr
def residue_gaps(pm)
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43] of UInt64
i = primes.index(pm).not_nil!
modpn = primes[0..i].product # modulus is primorial pm#
res_gaps = Hash(Int32, UInt64).new(0) # create hash of residue gaps
res_gaps[2] = 1; res_gaps[4] = 1 # initial gap counts of 2 and 4
r1, r2 = 1u64, 2u64 + pm # initial first two residues
while r2 < modpn // 2 # for first half modulus residues
if r2.gcd(modpn) == 1 # if r2 is a residue
res_gaps[(r2 - r1).to_i32] += 2 # compute gap size, double cnt for mirror pair
r1 = r2 # set old residue to current residue
end
r2 += 2 # set next odd value to current residue
end
res_gaps.to_a.sort! # output array of [gap size, gap count]
end
pm = ARGV[0].to_u64 # read prime value from terminal
print residue_gaps(pm) # display array of ai gap values
puts
$ crystal build --release residue_gaps.cr
Then run them like this:
$ time ruby residue_gaps.rb 17
[[2, 22275], [4, 22275], [6, 26630], [8, 6812], [10, 7734], [12, 4096], [14, 1406], [16, 432], [18, 376], [20, 24], [22, 78], [24, 20], [26, 2]]
ruby residue_gaps.rb 17 0.05s user 0.01s system 99% cpu 0.095 total
$ time ./residue_gaps 17
[{2, 22275}, {4, 22275}, {6, 26630}, {8, 6812}, {10, 7734}, {12, 4096}, {14, 1406}, {16, 432}, {18, 376}, {20, 24}, {22, 78}, {24, 20}, {26, 2}]
./residue_gaps 17 0.01s user 0.00s system 97% cpu 0.010 total
And here are timing comparisons (in seconds)
prime inputs | 11 | 13 | 17 | 19 | 23 | 29 | 31 |
--------------------|--------|--------|--------|--------|--------|--------|--------|
crystal 1.10.1 | 0.002 | 0.002 | 0.010 | 0.193 | 5.123 | 175.63 |6217.55 |
--------------------|--------|--------|--------|--------|--------|--------|--------|
truffleruby 23.1.1 | 0.045 | 0.060 | 0.095 | 0.310 | 2.968 | 91.66 |3297.21 |
As the primes increase Truffleruby becomes almost twice as fast as Crystal.
Can Crystal learn something from this?