rubybench_runner 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +7 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +44 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +43 -0
  9. data/Rakefile +12 -0
  10. data/bin/console +15 -0
  11. data/bin/setup +8 -0
  12. data/exe/rubybench_runner +101 -0
  13. data/lib/rubybench_runner.rb +17 -0
  14. data/lib/rubybench_runner/base_runner.rb +233 -0
  15. data/lib/rubybench_runner/configurations.rb +68 -0
  16. data/lib/rubybench_runner/dependencies_checker.rb +39 -0
  17. data/lib/rubybench_runner/helpers_version +1 -0
  18. data/lib/rubybench_runner/rails/.bundle/config +4 -0
  19. data/lib/rubybench_runner/rails/benchmarks/assets/javascripts/application.js +3 -0
  20. data/lib/rubybench_runner/rails/benchmarks/assets/javascripts/one.js.erb +5 -0
  21. data/lib/rubybench_runner/rails/benchmarks/assets/javascripts/two.js +1 -0
  22. data/lib/rubybench_runner/rails/benchmarks/form_partials/first.html.erb +17 -0
  23. data/lib/rubybench_runner/rails/benchmarks/form_partials/heavy/_item.html.erb +12 -0
  24. data/lib/rubybench_runner/rails/benchmarks/form_partials/heavy/_second.html.erb +7 -0
  25. data/lib/rubybench_runner/rails/benchmarks/form_partials/heavy/_third.html.erb +8 -0
  26. data/lib/rubybench_runner/rails/benchmarks/form_partials/layouts/application.html.erb +29 -0
  27. data/lib/rubybench_runner/rails/benchmarks/partials/first.html.erb +19 -0
  28. data/lib/rubybench_runner/rails/benchmarks/partials/heavy/_item.html.erb +0 -0
  29. data/lib/rubybench_runner/rails/benchmarks/partials/heavy/_second.html.erb +7 -0
  30. data/lib/rubybench_runner/rails/benchmarks/partials/heavy/_third.html.erb +3 -0
  31. data/lib/rubybench_runner/rails/benchmarks/partials/layouts/application.html.erb +29 -0
  32. data/lib/rubybench_runner/rails/benchmarks/support/benchmark_rails.rb +14 -0
  33. data/lib/rubybench_runner/rails/benchmarks/support/echo_channel.rb +13 -0
  34. data/lib/rubybench_runner/rails/benchmarks/support/request_helper.rb +9 -0
  35. data/lib/rubybench_runner/rails/benchmarks/support/url_generation_base.rb +79 -0
  36. data/lib/rubybench_runner/rails/benchmarks/views/posts/_form.html.erb +29 -0
  37. data/lib/rubybench_runner/rails/benchmarks/views/posts/edit.html.erb +6 -0
  38. data/lib/rubybench_runner/rails/benchmarks/views/posts/index.html.erb +32 -0
  39. data/lib/rubybench_runner/rails/benchmarks/views/posts/index.json.jbuilder +4 -0
  40. data/lib/rubybench_runner/rails/benchmarks/views/posts/new.html.erb +5 -0
  41. data/lib/rubybench_runner/rails/benchmarks/views/posts/show.html.erb +19 -0
  42. data/lib/rubybench_runner/rails/benchmarks/views/posts/show.json.jbuilder +1 -0
  43. data/lib/rubybench_runner/rails_runner.rb +91 -0
  44. data/lib/rubybench_runner/support/benchmark_runner.rb +62 -0
  45. data/lib/rubybench_runner/support/helpers.rb +11 -0
  46. data/lib/rubybench_runner/support/setup/.bundle/config +4 -0
  47. data/lib/rubybench_runner/support/setup/Gemfile +11 -0
  48. data/lib/rubybench_runner/support/setup/bm_create_string_columns_setup.rb +16 -0
  49. data/lib/rubybench_runner/support/setup/bm_destroy_setup.rb +12 -0
  50. data/lib/rubybench_runner/support/setup/bm_discourse_setup.rb +164 -0
  51. data/lib/rubybench_runner/support/setup/bm_finders_setup.rb +37 -0
  52. data/lib/rubybench_runner/support/setup/bm_preload_setup.rb +41 -0
  53. data/lib/rubybench_runner/support/setup/bm_save_setup.rb +12 -0
  54. data/lib/rubybench_runner/support/setup/bm_scope_all_setup.rb +32 -0
  55. data/lib/rubybench_runner/support/setup/bm_scope_where_setup.rb +30 -0
  56. data/lib/rubybench_runner/support/setup/bm_validations_invalid_setup.rb +18 -0
  57. data/lib/rubybench_runner/support/setup/bm_validations_valid_setup.rb +30 -0
  58. data/lib/rubybench_runner/support/setup/bm_with_default_scope_setup.rb +29 -0
  59. data/lib/rubybench_runner/version.rb +5 -0
  60. data/rubybench_runner.gemspec +34 -0
  61. metadata +200 -0
