Skip to content

Commit

Permalink
Set up CI benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitryTsepelev committed Jun 27, 2019
1 parent 113212e commit 2e3937b
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 270 deletions.
45 changes: 39 additions & 6 deletions .travis.yml
Expand Up @@ -3,37 +3,70 @@ cache: bundler

sudo: false

script:
- "RUBYOPT='-w' bundle exec rake $TASK"

rvm:
- 2.3
- 2.4
- 2.5
- 2.6
- ruby-head

gemfile:
- gemfiles/rails_4_2.gemfile
- gemfiles/rails_5_0.gemfile
- gemfiles/rails_5_1.gemfile
- gemfiles/rails_5_2.gemfile
- gemfiles/rails_6_0.gemfile
- gemfiles/railsmaster.gemfile

notifications:
email: false
env:
- TASK=spec
- TASK=bench
- TASK=memory

matrix:
fast_finish: true
exclude:
- rvm: 2.3
gemfile: gemfiles/railsmaster.gemfile
env: TASK=bench
- rvm: 2.4
gemfile: gemfiles/railsmaster.gemfile
env: TASK=bench
- rvm: 2.5
env: TASK=bench
- rvm: ruby-head
env: TASK=bench

- rvm: 2.3
env: TASK=memory
- rvm: 2.4
env: TASK=memory
- rvm: 2.5
env: TASK=memory
- rvm: ruby-head
env: TASK=memory

- rvm: 2.3
gemfile: gemfiles/rails_6_0.gemfile
- rvm: 2.3
gemfile: gemfiles/railsmaster.gemfile

- rvm: 2.4
gemfile: gemfiles/rails_6_0.gemfile
- rvm: 2.4
gemfile: gemfiles/railsmaster.gemfile

include:
- rvm: 2.6
gemfile: gemfiles/rails_5_0.gemfile
- rvm: 2.6
gemfile: gemfiles/rails_5_1.gemfile

allow_failures:
- rvm: ruby-head

notifications:
email: false

before_install:
- ls /home/travis/.rvm/gems/
- rm /home/travis/.rvm/gems/ruby-2.4.6@global/specifications/bundler-2.0.1.gemspec || true
Expand Down
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -63,5 +63,8 @@ ArLazyPreload.config.auto_preload = true

After that there is no need to call `lazy_preload` on the association, everything would be loaded lazily.

