kubo39's blog

ただの雑記です。

Faster command line tool in Crystal?

こんな感じで巷ではやっているので、crystalはどうだろうと思い試してみた。

# coding: utf-8
# $ crystal build --release fastcmdline.cr
# crystal build --release fastcmdline.cr  7.94s user 0.09s system 101% cpu 7.937 total
# $ time ./fastcmdline googlebooks-eng-all-1gram-20120701-0 1 2
# max_key: 2006 sum: 22569013
# ./fastcmdline googlebooks-eng-all-1gram-20120701-0 1 2  8.68s user 0.69s system 125% cpu 7.465 total

if ARGV.size < 3
   puts "synopsis: #{__FILE__} filename keyfield valuefield"
   exit 1
 end

filename = ARGV[0]
key_field_index = ARGV[1].to_i
value_field_index = ARGV[2].to_i
max_field_index = [key_field_index, value_field_index].max
delim = "\t"

# ここで初期値いじってもあんま効果ない・・ 11, 1024 ~ 8192あたりでとったけど11が一番ましくらい
sum_by_key = Hash(String, Int64).new(initial_capacity: 11)

File.each_line(filename) do |line|
  # たぶんここでメモリ確保走る分遅い
  fields = line.split(delim).first(max_field_index + 1)
  if max_field_index < fields.size
    key = fields[key_field_index]
    field_value = fields[value_field_index].to_i64
    if sum_by_key[key]?.nil?
      sum_by_key[key] = field_value
    else
      sum_by_key[key] += field_value
    end
  end
end

if sum_by_key.empty?
  puts "No entries"
else
  max_key = ""
  max_value = 0.to_i64
  sum_by_key.each do |key, value|
    if value > max_value
      max_key = key
      max_value = value
    end
  end
  puts "max_key: #{max_key} sum: #{max_value}"
end

同じ環境でdmdやrustとやったところdmdが6.6secs、rustが1.1secsほどなのでcrystalは少し遅い。

アルゴリズム的なところ以外でいうと、

  1. Hashの初期メモリ確保が小さくて割当なおすので遅い
  2. splitが毎回メモリ確保するので遅い
  3. メモリ割り当て時にGCスキャンが走るので遅い

というあたりがありそうなかんじだった。

1.は初期値の11から1024,2048,4096,8192あたりに変えて試したけどとくに効果なし。 2.はちょっとめんどくさいのでやらなかったけど、ここは効果ありそう。 3.はGC.stopが生えていなかったのでパス。

といった形で、まあ結局大して最適化してない。それでもCPythonに比べるとだいぶ速いのですごいなあ。