前提
Schemafile
# -*- mode: ruby -*-
# vi: set ft=ruby :
# 単語
require 'people.schema'
require 'person_addresses.schema'
people.schema
# -*- mode: ruby -*-
# vi: set ft=ruby :
create_table "people", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
t.string :japanese_name, null: false
t.string :english_name, null: true
t.timestamps
end
person_addresses.schema
# -*- mode: ruby -*-
# vi: set ft=ruby :
create_table "person_addresses", id: :integer, force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" do |t|
t.integer :person_id, null: false
t.index [:person_id], name: 'index_person_addresses_on_person_id'
t.integer :address_type, null: false
t.timestamps
end
Terminal
bundle exec ridgepole -c config/database.yml -E development --apply -f db/schemas/Schemafile
app/models/person.rb
# == Schema Information
#
# Table name: people
#
# id :integer not null, primary key
# japanese_name :string(255) not null
# english_name :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class Person < ApplicationRecord
has_many :person_addresses, dependent: :destroy
accepts_nested_attributes_for :person_addresses, reject_if: :all_blank, allow_destroy: true
validates :japanese_name, presence: true
validates :english_name, format: { with: /\A[\p{ascii}]+\z/ }, allow_blank: true
end
app/models/person_address.rb
# == Schema Information
#
# Table name: person_addresses
#
# id :integer not null, primary key
# person_id :integer not null
# address_type :integer not null
# created_at :datetime not null
# updated_at :datetime not null
#
class PersonAddress < ApplicationRecord
belongs_to :person
enum address_type: {
one: :one,
two: :two,
three: :three,
four: :four,
five: :five,
}, _prefix: true
validates :person_id, presence: true, on: :update
validates :address_type, presence: true
end
Active Record
pluck & any
pry(main)> `ruby -v`
=> "ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]\n"
pry(main)> Rails.version
=> "5.2.0"
CONVERT_MAP = {
one: :one,
two: :two,
three: :three,
four: :four,
five: :five,
}.freeze
1000.times.each do |idx|
person = Person.new(japanese_name: "#{idx}")
person_addresses = 2.times.map{|_idx| PersonAddress.new(address_type: CONVERT_MAP.symbolize_keys.keys.sample)}
person.assign_attributes(person_addresses: person_addresses)
person.save!
end
Benchmark.bm 5 do |row|
row.report "pluck and any" do
Person.last.person_addresses.pluck(:address_type).any? { |address_type| CONVERT_MAP[address_type.to_sym].present? }
end
row.report "where" do
Person.last.person_addresses.where(address_type: CONVERT_MAP.keys)
end
end
=> [#<Benchmark::Tms:0x0000556c84fcb2f0
@cstime=0.0,
@cutime=0.0,
@label="pluck and any",
@real=0.009011553949676454,
@stime=0.0,
@total=0.006902000000000186,
@utime=0.006902000000000186>,
#<Benchmark::Tms:0x0000556c84fc7060
@cstime=0.0,
@cutime=0.0,
@label="where",
@real=0.004103021929040551,
@stime=0.0,
@total=0.0033479999999999066,
@utime=0.0033479999999999066>]
=> [#<Benchmark::Tms:0x0000556c84aa8908
@cstime=0.0,
@cutime=0.0,
@label="pluck and any",
@real=0.008074268931522965,
@stime=0.0,
@total=0.006250000000000533,
@utime=0.006250000000000533>,
#<Benchmark::Tms:0x0000556c84b03448
@cstime=0.0,
@cutime=0.0,
@label="where",
@real=0.00198873202316463,
@stime=0.0,
@total=0.0015290000000005577,
@utime=0.0015290000000005577>]
=> [#<Benchmark::Tms:0x0000556c83d5f670
@cstime=0.0,
@cutime=0.0,
@label="pluck and any",
@real=0.006150746019557118,
@stime=0.0012540000000003104,
@total=0.004831000000000252,
@utime=0.0035769999999999413>,
#<Benchmark::Tms:0x0000556c83d41f08
@cstime=0.0,
@cutime=0.0,
@label="where",
@real=0.0022412820253521204,
@stime=0.0009770000000006718,
@total=0.0018830000000011893,
@utime=0.0009060000000005175>]
=> [#<Benchmark::Tms:0x0000556c83031bd8
@cstime=0.0,
@cutime=0.0,
@label="pluck and any",
@real=0.008198636001907289,
@stime=0.0004149999999993881,
@total=0.005551999999998891,
@utime=0.005136999999999503>,
#<Benchmark::Tms:0x0000556c83029730
@cstime=0.0,
@cutime=0.0,
@label="where",
@real=0.0025708150351420045,
@stime=0.0005850000000000577,
@total=0.002069999999998906,
@utime=0.0014849999999988484>]
=> [#<Benchmark::Tms:0x0000556c85ba55f8
@cstime=0.0,
@cutime=0.0,
@label="pluck and any",
@real=0.011462070979177952,
@stime=0.0,
@total=0.007191999999999865,
@utime=0.007191999999999865>,
#<Benchmark::Tms:0x0000556c85b9b968
@cstime=0.0,
@cutime=0.0,
@label="where",
@real=0.0026785259833559394,
@stime=0.00029100000000070736,
@total=0.002032000000001588,
@utime=0.0017410000000008807>]
- どうも
pluck
のちany?
などのeachはwhere
よりも遥かに遅い様です。
ビルトインオブジェクト
Array
pluck
ASCII文字をランダム生成
- ASCII文字自体の生成をするには、メタ
res = Benchmark.bm 10 do |row|
row.report "ascii" do
[*"\0".."~"].sample(200).join
end
end
res.each{|res| p [sprintf("%.5f", res.utime.round(5, half: :up)), sprintf("%.5f", res.stime.round(5, half: :up)), sprintf("%.5f", res.total.round(5, half: :up)), sprintf("%.5f", res.real.round(5, half: :up))].join(" | ") }
レポート名称 | user | system | total | real |
---|---|---|---|---|
ascii(1) | 0.0 | 0.00018 | 0.00018 | 0.00011 |
ascii(2) | 0.00026 | 0.0 | 0.00026 | 0.00034 |
ascii(3) | 0.00005 | 0.00002 | 0.00007 | 0.00007 |
ascii(4) | 0.00005 | 0.00002 | 0.00007 | 0.00007 |
ascii(5) | 0.00007 | 0.00003 | 0.00011 | 0.00017 |
Arrayからあるパターンのキーに一致するものだけフィルタしてそのキーを取りたい場合
レポート名称 | 意味 |
---|---|
forEach | 繰り返しで索引した場合 |
keep_if | keep_ifメソッドを使用した場合 |
select | selectメソッドを使用した場合 |
images = []; 1000000.times {|row| images << "img#{row + 1}_url" }
result_count = {:forEach=>0, :keep_if=>0, :select=>0}
Benchmark.bm 10 do |row|
row.report "forEach " do
fin = 50000
1.upto(fin) do |row|
url_key = "img#{row}_url"
next if images[row - 1] != url_key
Struct.new(:url, :caption, :title).new(url_key.to_sym, url_key.to_s.gsub(/url/, "caption"), url_key.to_s.gsub(/url/, "title"))
result_count[:forEach] += 1
end
end
row.report "keep_if" do
images.keep_if {|row| row.to_s.match?(/img([1-9]|[1-9][0-9]{1,3}|[1-4][0-9]{4}|50000)_url/) }.each do |url_key|
Struct.new(:url, :caption, :title).new(url_key.to_sym, url_key.to_s.gsub(/url/, "caption"), url_key.to_s.gsub(/url/, "title"))
result_count[:keep_if] += 1
end
end
row.report "select" do
images.select {|row| row.to_s.match?(/img([1-9]|[1-9][0-9]{1,3}|[1-4][0-9]{4}|50000)_url/) }.each do |url_key|
Struct.new(:url, :caption, :title).new(url_key.to_sym, url_key.to_s.gsub(/url/, "caption"), url_key.to_s.gsub(/url/, "title"))
result_count[:select] += 1
end
end
end
result_count
=> {:forEach=>50000, :keep_if=>50000, :select=>50000}
計測結果
1回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.312843 | 0.026700 | 1.339543 | ( 1.349017) |
keep_if | 1.768649 | 0.026811 | 1.795460 | ( 1.808968) |
select | 1.481335 | 0.030547 | 1.511882 | ( 1.523911) |
2回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.312844 | 0.034082 | 1.346926 | ( 1.365920) |
keep_if | 1.766020 | 0.022876 | 1.788896 | ( 1.797898) |
select | 1.439497 | 0.021102 | 1.460599 | ( 1.467751) |
3回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.317690 | 0.018919 | 1.336609 | ( 1.350585) |
keep_if | 1.763197 | 0.024135 | 1.787332 | ( 1.804232) |
select | 1.443859 | 0.022458 | 1.466317 | ( 1.472827) |
4回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.294830 | 0.015705 | 1.310535 | ( 1.318776) |
keep_if | 1.958510 | 0.051190 | 2.009700 | ( 2.040533) |
select | 1.321429 | 0.013722 | 1.335151 | ( 1.340082) |
5回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.422966 | 0.018969 | 1.441935 | ( 1.449615) |
keep_if | 1.733768 | 0.020578 | 1.754346 | ( 1.769189) |
select | 1.451152 | 0.015573 | 1.466725 | ( 1.472972) |
-
パフォーマンスとしては大差ないが、可読性では後者となるkeep_ifが僅かに遅く、forEachとselectが誤差値考慮すると大差とは言い難いような印象である(可読性は後者にはなるが)
Hashからあるパターンのキーに一致するものだけフィルタしてそのキーを取りたい場合
レポート名称 | 意味 |
---|---|
forEach | 繰り返しで索引した場合 |
keep_if | keep_ifメソッドを使用した場合 |
select | selectメソッドを使用した場合 |
images = []; 1000000.times {|row| images << "img#{row + 1}_url" }; image_map = {}; images.map {|row| image_map[row.to_s.to_sym] = row }
result_count = {:forEach=>0, :keep_if=>0, :select=>0}
Benchmark.bm 10 do |row|
row.report "forEach " do
fin = 50000
1.upto(fin) do |row|
url_key = "img#{row}_url".to_sym
next if image_map[url_key].blank?
Struct.new(:url, :caption, :title).new(url_key, url_key.to_s.gsub(/url/, "caption"), url_key.to_s.gsub(/url/, "title"))
result_count[:forEach] += 1
end
end
row.report "keep_if" do
image_map.keep_if {|key, value| key.to_s.match?(/img([1-9]|[1-9][0-9]{1,3}|[1-4][0-9]{4}|50000)_url/) }.each_key do |url_key|
Struct.new(:url, :caption, :title).new(url_key, url_key.to_s.gsub(/url/, "caption"), url_key.to_s.gsub(/url/, "title"))
result_count[:keep_if] += 1
end
end
row.report "select" do
image_map.select {|key, value| key.to_s.match?(/img([1-9]|[1-9][0-9]{1,3}|[1-4][0-9]{4}|50000)_url/) }.each_key do |url_key|
Struct.new(:url, :caption, :title).new(url_key, url_key.to_s.gsub(/url/, "caption"), url_key.to_s.gsub(/url/, "title"))
result_count[:select] += 1
end
end
end
result_count
=> {:forEach=>50000, :keep_if=>50000, :select=>50000}
計測結果
1回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.257482 | 0.099606 | 1.357088 | ( 1.371853) |
keep_if | 2.062804 | 0.068399 | 2.131203 | ( 2.144689) |
select | 2.317476 | 0.111707 | 2.429183 | ( 2.436765) |
2回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.292436 | 0.028099 | 1.320535 | ( 1.325629) |
keep_if | 1.595292 | 0.042578 | 1.637870 | ( 1.646557) |
select | 1.285199 | 0.022699 | 1.307898 | ( 1.317835) |
3回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.609683 | 0.048782 | 1.658465 | ( 1.667256) |
keep_if | 1.148458 | 0.017314 | 1.165772 | ( 1.171699) |
select | 1.340724 | 0.039342 | 1.380066 | ( 1.384263) |
4回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.439700 | 0.041089 | 1.480789 | ( 1.488726) |
keep_if | 1.220690 | 0.019430 | 1.240120 | ( 1.252073) |
select | 1.331339 | 0.042890 | 1.374229 | ( 1.379941) |
5回目
レポート名称 | user | system | total | real |
---|---|---|---|---|
forEach | 1.173899 | 0.010056 | 1.183955 | ( 1.187291) |
keep_if | 1.328620 | 0.038243 | 1.366863 | ( 1.378679) |
select | 1.267303 | 0.029949 | 1.297252 | ( 1.314939) |
-
パフォーマンスとしては大差ないが、可読性では後者となる誤差値が出てはいるものの、平均的にはkeep_if / selectの方が早い印象を受けなくもない(可読性では後者となる)