perf_check 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/perf_check +113 -0
- data/lib/perf_check.rb +88 -0
- data/lib/perf_check/git.rb +56 -0
- data/lib/perf_check/server.rb +117 -0
- data/lib/perf_check/test_case.rb +73 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ad9b614a45f9c940d13bb09e72a51cc2828b7bb0
|
4
|
+
data.tar.gz: f4bb7ee841ca754a94637afd3a9c60cf75200b8b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0eae9a892b51c88cbb0121560fd6c25e73167cf81847090d562057a057a087686bf486e398f2599309bb17f6f401e66a3252fbe8cbac52856aef6aa10a76a952
|
7
|
+
data.tar.gz: bf036d6e5baf5a8fd0d4e2f5c5aeb35f99e45c9b52ce598b2a8bc94c48f2fed78b3c5c2d4eae3a7f70bdb4c70a918ce271135daa5ce2c9416ced712809e808a5
|
data/bin/perf_check
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'perf_check'
|
4
|
+
|
5
|
+
perf_check = PerfCheck.new
|
6
|
+
options = perf_check.options
|
7
|
+
options.login = :admin
|
8
|
+
options.number_of_requests = 10
|
9
|
+
options.reference = 'master'
|
10
|
+
|
11
|
+
opts = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: perf_check [options] [route ...]"
|
13
|
+
|
14
|
+
opts.separator 'Login options:'
|
15
|
+
opts.on('--admin', 'Log in as admin user for route') do
|
16
|
+
options.login = :admin
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on('--standard', 'Log in as standard user for route') do
|
20
|
+
options.login = :standard
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on('--super', 'Log in as super user') do
|
24
|
+
options.login = :super
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('--user USER', '-u', 'Log in as USER') do |user|
|
28
|
+
options.login = user
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('--no-login', '-L', "Don't log in") do
|
32
|
+
options.login = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.separator "\nBenchmark options:"
|
36
|
+
opts.on('--requests N', '-n', 'Use N requests in benchmark, defaults to ') do |n|
|
37
|
+
options.number_of_requests = n.to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on('--reference COMMIT', '-r',
|
41
|
+
'Benchmark against COMMIT instead of master') do |commit|
|
42
|
+
options.reference = commit
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on('--quick', '-q',
|
46
|
+
'Fire off 5 requests just on this branch, no comparison with master') do
|
47
|
+
options.number_of_requests = 5
|
48
|
+
options.reference = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.separator ''
|
52
|
+
opts.separator <<EOF
|
53
|
+
Usage examples:
|
54
|
+
Benchmark PostController#index against master
|
55
|
+
perf_check /user/45/posts
|
56
|
+
perf_check /user/45/posts -n5
|
57
|
+
perf_check /user/45/posts --standard
|
58
|
+
|
59
|
+
Benchmark against a specific commit
|
60
|
+
perf_check /user/45/posts -r 0123abcdefg
|
61
|
+
perf_check /user/45/posts -r HEAD~2
|
62
|
+
|
63
|
+
Benchmark the changes in the working tree
|
64
|
+
perf_check /user/45/posts -r HEAD
|
65
|
+
EOF
|
66
|
+
end
|
67
|
+
opts.parse!
|
68
|
+
|
69
|
+
app_root = Dir.pwd
|
70
|
+
until app_root == '/' || File.exist?("#{app_root}/config/application.rb")
|
71
|
+
app_root = File.dirname(app_root)
|
72
|
+
end
|
73
|
+
|
74
|
+
unless File.exist?("#{app_root}/config/application.rb")
|
75
|
+
abort("perf_check should be run from a rails directory")
|
76
|
+
end
|
77
|
+
|
78
|
+
ENV['RAILS_ENV'] = 'development'
|
79
|
+
ENV['PERF_CHECK'] = '1'
|
80
|
+
require File.expand_path("#{app_root}/config/environment")
|
81
|
+
|
82
|
+
|
83
|
+
ARGV.each do |route|
|
84
|
+
perf_check.add_test_case(route)
|
85
|
+
end
|
86
|
+
|
87
|
+
if perf_check.test_cases.empty?
|
88
|
+
abort(opts.help)
|
89
|
+
end
|
90
|
+
|
91
|
+
perf_check.sanity_check
|
92
|
+
perf_check.run
|
93
|
+
|
94
|
+
perf_check.print_results
|
95
|
+
|
96
|
+
# _______________________
|
97
|
+
# < You made it faster!!! >
|
98
|
+
# -----------------------
|
99
|
+
# o . .
|
100
|
+
# o / `. .' "
|
101
|
+
# o .---. < > < > .---.
|
102
|
+
# O | \ \ - ~ ~ - / / |
|
103
|
+
# _____ ..-~ ~-..-~
|
104
|
+
# | | \~~~\.' `./~~~/
|
105
|
+
# ========= \__/ R U B Y T U N E \__/
|
106
|
+
# .' O \ / / \ "
|
107
|
+
# (_____, `._.' | } \/~~~/
|
108
|
+
# `----. / } | / \__/
|
109
|
+
# `-. | / | / `. ,~~|
|
110
|
+
# ~-.__| /_ - ~ ^| /- _ `..-'
|
111
|
+
# | / | / ~-. `-. _ _ _
|
112
|
+
# |_____| |_____| ~ - . _ _ _ _ _>
|
113
|
+
#
|
data/lib/perf_check.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'net/http'
|
5
|
+
require 'digest'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'benchmark'
|
8
|
+
require 'ostruct'
|
9
|
+
|
10
|
+
class PerfCheck
|
11
|
+
attr_accessor :options, :server, :test_cases
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
self.options = OpenStruct.new
|
15
|
+
self.server = Server.new
|
16
|
+
self.test_cases = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_test_case(route)
|
20
|
+
route = PerfCheck.normalize_resource(route)
|
21
|
+
test_cases.push(TestCase.new(route))
|
22
|
+
end
|
23
|
+
|
24
|
+
def sanity_check
|
25
|
+
if Git.current_branch == "master"
|
26
|
+
puts("Yo, profiling master vs. master isn't too useful, but hey, we'll do it")
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "="*77
|
30
|
+
print "PERRRRF CHERRRK! Grab a ☕️ and don't touch your working tree "
|
31
|
+
puts "(we automate git)"
|
32
|
+
puts "="*77
|
33
|
+
end
|
34
|
+
|
35
|
+
def run
|
36
|
+
(options.reference ? 2 : 1).times do |i|
|
37
|
+
if i == 1
|
38
|
+
Git.stash_if_needed
|
39
|
+
Git.checkout_reference(options.reference)
|
40
|
+
test_cases.each{ |x| x.latencies = x.reference_latencies }
|
41
|
+
end
|
42
|
+
|
43
|
+
test_cases.each do |test|
|
44
|
+
server.restart
|
45
|
+
if options.login
|
46
|
+
test.cookie = server.login(options.login, test)
|
47
|
+
end
|
48
|
+
|
49
|
+
puts("\n\nBenchmarking #{test.resource}:")
|
50
|
+
test.run(server, options.number_of_requests)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_results
|
56
|
+
puts("==== Results ====")
|
57
|
+
test_cases.each do |test|
|
58
|
+
puts(test.resource)
|
59
|
+
|
60
|
+
if test.reference_latencies.empty?
|
61
|
+
printf("your branch: ".rjust(15)+"%.1fms\n", test.this_latency)
|
62
|
+
next
|
63
|
+
end
|
64
|
+
|
65
|
+
master_latency = sprintf('%.1fms', test.reference_latency)
|
66
|
+
this_latency = sprintf('%.1fms', test.this_latency)
|
67
|
+
|
68
|
+
difference = sprintf('%+.1fms', test.latency_difference)
|
69
|
+
if test.latency_difference < 0
|
70
|
+
formatted_change = sprintf('%.1fx', test.latency_factor)
|
71
|
+
formatted_change = "yours is #{formatted_change} faster!"
|
72
|
+
else
|
73
|
+
formatted_change = sprintf('%.1fx', 1.0 / test.latency_factor)
|
74
|
+
formatted_change = "yours is #{formatted_change} slower!!!"
|
75
|
+
end
|
76
|
+
formatted_change = difference + " (#{formatted_change})"
|
77
|
+
|
78
|
+
puts("master: ".rjust(15) + "#{master_latency}")
|
79
|
+
puts("your branch: ".rjust(15)+ "#{this_latency}")
|
80
|
+
puts("change: ".rjust(15) + "#{formatted_change}")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
require 'perf_check/server'
|
87
|
+
require 'perf_check/test_case'
|
88
|
+
require 'perf_check/git'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
class PerfCheck
|
3
|
+
class Git
|
4
|
+
# the branch we are on while loading the script
|
5
|
+
@current_branch = `git rev-parse --abbrev-ref HEAD`.strip
|
6
|
+
|
7
|
+
def self.current_branch
|
8
|
+
@current_branch
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.checkout_reference(reference='master')
|
12
|
+
checkout(reference)
|
13
|
+
at_exit do
|
14
|
+
puts
|
15
|
+
Git.checkout_current_branch
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.checkout_current_branch
|
20
|
+
checkout(@current_branch)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.checkout(branch)
|
24
|
+
print "Checking out #{branch}... "
|
25
|
+
`git checkout #{branch} --quiet`
|
26
|
+
puts "Problem with git checkout! Bailing..." and exit(1) unless $?.success?
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.stash_if_needed
|
30
|
+
if anything_to_stash?
|
31
|
+
print("Stashing your changes... ")
|
32
|
+
system('git stash -q >/dev/null')
|
33
|
+
puts("Problem with git stash! Bailing...") and exit(1) unless $?.success?
|
34
|
+
at_exit do
|
35
|
+
Git.pop
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.anything_to_stash?
|
41
|
+
git_stash = `git diff`
|
42
|
+
git_stash << `git diff --staged`
|
43
|
+
!git_stash.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.pop
|
47
|
+
puts("Git stash applying...")
|
48
|
+
system('git stash pop -q')
|
49
|
+
puts("Problem with git stash! Bailing...") and exit(1) unless $?.success?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.normalize_resource(resource)
|
54
|
+
resource.sub(/^([^\/])/, '/\1')
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
|
2
|
+
require 'net/http'
|
3
|
+
require 'benchmark'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
class PerfCheck
|
8
|
+
class Server
|
9
|
+
def self.authorization(&block)
|
10
|
+
define_method(:login, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
at_exit do
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def login(login, route)
|
20
|
+
''
|
21
|
+
end
|
22
|
+
|
23
|
+
def pid
|
24
|
+
pidfile = 'tmp/pids/server.pid'
|
25
|
+
File.read(pidfile).to_i if File.exists?(pidfile)
|
26
|
+
end
|
27
|
+
|
28
|
+
def mem
|
29
|
+
mem = `ps -o rss= -p #{pid}`.strip.to_f / 1024
|
30
|
+
end
|
31
|
+
|
32
|
+
def prepare_to_profile
|
33
|
+
FileUtils.mkdir_p('tmp/perf_check/miniprofiler')
|
34
|
+
Dir["tmp/perf_check/miniprofiler/*"].each{|x| FileUtils.rm(x) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def latest_profiler_url
|
38
|
+
mp_timer = Dir["tmp/perf_check/miniprofiler/mp_timers_*"].first
|
39
|
+
if "#{mp_timer}" =~ /mp_timers_(\w+)/
|
40
|
+
mp_link = "/mini-profiler-resources/results?id=#{$1}"
|
41
|
+
FileUtils.mkdir_p('tmp/miniprofiler')
|
42
|
+
FileUtils.mv(mp_timer, mp_timer.sub(/^tmp\/perf_check\//, 'tmp/'))
|
43
|
+
end
|
44
|
+
mp_link
|
45
|
+
end
|
46
|
+
|
47
|
+
def profile
|
48
|
+
http = Net::HTTP.new(host, port).tap{ |http| http.read_timeout = 1000 }
|
49
|
+
response = nil
|
50
|
+
prepare_to_profile
|
51
|
+
|
52
|
+
latency = 1000 * Benchmark.measure do
|
53
|
+
http.start
|
54
|
+
response = yield(http)
|
55
|
+
http.finish
|
56
|
+
end.real
|
57
|
+
|
58
|
+
case response.code
|
59
|
+
when '200'
|
60
|
+
Profile.new.tap do |result|
|
61
|
+
result.latency = latency
|
62
|
+
result.profile_url = latest_profiler_url
|
63
|
+
result.response_body = response.body
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise Server::ApplicationError.new(response) unless response.code == '200'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def exit
|
71
|
+
p = pid
|
72
|
+
if p
|
73
|
+
Process.kill('QUIT', pid)
|
74
|
+
sleep(1.5)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def start
|
79
|
+
system('rails server -b 127.0.0.1 -d -p 3031 >/dev/null')
|
80
|
+
sleep(1.5)
|
81
|
+
|
82
|
+
@running = true
|
83
|
+
end
|
84
|
+
|
85
|
+
def restart
|
86
|
+
if !@running
|
87
|
+
print "starting rails..."
|
88
|
+
start
|
89
|
+
else
|
90
|
+
print "re-starting rails..."
|
91
|
+
exit
|
92
|
+
start
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def host
|
97
|
+
"127.0.0.1"
|
98
|
+
end
|
99
|
+
|
100
|
+
def port
|
101
|
+
3031
|
102
|
+
end
|
103
|
+
|
104
|
+
class ApplicationError < Exception
|
105
|
+
def initialize(resp)
|
106
|
+
@response = resp
|
107
|
+
end
|
108
|
+
def code
|
109
|
+
@response.code
|
110
|
+
end
|
111
|
+
def body
|
112
|
+
@response.body
|
113
|
+
end
|
114
|
+
end
|
115
|
+
class Profile < OpenStruct; end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
class PerfCheck
|
4
|
+
class TestCase
|
5
|
+
attr_accessor :resource, :controller, :action
|
6
|
+
attr_accessor :latencies, :cookie
|
7
|
+
attr_accessor :this_latencies, :reference_latencies
|
8
|
+
|
9
|
+
def initialize(route)
|
10
|
+
params = Rails.application.routes.recognize_path(route)
|
11
|
+
|
12
|
+
self.this_latencies = []
|
13
|
+
self.reference_latencies = []
|
14
|
+
self.latencies = this_latencies
|
15
|
+
|
16
|
+
self.controller = params[:controller].split('/')[-1]
|
17
|
+
self.action = params[:action]
|
18
|
+
self.resource = route
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(server, count)
|
22
|
+
(count+1).times do |i|
|
23
|
+
errors = 0
|
24
|
+
begin
|
25
|
+
profile = server.profile do |http|
|
26
|
+
http.get(resource, {'Cookie' => cookie})
|
27
|
+
end
|
28
|
+
rescue Server::ApplicationError => e
|
29
|
+
File.open("public/perf_check_failed_request.html", 'w') do |error_dump|
|
30
|
+
error_dump.write(e.body)
|
31
|
+
end
|
32
|
+
printf("\tRequest %2i: —— FAILURE (HTTP %s): %s\n",
|
33
|
+
i, e.code, '/perf_check_failed_request.html')
|
34
|
+
exit(1)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Disregard initial request, since in dev. mode it includes
|
38
|
+
# all the autoload overhead (?)
|
39
|
+
next if i.zero?
|
40
|
+
|
41
|
+
printf("\tRequest %2i: %.1fms\t%4dMB\t%s\n",
|
42
|
+
i, profile.latency, server.mem, profile.profile_url)
|
43
|
+
|
44
|
+
self.latencies << profile.latency
|
45
|
+
end
|
46
|
+
puts
|
47
|
+
end
|
48
|
+
|
49
|
+
def this_latency
|
50
|
+
this_latencies.inject(0.0, :+) / this_latencies.size
|
51
|
+
end
|
52
|
+
|
53
|
+
def reference_latency
|
54
|
+
reference_latencies.inject(0.0, :+) / reference_latencies.size
|
55
|
+
end
|
56
|
+
|
57
|
+
def latency_difference
|
58
|
+
this_latency - reference_latency
|
59
|
+
end
|
60
|
+
|
61
|
+
def latency_factor
|
62
|
+
reference_latency / this_latency
|
63
|
+
end
|
64
|
+
|
65
|
+
def eql?(test)
|
66
|
+
resource == test.resource
|
67
|
+
end
|
68
|
+
|
69
|
+
def hash
|
70
|
+
resource.hash
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: perf_check
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- rubytune
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-30 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
executables:
|
16
|
+
- perf_check
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/perf_check
|
21
|
+
- lib/perf_check.rb
|
22
|
+
- lib/perf_check/git.rb
|
23
|
+
- lib/perf_check/server.rb
|
24
|
+
- lib/perf_check/test_case.rb
|
25
|
+
homepage: https://github.com/rubytune/perf_check
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.2.2
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: PERF CHECKKK!
|
49
|
+
test_files: []
|