This post originated from an RSS feed registered with Ruby Buzz
by Jan Lelis.
Original Post: Converting decimal to binary integers: custom methods vs. to_i [Update]
Feed Title: rbJ*_*L.net
Feed URL: http://feeds.feedburner.com/rbJL
Feed Description: Hi, I am a fan of Ruby and like to explore it and the world around ;).
So I started this blog, where I am publishing code snippets, tutorials for beginners as well as general thoughts about Ruby, the web or programming in general.
At my last entry [binary representation of index-arrays], a question arose about what is the most efficient way to convert integers between the bases 2 and 10: either using built-in ruby methods (and even do lightweight string-operations) or calculating it manually. I had to find out ;). So I have written a little benchmark program, which does the conversion in three different ways:
using built-in to_i-magic
calculating it by hand
using sprintf
It stops the time each method needs to get the fastest. The result might be surprising. [Update: improved the custom methods]In most cases, the built-in functions are slightly faster than the custom methods. The following extract shows the output on my machine, using Ruby 1.9.1p0:
Speed test: using to_i and to_s - decimal to binary...0.15s
Speed test: using to_i and to_s - binary to decimal...0.08s
Methods ran correctly? yes
Speed test: calculating it - decimal to binary...0.31s
Speed test: calculating it - binary to decimal...0.11s
Methods ran correctly? yes
Speed test: using sprintf - decimal to binary...0.16s
Speed test: using sprintf - binary to decimal...0.10s
Methods ran correctly? yes
In Ruby 1.8.6p368, everything is a little bit slower:
Speed test: using to_i and to_s - decimal to binary...0.22s
Speed test: using to_i and to_s - binary to decimal...0.30s
Methods ran correctly? yes
Speed test: calculating it - decimal to binary...0.33s
Speed test: calculating it - binary to decimal...0.30s
Methods ran correctly? yes
Speed test: using sprintf - decimal to binary...0.26s
Speed test: using sprintf - binary to decimal...0.32s
Methods ran correctly? yes
An explanation for this is, that the built-in methods can calculate the result using native C, which often beats ruby implementations.
.to_i / .to_s is better than sprintf is better than calculating it manually.
I have also tried JRuby (1.3.1 —1.9) and the manual conversion was even worse:
Speed test: using to_i and to_s - decimal to binary...0.15s
Speed test: using to_i and to_s - binary to decimal...0.26s
Methods ran correctly? yes
Speed test: calculating it - decimal to binary...0.34s
Speed test: calculating it - binary to decimal...0.41s
Methods ran correctly? yes
Speed test: using sprintf - decimal to binary...0.13s
Speed test: using sprintf - binary to decimal...0.33s
Methods ran correctly? yes
Without the 1.9 switch, most times improve (but are still worse at calculating it in Ruby):
Speed test: using to_i and to_s - decimal to binary...0.13s
Speed test: using to_i and to_s - binary to decimal...0.30s
Methods ran correctly? yes
Speed test: calculating it - decimal to binary...0.37s
Speed test: calculating it - binary to decimal...0.35s
Methods ran correctly? yes
Speed test: using sprintf - decimal to binary...0.13s
Speed test: using sprintf - binary to decimal...0.27s
Methods ran correctly? yes
# # # # # # # # ## Test, which way to convert the base of an integer from 2 to 10 and vice versa,# is the most efficient oneclass STattr_reader:dec_to_bin,:bin_to_dec,:correct# abstract methodsdef desc;enddef speedtest_dec_to_bin;enddef speedtest_bin_to_dec;end# stop timedef timer(&proc)started_at=Time.nowyieldifblock_given?stopped_at=Time.nowres=stopped_at-started_atputs'%.2fs'%resresend# do the testingdef speedtest(to_test=(42**42)**42*(42**42)**42)@cur=to_testprint'Speed test: '<<desc<<' - decimal to binary...'@dec_to_bin=speedtest_dec_to_binprint'Speed test: '<<desc<<' - binary to decimal...'@bin_to_dec=speedtest_bin_to_decputs'Methods ran correctly? '<<((@correct=(@cur==to_test))?'yes':'no')endend# ruby's .to_i and .to_s functionsclass ST::To_i<STdef desc;'using to_i and to_s';enddef speedtest_dec_to_bintimer{@cur=@cur.to_s(2).to_i}enddef speedtest_bin_to_dectimer{@cur=@cur.to_s.to_i(2)}endend# calculate itclass ST::Calc<STdef desc;'calculating it';enddef speedtest_dec_to_bintimer{res=[]while@cur!=0# res << (@cur.odd? ? '1' : '0')# @cur >>= 1new_cur=@cur>>1res<<((@cur==new_cur<<1)?'0':'1')@cur=new_curend@cur=res.reverse.join.to_i}enddef speedtest_bin_to_dectimer{res=0cur_exp=1cur_missing_1=0# counts the 0s for shifting@cur.to_s.reverse.each_byte{|set|ifset==49cur_exp<<=cur_missing_1res+=cur_expcur_missing_1=1elsecur_missing_1+=1end}@cur=res}endend# using sprintfclass ST::Sprintf<STdef desc;'using sprintf';enddef speedtest_dec_to_bintimer{@cur=('%b'%@cur.to_s).to_i}enddef speedtest_bin_to_dectimer{@cur=('%d'%('0b'+@cur.to_s)).to_i}endend# for the lazy ones ;)def ST.demojl1=ST::To_i.newjl1.speedtestjl2=ST::Calc.newjl2.speedtestjl3=ST::Sprintf.newjl3.speedtestend# let's startST.demo
If you know a way to improve the manual calculating, please let me know ;)