Skip to content

Commit

Permalink
Make benchmarks run on multiple branches without conflicts
Browse files Browse the repository at this point in the history
- copy target files to a temp directory
- benchmark cache/no cache on each branch
- compare branches
- remove rake dependency that loads unnecessary files
  - run with ./bin/bench
- remove git gem dependency

Master looks slower than some older refs; need to confirm
  • Loading branch information
bf4 committed Dec 23, 2015
1 parent a3da64c commit aab0a88
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 42 deletions.
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,4 @@ end

group :development, :test do
gem 'rubocop', '~> 0.34.0', require: false
gem 'git'
end
35 changes: 0 additions & 35 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,38 +48,3 @@ task default: [:test, :rubocop]

desc 'CI test task'
task :ci => [:default]

require 'git'
require 'benchmark'
Rake::TestTask.new :benchmark_tests do |t|
t.libs << "test"
t.test_files = FileList['test/**/*_benchmark.rb']
t.ruby_opts = ['-r./test/test_helper.rb']
t.verbose = true
end

task :benchmark do
@git = Git.init('.')
ref = @git.current_branch

actual = run_benchmark_spec ref
master = run_benchmark_spec 'master'

@git.checkout(ref)

puts "\n\nResults ============================\n"
puts "------------------------------------~> (Branch) MASTER"
puts master
puts "------------------------------------\n\n"

puts "------------------------------------~> (Actual Branch) #{ref}"
puts actual
puts "------------------------------------"
end

def run_benchmark_spec(ref)
@git.checkout(ref)
response = Benchmark.realtime { Rake::Task['benchmark_tests'].invoke }
Rake::Task['benchmark_tests'].reenable
response
end
1 change: 1 addition & 0 deletions active_model_serializers.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
spec.files = `git ls-files -z`.split("\x0")
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
spec.executables = []

spec.required_ruby_version = '>= 2.0.0'

Expand Down
81 changes: 81 additions & 0 deletions bin/bench
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env ruby
require 'fileutils'
require 'benchmark'
require 'pathname'
require 'shellwords'

ROOT = Pathname File.expand_path(['..', '..'].join(File::Separator), __FILE__)
TMP_DIR = File.join(ROOT, 'tmp/bench')

def temp_dir_empty?
Dir[File.join(TMP_DIR, '*')].none?
end

def empty_temp_dir
FileUtils.mkdir_p(TMP_DIR)
Dir[File.join(TMP_DIR, '*')].each do |file|
FileUtils.rm(file)
end
end

def fill_temp_dir
Dir[File.join(ROOT, 'test', '**', '*_benchmark.rb')].each do |file|
FileUtils.cp(file, File.join(TMP_DIR, File.basename(file)))
end
at_exit { empty_temp_dir }
end

def refresh_temp_dir
empty_temp_dir
fill_temp_dir
end

def benchmark_tests
refresh_temp_dir if temp_dir_empty?
system("bundle exec ruby -Ilib:test #{Shellwords.shellescape(TMP_DIR)}/*.rb")
end

def current_branch
@current_branch ||= `cat .git/HEAD | cut -d/ -f3,4,5`.chomp
end

def checkout_ref(ref)
puts `git checkout #{ref}`.chomp
abort "Checkout failed: #{ref}, #{$?.exitstatus}" unless $?.success?
end

def benchmark
refresh_temp_dir
ref = current_branch

actual = run_benchmark_at_ref ref
master = run_benchmark_at_ref 'master'

checkout_ref(ref)

puts "\n\nResults ============================\n"
puts "------------------------------------~> (Branch) MASTER"
puts master
puts "------------------------------------\n\n"

puts "------------------------------------~> (Actual Branch) #{ref}"
puts actual
puts "------------------------------------"
end

def run_benchmark
response = Benchmark.realtime {
benchmark_tests
}
benchmark_tests
response
end

def run_benchmark_at_ref(ref)
checkout_ref(ref)
run_benchmark
end

if $0 == __FILE__
benchmark
end
166 changes: 160 additions & 6 deletions test/benchmark/serialization_benchmark.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,160 @@
require 'test_helper'
require 'bundler/setup'

