benchmark-http 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b3ee38b73acca01fe8fa41779cf8ce54df543e70a383efb931ae535f7faf53f4
4
+ data.tar.gz: 7291fa79b4ed7e60aaecef5be5c7241ce4e6c6aeac5ea930b006ca4e677672d1
5
+ SHA512:
6
+ metadata.gz: 2a1abfcbfc272ff0ed63ddb5ba98fb05479d61fe8a69cdb417cf814334252bad7d4fb12919f0a4c5e42c081809a30594376a1e56e4d88299f2df9bf576df4239
7
+ data.tar.gz: 34d9b1f2c5d3208d42e57d503dbda173a3671777ae5f15bcd821ae849968004d19fef8131d9ea7b17bb5bbd52c042d7fab8bd851e2a2042d8131a40eee0f09a5
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ sudo: required
3
+ dist: xenial
4
+ cache: bundler
5
+
6
+ before_script:
7
+ - gem update --system
8
+
9
+ matrix:
10
+ include:
11
+ - rvm: 2.3
12
+ - rvm: 2.4
13
+ - rvm: 2.5
14
+ - rvm: jruby-head
15
+ env: JRUBY_OPTS="--debug -X+O"
16
+ - rvm: ruby-head
17
+ - rvm: rbx-3
18
+ allow_failures:
19
+ - rvm: ruby-head
20
+ - rvm: jruby-head
21
+ - rvm: rbx-3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in benchmark-http.gemspec
6
+ gemspec
@@ -0,0 +1,144 @@
1
+ # Benchmark::HTTP
2
+
3
+ An asynchronous HTTP benchmark tool built on top of [async], [async-io] and [async-http]. Useful for analysing server performance. Supports HTTP1 and HTTP2.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/socketry/benchmark-http.svg)](http://travis-ci.org/socketry/benchmark-http)
6
+ [![Code Climate](https://codeclimate.com/github/socketry/benchmark-http.svg)](https://codeclimate.com/github/socketry/benchmark-http)
7
+ [![Coverage Status](https://coveralls.io/repos/socketry/benchmark-http/badge.svg)](https://coveralls.io/r/socketry/benchmark-http)
8
+
9
+ [async]: https://github.com/socketry/async
10
+ [async-io]: https://github.com/socketry/async-io
11
+ [async-http]: https://github.com/socketry/async-http
12
+
13
+ ## Installation
14
+
15
+ Install it yourself:
16
+
17
+ $ gem install benchmark-http
18
+
19
+ ## Usage
20
+
21
+ You can run `benchmark-http` is a top level tool for invoking specific benchmarks.
22
+
23
+ ### Concurrency
24
+
25
+ This benchmark determines the optimal level of concurrency (maximise throughput while keeping latency to a minimum).
26
+
27
+ ```shell
28
+ $ benchmark-http concurrency https://www.oriontransfer.co.nz/welcome/index
29
+ I am going to benchmark https://www.oriontransfer.co.nz/welcome/index...
30
+ I am running 1 asynchronous tasks that will each make sequential requests...
31
+ I made 273 requests in 52.4s. The per-request latency was 191.79ms. That's 5.214149911737622 asynchronous requests/second.
32
+ Variance: 997.437µs
33
+ Standard Deviation: 31.58ms
34
+ Standard Error: 0.0019114428646174592
35
+ I am running 2 asynchronous tasks that will each make sequential requests...
36
+ I made 177 requests in 16.8s. The per-request latency was 190.19ms. That's 10.51600772540387 asynchronous requests/second.
37
+ Variance: 632.076µs
38
+ Standard Deviation: 25.14ms
39
+ Standard Error: 0.001889722381767832
40
+ I am running 4 asynchronous tasks that will each make sequential requests...
41
+ I made 8 requests in 372.49ms. The per-request latency was 186.25ms. That's 21.476841588829895 asynchronous requests/second.
42
+ Variance: 0.048µs
43
+ Standard Deviation: 219.819µs
44
+ Standard Error: 7.771792696588776e-05
45
+ I am running 8 asynchronous tasks that will each make sequential requests...
46
+ I made 128 requests in 3.0s. The per-request latency was 188.10ms. That's 42.53004421587869 asynchronous requests/second.
47
+ Variance: 399.781µs
48
+ Standard Deviation: 19.99ms
49
+ Standard Error: 0.0017672840585127617
50
+ I am running 16 asynchronous tasks that will each make sequential requests...
51
+ I made 184 requests in 2.2s. The per-request latency was 188.46ms. That's 84.89938672881854 asynchronous requests/second.
52
+ Variance: 548.641µs
53
+ Standard Deviation: 23.42ms
54
+ Standard Error: 0.0017267724185582615
55
+ I am running 32 asynchronous tasks that will each make sequential requests...
56
+ I made 152 requests in 891.06ms. The per-request latency was 187.59ms. That's 170.58399627520865 asynchronous requests/second.
57
+ Variance: 335.694µs
58
+ Standard Deviation: 18.32ms
59
+ Standard Error: 0.00148610620533633
60
+ I am running 64 asynchronous tasks that will each make sequential requests...
61
+ I made 438 requests in 1.3s. The per-request latency was 191.68ms. That's 333.89790173541496 asynchronous requests/second.
62
+ Variance: 1.19ms
63
+ Standard Deviation: 34.51ms
64
+ Standard Error: 0.001648801656177374
65
+ I am running 128 asynchronous tasks that will each make sequential requests...
66
+ I made 360 requests in 533.83ms. The per-request latency was 189.81ms. That's 674.373540567776 asynchronous requests/second.
67
+ Variance: 555.212µs
68
+ Standard Deviation: 23.56ms
69
+ Standard Error: 0.0012418759009531876
70
+ I am running 256 asynchronous tasks that will each make sequential requests...
71
+ I made 512 requests in 463.03ms. The per-request latency was 231.51ms. That's 1105.762787139087 asynchronous requests/second.
72
+ Variance: 888.185µs
73
+ Standard Deviation: 29.80ms
74
+ Standard Error: 0.0013170938569343825
75
+ I am running 192 asynchronous tasks that will each make sequential requests...
76
+ I made 384 requests in 380.97ms. The per-request latency was 190.48ms. That's 1007.9615261872923 asynchronous requests/second.
77
+ Variance: 142.770µs
78
+ Standard Deviation: 11.95ms
79
+ Standard Error: 0.0006097518459132856
80
+ I am running 224 asynchronous tasks that will each make sequential requests...
81
+ I made 448 requests in 411.79ms. The per-request latency was 205.89ms. That's 1087.9398101463066 asynchronous requests/second.
82
+ Variance: 215.480µs
83
+ Standard Deviation: 14.68ms
84
+ Standard Error: 0.0006935294886115942
85
+ I am running 240 asynchronous tasks that will each make sequential requests...
86
+ I made 480 requests in 401.62ms. The per-request latency was 200.81ms. That's 1195.1473779597363 asynchronous requests/second.
87
+ Variance: 292.021µs
88
+ Standard Deviation: 17.09ms
89
+ Standard Error: 0.0007799848849992035
90
+ I am running 248 asynchronous tasks that will each make sequential requests...
91
+ I made 496 requests in 432.58ms. The per-request latency was 216.29ms. That's 1146.621534849607 asynchronous requests/second.
92
+ Variance: 446.681µs
93
+ Standard Deviation: 21.13ms
94
+ Standard Error: 0.0009489813840514241
95
+ I am running 252 asynchronous tasks that will each make sequential requests...
96
+ I made 504 requests in 417.86ms. The per-request latency was 208.93ms. That's 1206.1477638702509 asynchronous requests/second.
97
+ Variance: 222.939µs
98
+ Standard Deviation: 14.93ms
99
+ Standard Error: 0.0006650854376052381
100
+ I am running 254 asynchronous tasks that will each make sequential requests...
101
+ I made 508 requests in 419.67ms. The per-request latency was 209.83ms. That's 1210.4835614086478 asynchronous requests/second.
102
+ Variance: 177.005µs
103
+ Standard Deviation: 13.30ms
104
+ Standard Error: 0.0005902836562252991
105
+ I am running 255 asynchronous tasks that will each make sequential requests...
106
+ I made 510 requests in 434.38ms. The per-request latency was 217.19ms. That's 1174.0936493567908 asynchronous requests/second.
107
+ Variance: 457.592µs
108
+ Standard Deviation: 21.39ms
109
+ Standard Error: 0.000947227304291054
110
+ Your server can handle 255 concurrent requests.
111
+ At this level of concurrency, requests have ~1.13x higher latency.
112
+ ```
113
+
114
+ ## Contributing
115
+
116
+ 1. Fork it
117
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
118
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
119
+ 4. Push to the branch (`git push origin my-new-feature`)
120
+ 5. Create new Pull Request
121
+
122
+ ## License
123
+
124
+ Released under the MIT license.
125
+
126
+ Copyright, 2018, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
127
+
128
+ Permission is hereby granted, free of charge, to any person obtaining a copy
129
+ of this software and associated documentation files (the "Software"), to deal
130
+ in the Software without restriction, including without limitation the rights
131
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
132
+ copies of the Software, and to permit persons to whom the Software is
133
+ furnished to do so, subject to the following conditions:
134
+
135
+ The above copyright notice and this permission notice shall be included in
136
+ all copies or substantial portions of the Software.
137
+
138
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
139
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
140
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
141
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
142
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
143
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
144
+ THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ require_relative "lib/benchmark/http/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "benchmark-http"
5
+ spec.version = Benchmark::HTTP::VERSION
6
+ spec.authors = ["Samuel Williams"]
7
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
8
+
9
+ spec.summary = "An asynchronous benchmark toolbox for modern HTTP servers."
10
+ spec.homepage = "https://github.com/socketry/benchmark-http"
11
+
12
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
13
+ f.match(%r{^(test|spec|features)/})
14
+ end
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency "samovar"
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.16"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec", "~> 3.0"
23
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative '../lib/benchmark/http'
24
+
25
+ begin
26
+ Benchmark::HTTP::Command.parse(ARGV).invoke
27
+ rescue Interrupt
28
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative "http/version"
22
+ require_relative "http/command"
@@ -0,0 +1,72 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'command/concurrency'
22
+ require_relative 'version'
23
+ require 'samovar'
24
+
25
+ module Benchmark
26
+ module HTTP
27
+ module Command
28
+ def self.parse(*args)
29
+ Top.parse(*args)
30
+ end
31
+
32
+ class Top < Samovar::Command
33
+ self.description = "An asynchronous HTTP server benchmark."
34
+
35
+ options do
36
+ option '--verbose | --quiet', "Verbosity of output for debugging.", key: :logging
37
+ option '-h/--help', "Print out help information."
38
+ option '-v/--version', "Print out the application version."
39
+ end
40
+
41
+ nested '<command>',
42
+ 'concurrency' => Concurrency
43
+
44
+ def verbose?
45
+ @options[:logging] == :verbose
46
+ end
47
+
48
+ def quiet?
49
+ @options[:logging] == :quiet
50
+ end
51
+
52
+ def invoke(program_name: File.basename($0))
53
+ if verbose?
54
+ Async.logger.level = Logger::DEBUG
55
+ elsif quiet?
56
+ Async.logger.level = Logger::WARN
57
+ else
58
+ Async.logger.level = Logger::INFO
59
+ end
60
+
61
+ if @options[:version]
62
+ puts "benchmark-http v#{Falcon::VERSION}"
63
+ elsif @options[:help] or @command.nil?
64
+ print_usage(program_name)
65
+ else
66
+ @command.invoke(self)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,124 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative '../seconds'
22
+ require_relative '../statistics'
23
+
24
+ require 'async'
25
+ require 'async/http/client'
26
+ require 'async/http/url_endpoint'
27
+
28
+ require 'samovar'
29
+
30
+ module Benchmark
31
+ module HTTP
32
+ module Command
33
+ class Concurrency < Samovar::Command
34
+ self.description = "Determine the optimal level of concurrency."
35
+
36
+ options do
37
+ option '-t/--threshold <factor>', "The acceptable latency penalty when making concurrent requests", default: 1.2, type: Float
38
+ option '-c/--confidence <factor>', "The confidence required when computing latency (lower is less reliable but faster)", default: 0.99, type: Float
39
+ end
40
+
41
+ many :hosts, "One or more hosts to benchmark"
42
+
43
+ def confidence_factor
44
+ 1.0 - @options[:confidence]
45
+ end
46
+
47
+ def measure_performance(concurrency, endpoint, request_path)
48
+ puts "I am running #{concurrency} asynchronous tasks that will each make sequential requests..."
49
+
50
+ statistics = Statistics.new(concurrency)
51
+ task = Async::Task.current
52
+
53
+ concurrency.times.map do
54
+ task.async do
55
+ client = Async::HTTP::Client.new(endpoint, endpoint.protocol)
56
+
57
+ statistics.sample(confidence_factor) do
58
+ response = client.get(request_path)
59
+ end
60
+
61
+ client.close
62
+ end
63
+ end.each(&:wait)
64
+
65
+ puts "I made #{statistics.count} requests in #{Seconds[statistics.sequential_duration]}. The per-request latency was #{Seconds[statistics.latency]}. That's #{statistics.per_second} asynchronous requests/second."
66
+ puts "\t Variance: #{Seconds[statistics.variance]}"
67
+ puts "\tStandard Deviation: #{Seconds[statistics.standard_deviation]}"
68
+ puts "\t Standard Error: #{statistics.standard_error}"
69
+
70
+ return statistics
71
+ end
72
+
73
+ def run(url)
74
+ endpoint = Async::HTTP::URLEndpoint.parse(url)
75
+ request_path = endpoint.url.request_uri
76
+
77
+ puts "I am going to benchmark #{url}..."
78
+
79
+ Async::Reactor.run do |task|
80
+ statistics = []
81
+ minimum = 1
82
+
83
+ base = measure_performance(minimum, endpoint, request_path)
84
+ statistics << base
85
+
86
+ current = 2
87
+ maximum = nil
88
+
89
+ while statistics.last.concurrency < current
90
+ results = measure_performance(current, endpoint, request_path)
91
+
92
+ if base.similar?(results, @options[:threshold])
93
+ statistics << results
94
+
95
+ minimum = current
96
+
97
+ if maximum
98
+ current += (maximum - current) / 2
99
+ else
100
+ current *= 2
101
+ end
102
+ else
103
+ # current concurrency is too big, so we limit maximum to it.
104
+ maximum = current
105
+
106
+ current = (minimum + (maximum - minimum) / 2).floor
107
+ end
108
+ end
109
+
110
+ puts "Your server can handle #{statistics.last.concurrency} concurrent requests."
111
+
112
+ puts "At this level of concurrency, requests have ~#{(statistics.last.latency / statistics.first.latency).round(2)}x higher latency."
113
+ end
114
+ end
115
+
116
+ def invoke(parent)
117
+ @hosts.each do |host|
118
+ run(host).wait
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,52 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Benchmark
22
+ module HTTP
23
+ class Seconds
24
+ UNITS = ["s", "ms", "µs"]
25
+ SCALE = UNITS.count - 1
26
+
27
+ def self.[](value)
28
+ self.new(value)
29
+ end
30
+
31
+ def initialize(value)
32
+ @value = value
33
+ end
34
+
35
+ def scale
36
+ Math.log(@value) / Math.log(1000)
37
+ end
38
+
39
+ def to_s
40
+ scaled_value = @value
41
+ scale = 0
42
+
43
+ while scaled_value < 1 && scale < SCALE
44
+ scaled_value *= 1000
45
+ scale += 1
46
+ end
47
+
48
+ return sprintf("%0.#{scale+1}f%s", scaled_value, UNITS.fetch(scale))
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,118 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Benchmark
22
+ module HTTP
23
+ class Statistics
24
+ def initialize(concurrency)
25
+ @samples = []
26
+ @duration = 0
27
+
28
+ @concurrency = concurrency
29
+ end
30
+
31
+ attr :samples
32
+ attr :duration
33
+
34
+ attr :concurrency
35
+
36
+ def sequential_duration
37
+ @duration / @concurrency
38
+ end
39
+
40
+ def count
41
+ @samples.count
42
+ end
43
+
44
+ def per_second
45
+ @samples.count.to_f / sequential_duration.to_f
46
+ end
47
+
48
+ def latency
49
+ @duration.to_f / @samples.count.to_f
50
+ end
51
+
52
+ def similar?(other, difference = 2.0)
53
+ ratio = other.latency / self.latency
54
+
55
+ return ratio < difference
56
+ end
57
+
58
+ def average
59
+ if @samples.any?
60
+ @samples.sum / @samples.count
61
+ end
62
+ end
63
+
64
+ # Computes Population Variance, σ^2.
65
+ def variance
66
+ return nil if @samples.count < 2
67
+
68
+ average = self.average
69
+
70
+ return @samples.map{|n| (n - average)**2}.sum / @samples.count
71
+ end
72
+
73
+ # Population Standard Deviation, σ
74
+ def standard_deviation
75
+ if variance = self.variance
76
+ Math.sqrt(variance.abs)
77
+ end
78
+ end
79
+
80
+ def standard_error
81
+ if standard_deviation = self.standard_deviation
82
+ standard_deviation / Math.sqrt(@samples.count)
83
+ end
84
+ end
85
+
86
+ def measure
87
+ start_time = Time.now
88
+
89
+ result = yield
90
+
91
+ duration = Time.now - start_time
92
+ @samples << duration
93
+ @duration += duration
94
+
95
+ return result
96
+ end
97
+
98
+ def sample(confidence_factor, &block)
99
+ # warmup
100
+ yield
101
+
102
+ begin
103
+ measure(&block)
104
+ end until confident?(confidence_factor)
105
+ end
106
+
107
+ def print(out = STDOUT)
108
+ out.puts "#{@samples.count} samples. #{1.0 / self.average} per second. S/D: #{standard_deviation}."
109
+ end
110
+
111
+ private
112
+
113
+ def confident?(factor)
114
+ (@samples.count > @concurrency) && self.standard_error < (self.average * factor)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,25 @@
1
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Benchmark
22
+ module HTTP
23
+ VERSION = "0.1.0"
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: benchmark-http
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: samovar
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.16'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description:
70
+ email:
71
+ - samuel.williams@oriontransfer.co.nz
72
+ executables:
73
+ - benchmark-http
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - README.md
82
+ - Rakefile
83
+ - benchmark-http.gemspec
84
+ - bin/benchmark-http
85
+ - lib/benchmark/http.rb
86
+ - lib/benchmark/http/command.rb
87
+ - lib/benchmark/http/command/concurrency.rb
88
+ - lib/benchmark/http/seconds.rb
89
+ - lib/benchmark/http/statistics.rb
90
+ - lib/benchmark/http/version.rb
91
+ homepage: https://github.com/socketry/benchmark-http
92
+ licenses: []
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.7.6
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: An asynchronous benchmark toolbox for modern HTTP servers.
114
+ test_files: []