From: "Brian Schröder" Date: 2005-09-11T04:34:44+09:00 Subject: Re: Better Random benchmark - need help with profiling On 10/09/05, Martin Jansson wrote: > > I recently started to learn Ruby. I thought that the obviously flawed > Random > benchmark at http://shootout.alioth.debian.org would make a good first > project. > > I need some help with profiling as my system is a lot different from the > shootout setup. My system is a iBook G4 with Mac OS 10.3 and Ruby 1.8. They > use Linux on AMD with Ruby 1.9. > > The original implementation have two eyecatching flaws: > 1) It use Floats instead of Fixnum > 2) It uses a really costly way to get the first program parameter. > > After I corrected these two misstakes it used 30% less time and > considerably less memory (at least half as much, hard to measure). > > Then I started from scratch and wrote my own version, doing a lot of > experimentation. This is the result: > > # Code start > > module Kernel > $srandom = 42 > def random(max) > (max * ($srandom = ($srandom * 3877 + 29573) % 139968)).quo(139968) > end > end > > ($*.first.to_i-1).times do > random(100) > end > > printf "%.9f\n", random(100) > > # Code end > > > The most important qualities is (in order of saved time): > * Use Fixnum for integer arithmetics. Much faster and with better > precision. > * Use ARGV.first instead of ARGV.shift to get first parameter > * Use literals instead of constants. Ruby constants is no real constants, > they are global variables with A LOT of overhead. The specification > C-program use literals. An alternative would be to use > local variables. > * Use $* instead of ARGV. Sigh. Ruby constants are really, reeeally > slooooow. > * Use times as iterator. As do the original solution. > * Use a global variable to hold the seed. As do the original solution. > I can't find a sane way to keep the seed between method calls without > consuming a lot of precious time. > * Wraped the function in module Kernel. For some odd reason this makes the > program somewhat faster. I really don't like it, but... > > I've also tried a nice Simula flavoured version an, not so nice, > "oo"-version and a lot of really wacky versions. > They are a lot faster then the original code, but slower then the above > version. > > Risking to be burned because I'm somehow helping with the aliot things here are my 2cents bschroed@black:~/svn/projekte/random$ cat random.rb module Kernel $srandom = 42 def random(max) (max * ($srandom = ($srandom * 3877 + 29573) % 139968)).quo(139968) end end ($*.first.to_i-1).times do random(100) end printf "%.9f\n", random(100) bschroed@black:~/svn/projekte/random$ cat random1.rb class RandomNumberGenerator def initialize(seed = 42) @seed = seed end def random(max) (max * (@seed = (@seed * 3877 + 29573) % 139968)).quo(139968) end end rng = RandomNumberGenerator.new (ARGV[0].to_i-1).times do rng.random(100) end puts "%.9f" % rng.random(100) bschroed@black:~/svn/projekte/random$ cat benchmark-random.rb require 'benchmark' Benchmark.bm(30) do | b | [["random.rb", "Original"], ["random1.rb", "Rubiesque"]].each do | file, title | b.report(title) do 200.times do system "ruby #{file} 5000 > /dev/null" end end end end bschroed@black:~/svn/projekte/random$ ruby benchmark-random.rb user system total real Original 0.020000 0.050000 9.620000 ( 9.583356) Rubiesque 0.010000 0.060000 9.820000 ( 9.792614) As you see, it is quite cheap to write prettier code. And the modified version even keeps the state in a class-variable, so you can have multiple rng's with differen states around. hope to help, Brian -- http://ruby.brian-schroeder.de/ Stringed instrument chords: http://chordlist.brian-schroeder.de/