LoginSignup
1
0

More than 3 years have passed since last update.

今更だけどrailsのmapとpluckを比較してみた

Last updated at Posted at 2020-01-01

今更だけどrailsのmapとpluckを比較してみた

あけましておめでとうございます。
キーボードと戯れていたら2020年を迎えることができました。mendです。
就職してサーバーサイドエンジニアとして働いていて、現在のお仕事ではRailsを使用しております。
学生時代はRailsでの開発経験がなく、かなり浅い知識で仕事に取り組む中でmodelの特定カラムの情報を取得する部分のレビューに

??? 「mapは重いからやめたほうがいいよ」

??? 「ここ(map)、pluckに変えたほうがいいですよ」

とのご指摘をもらうことがよくありました。
帰省した実家で退屈をしているのもあって今回の記事では普段癖で書いてしまうmapよりも、本当にpluckのほうが軽いのかを検証してみたいと思います。

検証環境

デフォルト設定です

  • Ruby on Rails: 5.2.3
  • データベース: SQLite

忙しい人のためにまずは結論

処理時間

件数 100 1000 10000 100000
map 0.002795s 0.019125s 0.125839s 1.529347s
pluck 0.000799s 0.001634s 0.015934s 0.164019s

結果:mapが重い
すごく当たり前ですが、やはりレコードが多くなるにつれて処理にかかる時間が長くなりました。

rakeタスクを作成する

いちいちコンソール叩いて検証するのも面倒なのでタスクを作成して検証しました。

$bundle exec rails generate task method_performance_test

このコマンドでlib/tasks/method_perforance_test.rakeが作成されるので、こちらにタスクとして行う処理を書いていきます。

railsでモデルを扱うタスクの場合、:environmentが必要みたいなので書き足しておきました。

lib/tasks/method_performance_test.rake
task method_performance_test: :environment do |_task|
  # ここに処理を書いていく
end

検証用モデルの作成

mappluckの検証に使用するモデルは結果的にカラムの情報が取得できればいいので特に何も気にせず作成しました。
とりあえず今回の記事では文字列を格納するnameのカラムを持つUserを作成しました。

$ bundle exec rails g model User name:string

これでモデルとマイグレーションファイルが作成されるので

$ bundle exec rails db:migrate

マイグレーションを忘れずに行いましょう。個人的にはridgepoleのほうが好きです。

rakeタスクの実装

ユーザー全件の削除

今回は検証として行うので、きちんとレコードを空にしてから処理を行うようにします。
全部消すのは簡単なのでササッと処理を書いちゃいます。

# ユーザー全件を削除する
User.all.map { |user| user.destroy! }

うわこっわ....

task内で環境変数を呼び出す

レコード数の件数ごとにパフォーマンスの測定を行いたいので、コマンド実行時に環境変数を参照できるようにしたいと思います。

$ bundle exec rake method_performance_test SIZE=10

このように最後に付け足すことで、タスク内で環境変数を呼び出せるようにします

lib/tasks/method_performance_test.rake
puts "#{ENV['SIZE'].to_i}" # 10

検証用のレコードを作成する

先程の環境変数から受け取った値の件数だけレコードを作成できるようにします

lib/tasks/method_performance_test.rake
num = ENV['SIZE'].to_i
num.times do |n|
  User.create(name: "test#{num.to_s.rjust(7, "0")}")
end

mappluckの処理時間を計測する

実行時間の計測自体は結構シンプルです。
現在時刻から計測開始時刻を引くだけです。

lib/tasks/method_performance_test.rake
# mapの処理時間を計測する
start_time = Time.current
User.all.map(&:id)
map_time = Time.current - start_time

# pluckの処理時間を計測する
start_time = Time.current
User.all.pluck(:id)
pluck_time = Time.current - start_time

作成したタスク

最終的にこんな感じになりました

lib/tasks/method_performance_test.rake
task method_performance_test: :environment do |_task|
  puts "user_size:#{ENV['SIZE'].to_i}"

  num = ENV['SIZE'].to_i

  puts "=====initializing====="
  User.all.map { |user| user.destroy! }

  puts "=====create users: size = #{num}====="
  insert_time = Time.current
  num.times do |n|
    User.create!(name: "test#{num.to_s.rjust(7, "0")}")
  end
  puts "=====create users: done====="
  puts " "
  puts "INSERT time: #{Time.current - insert_time}s"
  puts " "

  puts "=====user.all.map(&:id): start====="
  puts "=====class:#{User.all.map(&:id).class}====="
  start_time = Time.current
  User.all.map(&:id)
  map_time = Time.current - start_time
  puts "result map: #{map_time}s"
  puts "=====user.all.map(&:id): end====="

  puts "=====user.all.pluck(:id): start====="
  puts "=====class:#{User.all.pluck(:id).class}====="
  start_time = Time.current
  User.all.pluck(:id)
  pluck_time = Time.current - start_time
  puts "result pluck: #{pluck_time}s"
  puts "=====user.all.pluck(:id): end====="

  puts "=====clean up test data====="
  delete_time = Time.current
  User.all.map { |user| user.destroy! }
  puts " "
  puts "DELETE time: #{Time.current - delete_time}s"
  puts " "

  puts "=====result====="
  puts "map   :#{map_time}s"
  puts "pluck :#{pluck_time}s"

end
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0