読者です 読者をやめる 読者になる 読者になる

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

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

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

Ruby PC

まずは結果から。

  1. standard insertはtransaction有りのinsertをexecuteで実行
  2. w/o trはトランザクションなし
  3. execute2はinsertを実行する際のメソッドにexecute2を適用
  4. batchはinsertを実行する際のメソッドにbatchを適用
  5. loop_countはループした回数
  6. loop_count を上げる毎に、遅い処理は飛ばしました。
>loop_count = 100
                                     user     system      total        real
standard insert                  0.016000   0.000000   0.016000 (  0.246619)
standard insert w/o tr           0.063000   0.484000   0.547000 ( 19.855588)
execute2 insert                  0.016000   0.000000   0.016000 (  0.261787)
execute2 insert w/o tr           0.062000   0.328000   0.390000 ( 21.331330)
batch insert                     0.032000   0.000000   0.032000 (  0.307304)
batch insert w/o tr              0.078000   0.344000   0.422000 ( 22.041804)


>loop_count = 1000
                                     user     system      total        real
standard insert                  0.094000   0.000000   0.094000 (  0.361945)
execute2 insert                  0.062000   0.000000   0.062000 (  0.340452)
batch insert                     1.656000   0.172000   1.828000 (  2.046856)


>loop_count = 100000
                                     user     system      total        real
standard insert                  7.734000   0.250000   7.984000 (  8.557611)
execute2 insert                  6.829000   0.094000   6.923000 (  7.390702)

これを見ると、トランザクション無しの実行はほぼありえないです。100倍くらい速度が違うことになります。
executeとexecute2の違いは10%くらい、execute2が早いという結果になりました。

ちなみに、execute2がなんなのかは、なかなかいい資料に出会えなくてわかってません。ごめんなさい><

ベンチマークとは関係ないですが、今回100Kのレコードを流し込んだDBのサイズはvacuum後で、5,793KBでした。

一応ソースも載せておきます。

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 = 100
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(30) 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
  x.report('standard insert w/o tr'){
    loop_count.times{|i|
      db.execute("INSERT INTO Item (Code, Name, Date) VALUES (#{i}, '#{insert_code[i]}', '2014-08-03')")
    }
  }
  db.execute("DELETE FROM Item")
  
  
  #execute2 insert with transaction
  x.report('execute2 insert'){
  	db.transaction
    loop_count.times{|i|
    	db.execute2("INSERT INTO Item (Code, Name, Date) VALUES (#{i}, '#{insert_code[i]}', '2014-08-03')")
  	}
  	db.commit
  }
  db.execute("DELETE FROM Item")
  
  #exexute2 insert without transaction
  x.report('execute2 insert w/o tr'){
    loop_count.times{|i|
      db.execute2("INSERT INTO Item (Code, Name, Date) VALUES (#{i}, '#{insert_code[i]}', '2014-08-03')")
    }
  }
  db.execute("DELETE FROM Item")
  
  #batch insert with transaction
  x.report('batch insert'){
  	db.transaction
  	batch_str = ""
    loop_count.times{|i|
    	batch_str <<= "INSERT INTO Item (Code, Name, Date) VALUES (#{i}, '#{insert_code[i]}', '2014-08-03');"
    }
    db.execute(batch_str)
    db.commit
  }
  db.execute_batch("DELETE FROM Item")
  
  #batch insert without transaction
  x.report('batch insert w/o tr'){
  	batch_str = ""
    loop_count.times{|i|
    	batch_str <<= "INSERT INTO Item (Code, Name, Date) VALUES (#{i}, '#{insert_code[i]}', '2014-08-03');"
    }
    db.execute(batch_str)
  }
  db.execute_batch("DELETE FROM Item")
  
end