Float#ceil vs Integer#divmod
とあるアプリを書いているときに、
「ある個数の要素の集まりを、一定の数単位で分ける。
その単位に足りない余りも、分けられた一つとして数える。
するといくつに分けられるか」
という処理を書くことになりました。
我ながら日本語が実にヘタだ。つまり例としては、110個の蜜柑を20個入りの箱かなんかに詰めていく、と。んでもって満杯の箱5個+中途半端な10個入りの箱1個=計6個、というような数え方がしたかったのです。
わたしは初め、次のように書きました。
n = (hoge_size.to_f / unit_size).ceil
でもってアプリは正常に動きました。
しかし、わたしはここで色気を出して「高速化してやろう」と思ったのです。
なにせFloatです、Float。きっと重いに違いない。
というわけで、次のようなことを考えました。
n, r = hoge_size.divmod(unit_size) n += 1 unless r.zero?
整数しか出てきていません。おまけにBignumの範囲を扱うことなどありえません。これは速くなるんじゃないか?
しかし、結果は。
require 'benchmark' N_VIEW_ENTRY = 20 def measure_f(x) (x.to_f / N_VIEW_ENTRY).ceil end def measure_i(x) n, r = x.divmod(N_VIEW_ENTRY) n += 1 unless r.zero? n end n_try = 100000 Benchmark.bmbm do |job| job.report 'by Float#ceil' do n_try.times{|n| measure_f(n) } end job.report 'by Integer#divmod' do n_try.times{|n| measure_i(n) } end end
このベンチに対して
Rehearsal ----------------------------------------------------- by Float#ceil 0.219000 0.000000 0.219000 ( 0.250000) by Integer#divmod 0.328000 0.000000 0.328000 ( 0.343000) -------------------------------------------- total: 0.547000sec user system total real by Float#ceil 0.218000 0.000000 0.218000 ( 0.235000) by Integer#divmod 0.329000 0.000000 0.329000 ( 0.359000)
こんな有様でした。
よく考えれば、後者だってdivmodがArrayをいちいち作ってるわけで…。
それに加え、この処理、1回のリクエストに対して1回しか実行されない類のものでして…。
こんなとこが高速化できたってしょうもないというのはわかっていたのですが、「高速化」という響きに負けてつい取り組んでしまったのでした。