require 'rails'
require 'action_controller'
require 'action_controller/test_case'
require 'action_controller/railtie'
require 'active_support/json'
require 'minitest/autorun'
# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)

class Foo < Rails::Application
if Rails.version.to_s.start_with? '4'
config.action_controller.perform_caching = true
config.active_support.test_order = :random
ActionController::Base.cache_store = :memory_store
config.eager_load = false
config.secret_key_base = 'abc123'
end
end

require 'active_model_serializers'

# require 'fixtures/poro'

module TestHelper
Routes = ActionDispatch::Routing::RouteSet.new
Routes.draw do
get ':controller(/:action(/:id))'
get ':controller(/:action)'
end

ActionController::Base.send :include, Routes.url_helpers
end

ActionController::TestCase.class_eval do
def setup
@routes = TestHelper::Routes
end
end

class Model
def initialize(hash = {})
@attributes = hash
end

def cache_key
"#{self.class.name.downcase}/#{id}-#{updated_at}"
end

def updated_at
@attributes[:updated_at] ||= Time.current.to_i
end

def read_attribute_for_serialization(name)
if name == :id || name == 'id'
id
else
@attributes[name]
end
end

def id
@attributes[:id] || @attributes['id'] || object_id
end

def to_param
id
end

def method_missing(meth, *args)
if meth.to_s =~ /^(.*)=$/
@attributes[Regexp.last_match(1).to_sym] = args[0]
elsif @attributes.key?(meth)
@attributes[meth]
else
super
end
end
end
AuthorSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name

has_many :posts, embed: :ids
has_one :bio
end
BlogSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :name
end
CommentSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :body

def custom_options
options
end
end
PostSerializer = Class.new(ActiveModel::Serializer) do
attributes :id, :title, :body

has_many :comments, serializer: CommentSerializer
belongs_to :blog, serializer: BlogSerializer
belongs_to :author, serializer: AuthorSerializer

def blog
Blog.new(id: 999, name: 'Custom blog')
end

def custom_options
options
end
end

CachingAuthorSerializer = Class.new(AuthorSerializer) do
cache key: 'writer'
end
CachingCommentSerializer = Class.new(CommentSerializer) do
cache expires_in: 1.day
end
CachingPostSerializer = Class.new(PostSerializer) do
cache key: 'post', expires_in: 0.1
belongs_to :blog, serializer: BlogSerializer
belongs_to :author, serializer: CachingAuthorSerializer
has_many :comments, serializer: CachingCommentSerializer
end

Comment = Class.new(Model)
Author = Class.new(Model)
Post = Class.new(Model)
Blog = Class.new(Model)

module ActionController
module Serialization
class SerializerTest < ActionController::TestCase
class PostController < ActionController::Base

def render_with_cache_enable
comment = Comment.new({ id: 1, body: 'ZOMG A COMMENT' })
comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
author = Author.new(id: 1, name: 'Joao Moura.')
post = Post.new({ id: 1, title: 'New Post', blog:nil, body: 'Body', comments: [comment], author: author })
post = Post.new(id: 1, title: 'New Post', blog: nil, body: 'Body', comments: [comment], author: author)

render json: post
end

def render_with_cache_disabled
comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
author = Author.new(id: 1, name: 'Joao Moura.')
post = Post.new(id: 1, title: 'New Post', blog: nil, body: 'Body', comments: [comment], author: author)

render json: post, serializer: CachingPostSerializer
end
end

tests PostController

def test_render_with_cache_enable
def test_render_benchmark
ActionController::Base.cache_store.clear
get :render_with_cache_enable
get :render_with_cache_disabled

expected = {
id: 1,
Expand All @@ -42,7 +178,25 @@ def test_render_with_cache_enable
assert_equal 'application/json', @response.content_type
assert_equal expected.to_json, @response.body

get :render_with_cache_enable
n = 1_000
Benchmark.bmbm do |x|
x.report('cache') do
ActionController::Base.cache_store.clear
i = 0
while i < n
get :render_with_cache_enable
i += 1
end
end
x.report('no cache') do
ActionController::Base.cache_store.clear
i = 0
while i < n
get :render_with_cache_disabled
i += 1
end
end
end
assert_equal expected.to_json, @response.body
end
end
Expand Down

0 comments on commit aab0a88

Please sign in to comment.