> Worried about the performance? Take a look at [benchmarks](https://travis-ci.org/DmitryTsepelev/ar_lazy_preload) (`TASK=bench` and `TASK=memory`)
## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
10 changes: 10 additions & 0 deletions Rakefile
Expand Up @@ -13,3 +13,13 @@ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
else
task default: [:rubocop, :spec]
end

task :bench do
cmd = %w[bundle exec ruby benchmark/main.rb]
exit system(*cmd)
end

task :memory do
cmd = %w[bundle exec ruby benchmark/memory.rb]
exit system(*cmd)
end
53 changes: 53 additions & 0 deletions benchmark/base_bench.rb
@@ -0,0 +1,53 @@
# frozen_string_literal: true

$:.push File.expand_path("lib", __dir__)

require "memory_profiler"

ENV["RAILS_ENV"] = "test"

require_relative "./../spec/dummy/config/environment"

require "active_record"
require "ar_lazy_preload"

ActiveRecord::Base.establish_connection(
adapter: "sqlite3",
database: ":memory:"
)

require_relative "./../spec/helpers/schema"
require_relative "./../spec/helpers/models"

class BaseBench
def run
raise NotImplementedError
end

protected

# From https://stackoverflow.com/a/20640938/838346
def without_gc
GC.start # start out clean
GC.disable
yield
GC.enable
end

def setup_data
# Setup data
10.times do
user_1 = User.create!
user_2 = User.create!
100.times do
post_1 = Post.create!(user: user_1)
post_2 = Post.create!(user: user_2)

10.times do
Comment.create!(post: post_1, user: user_2)
Comment.create!(post: post_2, user: user_1)
end
end
end
end
end
204 changes: 36 additions & 168 deletions benchmark/main.rb
@@ -1,187 +1,55 @@
# frozen_string_literal: true

$:.push File.expand_path("lib", __dir__)
require_relative "./base_bench"

require "benchmark"
class Bench < BaseBench
def run
setup_data

ENV["RAILS_ENV"] = "test"
Benchmark.bm(50) do |x|
run_bench(x, usage_percent: 100)
run_bench(x, usage_percent: 50)
run_bench(x, usage_percent: 10)
run_bench(x, usage_percent: 0)

require_relative "./../spec/dummy/config/environment"
run_bench(x, usage_percent: 100, auto_preload: false)
run_bench(x, usage_percent: 50, auto_preload: false)
run_bench(x, usage_percent: 10, auto_preload: false)
run_bench(x, usage_percent: 0, auto_preload: false)

require "active_record"
require "ar_lazy_preload"

ActiveRecord::Base.establish_connection(
adapter: "sqlite3",
database: ":memory:"
)

require_relative "./../spec/helpers/schema"
require_relative "./../spec/helpers/models"

# From https://stackoverflow.com/a/20640938/838346
def without_gc
GC.start # start out clean
GC.disable
yield
GC.enable
end

# Setup data
10.times do
user_1 = User.create!
user_2 = User.create!
100.times do
post_1 = Post.create!(user: user_1)
post_2 = Post.create!(user: user_2)

10.times do
Comment.create!(post: post_1, user: user_2)
Comment.create!(post: post_2, user: user_1)
end
end
end

Benchmark.bm(50) do |x|
without_gc do
x.report("AR eager loading w/ 100% usage: ") do
::User.all.includes(posts: :comments).map do |user|
user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
end
run_bench(x, usage_percent: 100, auto_preload: true)
run_bench(x, usage_percent: 50, auto_preload: true)
run_bench(x, usage_percent: 10, auto_preload: true)
run_bench(x, usage_percent: 0, auto_preload: true)
end
end

without_gc do
x.report("AR eager loading w/ 50% usage: ") do
::User.all.includes(posts: :comments).map do |user|
next nil if user.id.odd?
private

user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
def run_bench(x, usage_percent:, auto_preload: nil)
label =
case auto_preload
when nil then "AR eager loading"
when false then "AR lazy preloading w/o auto_preload"
when true then "AR lazy preloading w/ auto_preload"
end
end
end

without_gc do
x.report("AR eager loading w/ 10% usage: ") do
::User.all.includes(posts: :comments).map do |user|
next nil if (user.id % 10).positive?
without_gc do
ArLazyPreload.config.auto_preload = auto_preload unless auto_preload.nil?

user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
end
end
end

without_gc do
x.report("AR eager loading w/ 0% usage: ") do
::User.all.includes(posts: :comments).map do |user|
user.id
end
end
end
scope = auto_preload ? User.all : ::User.all.includes(posts: :comments)

without_gc do
x.report("AR lazy preloading w/o auto_preload w/ 100% usage: ") do
ArLazyPreload.config.auto_preload = false

::User.all.lazy_preload(posts: :comments).map do |user|
user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
x.report("#{label} #{usage_percent}% usage: ") do
scope.map do |user|
if usage_percent.zero?
user.id
elsif (user.id % (100 / usage_percent)).zero?
user.posts.each { |post| post.comments.each(&:id) }
end
end
end
end
end

without_gc do
x.report("AR lazy preloading w/o auto_preload w/ 50% usage: ") do
ArLazyPreload.config.auto_preload = false

::User.all.lazy_preload(posts: :comments).map do |user|
next nil if user.id.odd?

user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
end
end
end

without_gc do
x.report("AR lazy preloading w/o auto_preload w/ 10% usage: ") do
ArLazyPreload.config.auto_preload = false

::User.all.lazy_preload(posts: :comments).map do |user|
next nil if (user.id % 10).positive?

user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
end
end
end

without_gc do
x.report("AR lazy preloading w/o auto_preload w/ 0% usage: ") do
ArLazyPreload.config.auto_preload = false

::User.all.lazy_preload(posts: :comments).map do |user|
user.id
end
end
end

without_gc do
x.report("AR lazy preloading w/ auto_preload w/ 100% usage: ") do
ArLazyPreload.config.auto_preload = true

::User.all.map do |user|
user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
end
end
end

without_gc do
x.report("AR lazy preloading w/ auto_preload w/ 50% usage: ") do
ArLazyPreload.config.auto_preload = true

::User.all.map do |user|
next nil if user.id.odd?

user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
end
end
end

without_gc do
x.report("AR lazy preloading w/ auto_preload w/ 10% usage: ") do
ArLazyPreload.config.auto_preload = true

::User.all.map do |user|
next nil if (user.id % 10).positive?

user.posts.to_a.each do |post|
post.comments.to_a.each {|c| c.id}
end
end
end
end

without_gc do
x.report("AR lazy preloading w/ auto_preload w/ 0% usage: ") do
ArLazyPreload.config.auto_preload = true

::User.all.map do |user|
user.id
end
end
end
end

Bench.new.run

0 comments on commit 2e3937b

Please sign in to comment.