@@ -0,0 +1,4 @@
1
+ json.array!(@posts) do |post|
2
+ json.extract! post, :id, :title, :body, :author
3
+ json.url post_url(post, format: :json)
4
+ end
@@ -0,0 +1,5 @@
1
+ <h1>New Post</h1>
2
+
3
+ <%= render 'form' %>
4
+
5
+ <%= link_to 'Back', posts_path %>
@@ -0,0 +1,19 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <p>
4
+ <strong>Title:</strong>
5
+ <%= @post.title %>
6
+ </p>
7
+
8
+ <p>
9
+ <strong>Body:</strong>
10
+ <%= @post.body %>
11
+ </p>
12
+
13
+ <p>
14
+ <strong>Author:</strong>
15
+ <%= @post.author %>
16
+ </p>
17
+
18
+ <%= link_to 'Edit', edit_post_path(@post) %> |
19
+ <%= link_to 'Back', posts_path %>
@@ -0,0 +1 @@
1
+ json.extract! @post, :id, :title, :body, :author, :created_at, :updated_at
@@ -0,0 +1,91 @@
1
+ module RubybenchRunner
2
+ class RailsRunner < BaseRunner
3
+ def command
4
+ command = "RAILS_ENV=production #{super}"
5
+ if require_db?
6
+ command = "DATABASE_URL=#{database_url} #{command}"
7
+ end
8
+ command
9
+ end
10
+
11
+ def database_url
12
+ return @db_url if @db_url
13
+ raw_config = RubybenchRunner::Configurations.new
14
+ config = OpenStruct.new(raw_config[opts.db.to_sym])
15
+ url = "#{opts.db}://#{config.user}"
16
+ url += ":#{config.password}" if config.password
17
+ url += "@"
18
+ if config.host && config.port
19
+ url += "#{config.host}:#{config.port}"
20
+ end
21
+ url += "/#{config.dbname}"
22
+ with_prep_statement = opts.wps == true
23
+ url += "?prepared_statements=#{with_prep_statement}"
24
+ @db_url = url
25
+ end
26
+
27
+ def setup_db
28
+ return if !require_db?
29
+ log("Checking database...")
30
+ config = RubybenchRunner::Configurations.new(mysql_map: true)
31
+ if opts.db == "postgres"
32
+ require 'pg'
33
+ conn_config = config["postgres"]
34
+ rubybench_db = conn_config[:dbname]
35
+ conn_config[:dbname] = "postgres"
36
+ conn = PG.connect(conn_config)
37
+ begin
38
+ res = conn.exec("SELECT 1 FROM pg_database WHERE datname = '#{rubybench_db}'")
39
+ if !res.first
40
+ conn.exec("CREATE DATABASE #{rubybench_db}")
41
+ log("Created PostgreSQL database with the name '#{rubybench_db}'")
42
+ end
43
+ ensure
44
+ conn.close
45
+ end
46
+ elsif opts.db == "mysql2"
47
+ require 'mysql2'
48
+ conn_config = config["mysql2"]
49
+ rubybench_db = conn_config[:database]
50
+ conn_config[:database] = "mysql"
51
+ client = Mysql2::Client.new(conn_config)
52
+ begin
53
+ res = client.query("SHOW DATABASES LIKE '#{rubybench_db}'")
54
+ if !res.first
55
+ client.query("CREATE DATABASE #{rubybench_db}")
56
+ log("Created MySQL database with the name '#{rubybench_db}'")
57
+ end
58
+ ensure
59
+ client.close
60
+ end
61
+ end
62
+ end
63
+
64
+ def benchmark_name
65
+ @benchmark_name ||= "Rails"
66
+ end
67
+
68
+ def gemfile_content
69
+ @gemfile_content ||= <<~GEMFILE
70
+ source 'https://rubygems.org'
71
+
72
+ gem 'rails', path: '#{@repo_path}'
73
+
74
+ gem 'mysql2', '0.5.2'
75
+ gem 'pg', '1.1.4'
76
+ gem 'benchmark-ips', '~> 2.7.2'
77
+ gem 'redis', '~> 4.1.2'
78
+ gem 'puma', '~> 3.12.1'
79
+ GEMFILE
80
+ end
81
+
82
+ def save_dir
83
+ @save_dir ||= File.join(dest_dir, "rails")
84
+ end
85
+
86
+ def require_db?
87
+ filename = @script_url.split("/")[-1]
88
+ filename.match?(/activerecord|scaffold/)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,62 @@
1
+ require 'benchmark/ips'
2
+ require 'json'
3
+
4
+ module Benchmark
5
+ module Runner
6
+ def self.run(label=nil, version:, time:, disable_gc:, warmup:, &block)
7
+ unless block_given?
8
+ raise ArgumentError.new, "You must pass block to run"
9
+ end
10
+
11
+ GC.disable if disable_gc
12
+
13
+ ips_result = compute_ips(time, warmup, label, &block)
14
+ objects_result = compute_objects(&block)
15
+
16
+ print_output(ips_result, objects_result, label, version)
17
+ end
18
+
19
+ def self.compute_ips(time, warmup, label, &block)
20
+ report = Benchmark.ips(time, warmup, true) do |x|
21
+ x.report(label) { yield }
22
+ end
23
+
24
+ report.entries.first
25
+ end
26
+
27
+ def self.compute_objects(&block)
28
+ if block_given?
29
+ key =
30
+ if RUBY_VERSION < '2.2'
31
+ :total_allocated_object
32
+ else
33
+ :total_allocated_objects
34
+ end
35
+
36
+ before = GC.stat[key]
37
+ yield
38
+ after = GC.stat[key]
39
+ after - before
40
+ end
41
+ end
42
+
43
+ def self.print_output(ips_result, objects_result, label, version)
44
+ standard_deviation =
45
+ # https://github.com/evanphx/benchmark-ips/commit/b42c3dfbe104f32ce7db075a01858d598da87a8b
46
+ if ips_result.respond_to?(:error_percentage)
47
+ ips_result.error_percentage
48
+ else
49
+ ips_result.stddev_percentage # deprecated
50
+ end
51
+ output = {
52
+ label: label,
53
+ version: version,
54
+ iterations_per_second: ips_result.ips,
55
+ iterations_per_second_standard_deviation: standard_deviation,
56
+ total_allocated_objects_per_iteration: objects_result,
57
+ }.to_json
58
+
59
+ puts output
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ STRING_COLUMNS_COUNT = 25
2
+
3
+ def db_adapter
4
+ ENV['DATABASE_URL'].split(":")[0]
5
+ end
6
+
7
+ def db_setup(script:)
8
+ Dir.chdir("../../support/setup") do
9
+ `DATABASE_URL=#{ENV.fetch("DATABASE_URL")} BUNDLE_GEMFILE=Gemfile ruby #{script}`
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "true"
3
+ BUNDLE_PATH: "vendor/bundle"
4
+ BUNDLE_JOBS: "4"
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ group :postgres do
4
+ gem "pg"
5
+ end
6
+
7
+ group :mysql do
8
+ gem "mysql2"
9
+ end
10
+
11
+ gem "activerecord"
@@ -0,0 +1,16 @@
1
+ require "bundler/setup"
2
+ require "active_record"
3
+ require_relative "../helpers"
4
+
5
+ ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL'))
6
+ ActiveRecord::Migration.verbose = false
7
+
8
+ ActiveRecord::Schema.define do
9
+ create_table :users, force: true do |t|
10
+ STRING_COLUMNS_COUNT.times do |i|
11
+ t.string :"column#{i}"
12
+ end
13
+
14
+ t.timestamps null: false
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ require "bundler/setup"
2
+ require "active_record"
3
+
4
+ ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL'))
5
+ ActiveRecord::Migration.verbose = false
6
+
7
+ ActiveRecord::Schema.define do
8
+ create_table :users, force: true do |t|
9
+ t.string :name, :email
10
+ t.timestamps null: false
11
+ end
12
+ end
@@ -0,0 +1,164 @@
1
+ require "bundler/setup"
2
+ require "active_record"
3
+
4
+ ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL'))
5
+
6
+ ActiveRecord::Migration.verbose = false
7
+
8
+ ActiveRecord::Schema.define do
9
+ create_table :topics, force: true do |t|
10
+ t.timestamps null: false
11
+ t.string "title"
12
+ t.datetime "bumped_at"
13
+ t.string "archetype"
14
+ t.datetime "deleted_at"
15
+ t.boolean "pinned_globally"
16
+ t.datetime "pinned_at"
17
+ t.integer "user_id", null: false
18
+ t.integer "category_id"
19
+
20
+ t.index ["deleted_at", "archetype", "category_id", "id"], name: "idx_topics_front_page", using: :btree
21
+ t.index ["user_id"], name: "idx_topics_user_id_deleted_at", using: :btree
22
+ t.index ["bumped_at"], name: "index_topics_on_bumped_at", using: :btree
23
+ t.index ["id", "deleted_at"], name: "index_topics_on_id_and_deleted_at", using: :btree
24
+ t.index ["pinned_at"], name: "index_topics_on_pinned_at", using: :btree
25
+ t.index ["pinned_globally"], name: "index_topics_on_pinned_globally", using: :btree
26
+ end
27
+
28
+ create_table :topic_users, force: true do |t|
29
+ t.timestamps null: false
30
+ t.integer "user_id", null: false
31
+ t.integer "topic_id", null: false
32
+ t.integer "notification_level"
33
+ t.datetime "cleared_pinned_at"
34
+
35
+ t.index ["topic_id", "user_id"], name: "idx_topic_users_on_topic_id_and_user_id", unique: true, using: :btree
36
+ t.index ["user_id", "topic_id"], name: "idx_topic_users_on_user_id_and_topic_id", unique: true, using: :btree
37
+ end
38
+
39
+ create_table :categories, force: true do |t|
40
+ t.timestamps null: false
41
+ t.integer "topic_id"
42
+ end
43
+
44
+ create_table :category_users, force: true do |t|
45
+ t.timestamps null: false
46
+ t.integer "user_id", null: false
47
+ t.integer "category_id", null: false
48
+ t.integer "notification_level"
49
+
50
+ t.index ["user_id", "category_id", "notification_level"], name: "idx_category_users_on_user_cat_and_not", unique: true, using: :btree
51
+ t.index ["category_id", "user_id", "notification_level"], name: "idx_category_users_on_cat_user_and_not", unique: true, using: :btree
52
+ end
53
+
54
+ create_table :users, force: true do |t|
55
+ t.timestamps null: false
56
+ t.string "username"
57
+
58
+ t.index ["username"], name: "index_users_on_username", unique: true, using: :btree
59
+ end
60
+ end
61
+
62
+ class User < ActiveRecord::Base
63
+ has_many :topic_users
64
+ has_many :category_users
65
+ has_many :topics
66
+ end
67
+
68
+ class Topic < ActiveRecord::Base
69
+ has_many :topic_users
70
+ has_many :categories
71
+ belongs_to :user
72
+ belongs_to :category
73
+ scope :listable_topics, -> { where('topics.archetype <> ?', 'private_message') }
74
+ end
75
+
76
+ class TopicUser < ActiveRecord::Base
77
+ belongs_to :topic
78
+ belongs_to :user
79
+ end
80
+
81
+ class Category < ActiveRecord::Base
82
+ has_many :category_users
83
+ has_many :topics
84
+ belongs_to :topic
85
+ end
86
+
87
+ class CategoryUser < ActiveRecord::Base
88
+ belongs_to :category
89
+ belongs_to :user
90
+ end
91
+
92
+ # Helpers
93
+
94
+ def archetype(i)
95
+ if i % 4 == 0
96
+ "not_private_message"
97
+ else
98
+ "private_message"
99
+ end
100
+ end
101
+
102
+ def bumped_at(i, user_id)
103
+ Time.at(50000 + i + user_id)
104
+ end
105
+
106
+ def deleted_at(i, user_id)
107
+ if i % 3 == 0
108
+ Time.at(100000 + i + user_id)
109
+ end
110
+ end
111
+
112
+ def pinned_globally(i)
113
+ i % 2 == 0
114
+ end
115
+
116
+ def pinned_at(i, user_id)
117
+ if i % 2 == 0
118
+ Time.at(1000 + i + user_id)
119
+ end
120
+ end
121
+
122
+ def notification_level(i)
123
+ i % 2 == 0 ? 1 : 0
124
+ end
125
+
126
+ def cleared_pinned_at(topic)
127
+ if topic.pinned_at
128
+ topic.pinned_at + 5
129
+ end
130
+ end
131
+
132
+ # Stage DB
133
+
134
+ 10.times do |i|
135
+ User.create!(username: "user#{i}")
136
+ end
137
+
138
+ User.all.each do |user|
139
+ 500.times do |i|
140
+ user.topics.create!(
141
+ title: "#{user.username} topic #{i}",
142
+ pinned_globally: pinned_globally(i),
143
+ bumped_at: bumped_at(i, user.id),
144
+ archetype: archetype(i),
145
+ deleted_at: deleted_at(i, user.id),
146
+ pinned_at: pinned_at(i, user.id)
147
+ )
148
+ end
149
+ end
150
+
151
+ 200.times do |i|
152
+ Category.create!(topic: Topic.limit(1).offset(i).first)
153
+ end
154
+
155
+ Topic.last(100).each_with_index do |topic, i|
156
+ topic.category = Category.offset(i).limit(1).first
157
+ topic.save!
158
+ end
159
+
160
+ User.all.each do |user|
161
+ Topic.where.not(user: user).each_with_index do |topic, i|
162
+ TopicUser.create!(user: user, topic: topic, cleared_pinned_at: cleared_pinned_at(topic), notification_level: notification_level(i))
163
+ end
164
+ end
@@ -0,0 +1,37 @@
1
+ require "bundler/setup"
2
+ require "active_record"
3
+
4
+ ActiveRecord::Base.establish_connection(ENV.fetch('DATABASE_URL'))
5
+ ActiveRecord::Migration.verbose = false
6
+
7
+ ActiveRecord::Schema.define do
8
+ create_table :users, force: true do |t|
9
+ t.string :name
10
+ t.string :email
11
+ t.boolean :approved
12
+ t.integer :age
13
+ t.datetime :birthday
14
+ t.timestamps null: false
15
+ end
16
+ end
17
+
18
+ class User < ActiveRecord::Base; end
19
+
20
+ attributes = {
21
+ name: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
22
+ email: "foobar@email.com",
23
+ approved: false,
24
+ age: 51,
25
+ birthday: DateTime.now
26
+ }
27
+
28
+ 1000.times do
29
+ User.create!(attributes)
30
+ end
31
+
32
+ User.create!(
33
+ name: 'kir',
34
+ email: 'shatrov@me.com',
35
+ approved: true,
36
+ birthday: DateTime.now
37
+ )