aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--activerecord/scripts/performance.rb229
2 files changed, 231 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 43c4d7b124..17c77cc23e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
debug.log
+performance.log
doc/rdoc
activeresource/doc
activerecord/doc
@@ -7,6 +8,7 @@ actionmailer/doc
activesupport/doc
activeresource/pkg
activerecord/pkg
+activerecord/scripts/tmp
actionpack/pkg
activemodel/test/fixtures/fixture_database.sqlite3
actionmailer/pkg
diff --git a/activerecord/scripts/performance.rb b/activerecord/scripts/performance.rb
new file mode 100644
index 0000000000..fb84543685
--- /dev/null
+++ b/activerecord/scripts/performance.rb
@@ -0,0 +1,229 @@
+#!/usr/bin/env ruby -KU
+
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+
+require 'ftools'
+require 'rubygems'
+
+gem 'addressable', '~>2.0'
+gem 'faker', '~>0.3.1'
+gem 'rbench', '~>0.2.3'
+
+require 'active_record'
+require 'logger'
+require 'active_support'
+require 'addressable/uri'
+require 'faker'
+require 'rbench'
+
+socket_file = Pathname.glob(%w[
+ /opt/local/var/run/mysql5/mysqld.sock
+ tmp/mysqld.sock
+ /tmp/mysqld.sock
+ tmp/mysql.sock
+ /tmp/mysql.sock
+ /var/mysql/mysql.sock
+ /var/run/mysqld/mysqld.sock
+]).find { |path| path.socket? }
+
+configuration_options = {
+ :adapter => 'mysql',
+ :username => 'rails',
+ :password => '',
+ :database => 'activerecord_unittest',
+}
+
+configuration_options[:socket] = socket_file unless socket_file.nil?
+
+if configuration_options[:adapter]
+ sqlfile = File.join(File.dirname(__FILE__), 'tmp', 'performance.sql')
+ mysql_bin = %w[ mysql mysql5 ].select { |bin| `which #{bin}`.length > 0 }
+ mysqldump_bin = %w[ mysqldump mysqldump5 ].select { |bin| `which #{bin}`.length > 0 }
+end
+
+ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), 'performance.log'))
+ActiveRecord::Base.logger.level = 0
+
+ActiveRecord::Base.establish_connection(configuration_options)
+
+class Exhibit < ActiveRecord::Base #:nodoc:
+ belongs_to :user
+end
+
+class User < ActiveRecord::Base #:nodoc:
+ has_many :exhibits
+end
+
+ActiveRecord::Migration.create_table :users, :force => true do |t|
+ t.string :name, :email
+ t.datetime :created_on
+end
+
+ActiveRecord::Migration.create_table :exhibits, :force => true do |t|
+ t.string :name
+ t.integer :user_id
+ t.text :notes
+ t.datetime :created_on
+end
+
+Exhibit.find_by_sql('SELECT 1')
+
+def touch_attributes(*exhibits)
+ exhibits.flatten.each do |exhibit|
+ exhibit.id
+ exhibit.name
+ exhibit.created_on
+ end
+end
+
+def touch_relationships(*exhibits)
+ exhibits.flatten.each do |exhibit|
+ exhibit.id
+ exhibit.name
+ exhibit.created_on
+ exhibit.user
+ end
+end
+
+c = configuration_options
+
+if sqlfile && File.exists?(sqlfile)
+ puts "Found data-file. Importing from #{sqlfile}"
+ `#{mysql_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} < #{sqlfile}`
+else
+ puts 'Generating data for benchmarking...'
+
+ # pre-compute the insert statements and fake data compilation,
+ # so the benchmarks below show the actual runtime for the execute
+ # method, minus the setup steps
+
+ # Using the same paragraph for all exhibits because it is very slow
+ # to generate unique paragraphs for all exhibits.
+ notes = Faker::Lorem.paragraphs.join($/)
+ today = Date.today
+
+ puts 'Inserting 10,000 users and exhibits...'
+ 10_000.times do
+ user = User.create(
+ :created_on => today,
+ :name => Faker::Name.name,
+ :email => Faker::Internet.email
+ )
+
+ Exhibit.create(
+ :created_on => today,
+ :name => Faker::Company.name,
+ :user => user,
+ :notes => notes
+ )
+ end
+
+ if sqlfile
+ answer = nil
+ until answer && answer[/^$|y|yes|n|no/]
+ print('Would you like to dump data into tmp/performance.sql (for faster setup)? [Yn]');
+ STDOUT.flush
+ answer = gets
+ end
+
+ if answer[/^$|y|yes/]
+ File.makedirs(File.dirname(sqlfile))
+ `#{mysqldump_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{sqlfile}`
+ puts "File saved\n"
+ end
+ end
+end
+
+TIMES = ENV.key?('x') ? ENV['x'].to_i : 10_000
+
+puts 'You can specify how many times you want to run the benchmarks with rake:perf x=(number)'
+puts 'Some tasks will be run 10 and 1000 times less than (number)'
+puts "Benchmarks will now run #{TIMES} times"
+# Inform about slow benchmark
+# answer = nil
+# until answer && answer[/^$|y|yes|n|no/]
+# print("A slow benchmark exposing problems with SEL is newly added. It takes approx. 20s\n");
+# print("you have scheduled it to run #{TIMES / 100} times.\nWould you still include the particular benchmark? [Yn]")
+# STDOUT.flush
+# answer = gets
+# end
+# run_rel_bench = answer[/^$|y|yes/] ? true : false
+
+
+RBench.run(TIMES) do
+
+ column :times
+ column :ar
+
+ report 'Model#id', (TIMES * 100).ceil do
+ ar_obj = Exhibit.find(1)
+
+ ar { ar_obj.id }
+ end
+
+ report 'Model.new (instantiation)' do
+ ar { Exhibit.new }
+ end
+
+ report 'Model.new (setting attributes)' do
+ attrs = { :name => 'sam' }
+ ar { Exhibit.new(attrs) }
+ end
+
+ report 'Model.get specific (not cached)' do
+ ActiveRecord::Base.uncached { ar { touch_attributes(Exhibit.find(1)) } }
+ end
+
+ report 'Model.get specific (cached)' do
+ ActiveRecord::Base.cache { ar { touch_attributes(Exhibit.find(1)) } }
+ end
+
+ report 'Model.first' do
+ ar { touch_attributes(Exhibit.first) }
+ end
+
+ report 'Model.all limit(100)', (TIMES / 10).ceil do
+ ar { touch_attributes(Exhibit.find(:all, :limit => 100)) }
+ end
+
+ report 'Model.all limit(100) with relationship', (TIMES / 10).ceil do
+ ar { touch_relationships(Exhibit.find(:all, :limit => 100, :include => [ :user ])) }
+ end
+
+ report 'Model.all limit(10,000)', (TIMES / 1000).ceil do
+ ar { touch_attributes(Exhibit.find(:all, :limit => 10_000)) }
+ end
+
+ exhibit = {
+ :name => Faker::Company.name,
+ :notes => Faker::Lorem.paragraphs.join($/),
+ :created_on => Date.today
+ }
+
+ report 'Model.create' do
+ ar { Exhibit.create(exhibit) }
+ end
+
+ report 'Resource#attributes=' do
+ attrs_first = { :name => 'sam' }
+ attrs_second = { :name => 'tom' }
+ ar { exhibit = Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
+ end
+
+ report 'Resource#update' do
+ ar { Exhibit.find(1).update_attributes(:name => 'bob') }
+ end
+
+ report 'Resource#destroy' do
+ ar { Exhibit.first.destroy }
+ end
+
+ report 'Model.transaction' do
+ ar { Exhibit.transaction { Exhibit.new } }
+ end
+
+ summary 'Total'
+end
+
+ActiveRecord::Migration.drop_table "exhibits"
+ActiveRecord::Migration.drop_table "users"