perf_check 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|