From: Ryan Davis Date: 2008-02-21T07:10:25+09:00 Subject: Re: IP Address to Decimal - inject sucks On Feb 20, 2008, at 11:50 , Alex Shulgin wrote: > On Feb 20, 3:38 am, Tiago Pinto wrote: >> >> What do you think? There's a way to do this? Is this just a silly >> question? >> >> I know, we should be working, but we both love Ruby. > > Everybody replied already, still I think left-shift deserves > mentioning: > > "127.0.0.1".split('.').inject(0) { |s,x| (s << 8) + x.to_i } > => 2130706433 It does, and it is the only inject form I consider valid. However, converting it to map leads to a 20-25% reduction in time. Ara's pack/unpack is always what I intuitively reach for in a problem like this, but it turns out to be more expensive than a simple map/ left-shift form. IPAddr is very powerful, but there is a cost to that power (parsing every IP form under the sun). If I needed anything more than JUST converting to/from ints I'd use IPAddr. # of iterations = 100000 user system total real null_time 0.020000 0.000000 0.020000 ( 0.017184) split/map/<<+ 1.040000 0.000000 1.040000 ( 1.042783) split/map/pack/unpack 1.180000 0.000000 1.180000 ( 1.177197) split/inject/<<+ 1.310000 0.000000 1.310000 ( 1.308850) split/inject/+* 1.430000 0.010000 1.440000 ( 1.436653) split/inject/+*= 1.640000 0.000000 1.640000 ( 1.637948) split/zip/inject/+* 2.290000 0.000000 2.290000 ( 2.288469) ohdeargodno 2.670000 0.000000 2.670000 ( 2.678789) ipaddr/to_i 3.940000 0.010000 3.950000 ( 3.950504) # of iterations = 1000000 user system total real null_time 0.130000 0.000000 0.130000 ( 0.131523) split/map/<< 10.490000 0.010000 10.500000 ( 10.578584) split/map/pack/unpack 12.360000 0.010000 12.370000 ( 12.383726) split/inject/<< 14.130000 0.030000 14.160000 ( 14.202936) split/inject/+* 15.700000 0.040000 15.740000 ( 15.872160) split/inject/+*= 18.000000 0.050000 18.050000 ( 18.176626) split/zip/inject/+* 25.290000 0.060000 25.350000 ( 25.530274) ohdeargodno 28.350000 0.070000 28.420000 ( 28.551007) ipaddr/to_i 41.570000 0.080000 41.650000 ( 41.822529) #!/usr/bin/env ruby -w require 'benchmark' require 'ipaddr' max = (ARGV.shift || 1_000_000).to_i puts "# of iterations = #{max}" Benchmark::bm(22) do |x| x.report("null_time") do for i in 0..max do # do nothing end end x.report("split/map/<<+") do for i in 0..max do ip = 0 "127.0.0.1".split('.').map { |n| ip = (ip << 8) + n.to_i } end end x.report("split/map/pack/unpack") do for i in 0..max do "127.0.0.1".split(".").map{|s| s.to_i }.pack("C*").unpack("N") end end x.report("split/inject/<<+") do for i in 0..max do "127.0.0.1".split('.').inject(0) { |s,n| (s << 8) + n.to_i } end end x.report("split/inject/+*") do for i in 0..max do '127.0.0.1'.split('.').inject(0) { |t,v| v.to_i + t*256 } end end x.report("split/inject/+*=") do for i in 0..max do "127.0.0.1".split(".").inject(0) { |s,i| s+=i.to_i; s *= 256 } / 256 end end x.report("split/zip/inject/+*") do for i in 0..max do '127.0.0.1'.split(".").zip([256**3,256**2,256,1]).inject(0) { | sum, a| sum + a[0].to_i*a[1] } end end x.report("ohdeargodno") do for i in 0..max do "127.0.0.1".split(".").map { |i| i.to_i }.reverse.inject([]) { | memo, part| memo << part * (256 ** memo.size) }.inject(0) { |memo, part| memo += part } end end x.report("ipaddr/to_i") do for i in 0..max do IPAddr.new( '127.0.0.1' ).to_i end end end