かってにインパクトファクター

子育てサラリーマンが日々の雑多なことをつらつらと綴ってます。時々政治ネタ経済ネタコンピュータネタなどをはさみます。

ruby + sqlite でinsertのベンチマークをとってみた part3

第三弾となりましたが、SQLITEのINSERTのベンチマークをとってみました。

今回はprepare。
SQLITEでは、実行時にSQL文を解析しているようですが、事前に解析を済ませてしまって、EXECUTE時の負荷を下げましょうという試みです。

条件は、

  1. SQL文を ? を用いた形でprepare
  2. SQL文を、シンボルを用いた形でprepare(with Symbol)
  3. 前回まであったexecute2は、prepareでは実行不可
  4. v2.0以降の機能であるキーワード引数(in v2.0)
  5. loop_countは繰り返しの回数

では早速結果を

>loop_count = 10000
                                               user     system      total        real
standard insert                            0.703000   0.015000   0.718000 (  1.024075)
st after preparing                         0.266000   0.000000   0.266000 (  0.579407)
st after preparing with Symbol             0.328000   0.016000   0.344000 (  0.657017)
st after preparing with Symbol in v2.0     0.328000   0.031000   0.359000 (  0.614970)

                                               user     system      total        real
standard insert                            0.750000   0.016000   0.766000 (  1.024316)
st after preparing                         0.266000   0.000000   0.266000 (  0.593287)
st after preparing with Symbol             0.343000   0.015000   0.358000 (  0.647709)
st after preparing with Symbol in v2.0     0.344000   0.000000   0.344000 (  0.649645)


>loop_count = 100000
                                               user     system      total        real
standard insert                            7.625000   0.203000   7.828000 (  8.483301)
st after preparing                         2.860000   0.141000   3.001000 (  3.389159)
st after preparing with Symbol             3.593000   0.125000   3.718000 (  4.124089)
st after preparing with Symbol in v2.0     3.688000   0.094000   3.782000 (  4.141411)

                                               user     system      total        real
standard insert                            7.657000   0.203000   7.860000 (  8.375045)
st after preparing                         3.000000   0.078000   3.078000 (  3.515643)
st after preparing with Symbol             3.688000   0.047000   3.735000 (  4.125032)
st after preparing with Symbol in v2.0     3.766000   0.031000   3.797000 (  4.171897)

という結果になりました。
これを見ると、prepareでは、倍以上の速度確保出来ていることになります。
ただ、シンボルを用いるとコードは見やすくなりますが、速度が20%程度犠牲になっています。

一応コードも載せておきます。

require 'benchmark'
require 'rubygems'
require 'sqlite3'

db = SQLite3::Database.new("Inventory.db")
#使える型は、NULL、INTEGER、REAL、TEXT、BLOB
db.execute(<<EOS
  CREATE TABLE IF NOT EXISTS Item
    (
     ID   INTEGER PRIMARY KEY AUTOINCREMENT, 
     Code INTEGER UNIQUE NOT NULL,
     Name TEXT NOT NULL,
     Date TEXT NOT NULL
    );
EOS
)

loop_count = 100000
insert_code = []

loop_count.times{
  insert_code.push((0...20).map{ ('A'..'Z').to_a[rand(26)] }.join)
}

db.execute("delete from Item")

Benchmark.bm(40) do |x|
  
  #standard insert with transaction
  x.report('standard insert'){
  	db.transaction
    loop_count.times{|i|
    	db.execute("INSERT INTO Item (Code, Name, Date) VALUES (#{i}, '#{insert_code[i]}', '2014-08-03')")
  	}
  	db.commit
  }
  db.execute("DELETE FROM Item")
  
  
  
  #standard insert after preparing
  x.report('st after preparing'){
  	db.transaction
  	pre = db.prepare("INSERT INTO Item (Code, Name, Date) VALUES (?, ?, ?)")
    loop_count.times{|i|
    	pre.execute(i, insert_code[i], '2014-08-03')
  	}
  	db.commit
  }
  db.execute("DELETE FROM Item")
  
  
  #standard insert after preparing with Symbol
  x.report('st after preparing with Symbol'){
  	db.transaction
  	pre = db.prepare("INSERT INTO Item (Code, Name, Date) VALUES (:Code, :Name, :Date)")
    loop_count.times{|i|
    	pre.execute(:Code => i, :Name => insert_code[i], :Date => '2014-08-03')
  	}
  	db.commit
  }
  db.execute("DELETE FROM Item")
  
  
  #standard insert after preparing with Symbol in v2.0
  x.report('st after preparing with Symbol in v2.0'){
  	db.transaction
  	pre = db.prepare("INSERT INTO Item (Code, Name, Date) VALUES (:Code, :Name, :Date)")
    loop_count.times{|i|
    	pre.execute(Code: i, Name: insert_code[i], Date: '2014-08-03')
  	}
  	db.commit
  }
  db.execute("DELETE FROM Item")
  
end