graph-function 0.1.5 → 0.1.7
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 +4 -4
- data/README.md +41 -10
- data/examples/comparing_ints.gif +0 -0
- data/examples/comparing_ints.html +21 -21
- data/graph-function.gemspec +1 -1
- data/lib/graph/function/comparison.rb +35 -12
- data/lib/graph/function/ints_comparison.rb +7 -42
- data/lib/graph/function/plot_config.rb +1 -1
- data/lib/graph/function/reformat_string.rb +4 -0
- data/lib/graph/function/version.rb +1 -1
- data/lib/graph/function.rb +8 -3
- metadata +3 -3
- data/lib/graph/function/only.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8156ac336aa21fcec58bb48b1a126a56858ef0a
|
4
|
+
data.tar.gz: d3cac5ba48aa74d4454e93c0155713543052a33a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3eee5a95f3524c98b31e2e563ab2d59ce3c525ce7a2ca95e0d21a0cecc42259122bd2b23be6365c741f6534a636d6722b9bb716db56b66d5e7c3258d518bdfe
|
7
|
+
data.tar.gz: 8dd2d7be6cbbdaf401772dbbe293aa78a97952bf63da5ef69b1a382ded1f232a4d4e0b5143123b8adfcff1d68eec602c58eb327788be73ab5299586ba9ff0133
|
data/README.md
CHANGED
@@ -22,10 +22,6 @@ This gem's goal is to make it easy to compare the [asymptotic performance](https
|
|
22
22
|
|
23
23
|
When I work on katas and exercises I found I often wanted to compare my implementations. After doing so a half dozen times I noticed some patterns, and figured it'd be valuable to capture those into an easier API to work with. While working on a kata I like the immediacy of replotting back on x11, but because of gnuplot's structure it is just as easy to get images or html canvas graphs.
|
24
24
|
|
25
|
-
## Disclaimer
|
26
|
-
|
27
|
-
Because of the current implementation details: Ruby methods which operate on `self` **will not work**, and there is a negligible constant slow down on all functions tested by `Comparison` because of the use of `send(:func)`. The latter won't corrupt comparisons, but means you don't want to use this gem to benchmark functions individually **except** through `Graph::Function::Only`.
|
28
|
-
|
29
25
|
## Installation
|
30
26
|
|
31
27
|
Because this gem depends on `gnuplot` and `xquartz`, we need to follow their [prereq steps](https://github.com/rdp/ruby_gnuplot#pre-requisites-and-installation):
|
@@ -64,6 +60,10 @@ Graph::Function.as_gif
|
|
64
60
|
Graph::Function::IntsComparison.of(method(:sort), method(:bubble_sort))
|
65
61
|
```
|
66
62
|
|
63
|
+
Produces:
|
64
|
+
|
65
|
+

|
66
|
+
|
67
67
|
### Setup
|
68
68
|
|
69
69
|
To set up, you only need the following:
|
@@ -73,9 +73,11 @@ require 'graph/function'
|
|
73
73
|
Graph::Function.as_x11
|
74
74
|
```
|
75
75
|
|
76
|
-
If you don't want to output to [x11](https://www.xquartz.org/), just set `config.terminal` to a different option. Two convenience methods exist for `gif` and `canvas
|
76
|
+
If you don't want to output to [x11](https://www.xquartz.org/), just set `config.terminal` to a different option. Two convenience methods exist for `gif` and `canvas`:
|
77
77
|
|
78
78
|
```ruby
|
79
|
+
# by default file will be set to name of the executing file and dumped in its dir
|
80
|
+
# or you can set file yourself like so:
|
79
81
|
Graph::Function.as_gif(File.expand_path('../comparing_ints.gif', __FILE__))
|
80
82
|
Graph::Function.as_canvas(File.expand_path('../comparing_ints.html', __FILE__))
|
81
83
|
```
|
@@ -87,10 +89,13 @@ Graph::Function.configure do |config|
|
|
87
89
|
config.terminal = 'dumb'
|
88
90
|
config.output = File.expand_path('../your_graph_name.txt', __FILE__)
|
89
91
|
config.step = (0..10_000).step(1000).to_a # default value
|
92
|
+
config.trials = 1
|
90
93
|
end
|
91
94
|
```
|
92
95
|
|
93
|
-
In configuration, you can
|
96
|
+
In configuration, you can control the "step" size of `x` in the plot. Its default value is `(0..10_000).step(1000).to_a` (`[0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]`) but you can make it as fine or rough grained as you need up to any size.
|
97
|
+
|
98
|
+
You can also set a number of trials over which to average execution times.
|
94
99
|
|
95
100
|
### Graphing
|
96
101
|
|
@@ -104,9 +109,24 @@ Graph::Function::IntsComparison.of(c.method(:function_name_one), c.method(:funct
|
|
104
109
|
|
105
110
|

|
106
111
|
|
107
|
-
For more complex use cases, you'll be creating a `Graph::Function::Comparison`
|
112
|
+
For more complex use cases, you'll be creating a `Graph::Function::Comparison` with some generator of data, and executing `#of` with `Method` objects or `Proc`s that operate on the same parameter types<sup id="a1">[1](#f1)</sup>. (Note because `IntsComparison` *does not need a generator*, `.of` is a class method instead.)
|
113
|
+
|
114
|
+
### Generators
|
115
|
+
|
116
|
+
To generate values of the type needed by your function, you can write
|
117
|
+
a generator in Ruby or use the provided dependency
|
118
|
+
[Rantly](https://github.com/hayeah/rantly).
|
119
|
+
|
120
|
+
Here's an example of a simple Ruby generator, it's just a `Proc` parameterized on `size`:
|
108
121
|
|
109
|
-
|
122
|
+
```ruby
|
123
|
+
tiny_int_generator = proc {|size| Array.new(size) { rand(-9...9) } }
|
124
|
+
comparison = Graph::Function::Comparison.new(tiny_int_generator)
|
125
|
+
```
|
126
|
+
|
127
|
+
For Rantly usage, there's great documentation on generating many different kinds of data in
|
128
|
+
their documentation, but here's an example of comparing two functions that
|
129
|
+
take `Hash{String => Integer}`:
|
110
130
|
|
111
131
|
```ruby
|
112
132
|
# you must put it in a proc taking size so Graph::Function can increase it
|
@@ -124,8 +144,7 @@ If you want to make use of more "real" fake data, [Faker](https://github.com/sty
|
|
124
144
|
```ruby
|
125
145
|
# again, we need to parameterize our generator with size
|
126
146
|
faker_generator = proc {|size| Rantly(size) { call(Proc.new { Faker::Date.backward(14) }) }
|
127
|
-
|
128
|
-
graph = Graph::Function::Only.new(faker_generator)
|
147
|
+
graph = Graph::Function::Comparison.new(faker_generator)
|
129
148
|
graph.of(method(:custom_types))
|
130
149
|
# => will output an xquartz graph
|
131
150
|
```
|
@@ -136,6 +155,18 @@ The only downside here is that you can't parameterize `Faker`, but you could use
|
|
136
155
|
|
137
156
|
Check out the [spec file](spec/graph/function_spec.rb) to see all of these or see [examples](examples/).
|
138
157
|
|
158
|
+
### Functions that use `self`
|
159
|
+
|
160
|
+
For graphing functions that operate on `self`, such as `String#upcase`, you must provide a `Method` or `Proc` that wraps the method call. For instance:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
generator = proc {|size| Rantly { sized(size) { string } } }
|
164
|
+
# wrap the call to upcase
|
165
|
+
test_upcase = proc {|s| s.upcase }
|
166
|
+
graph = Graph::Function::Comparison.new(generator)
|
167
|
+
graph.of(test_upcase)
|
168
|
+
```
|
169
|
+
|
139
170
|
## Development
|
140
171
|
|
141
172
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/examples/comparing_ints.gif
CHANGED
Binary file
|
@@ -168,7 +168,7 @@ L(5759,301);
|
|
168
168
|
L(540,301);
|
169
169
|
ctx.closePath();
|
170
170
|
ctx.stroke();
|
171
|
-
TR(90,2041,270,10.0,"Center","execution time");
|
171
|
+
TR(90,2041,270,10.0,"Center","execution time (seconds)");
|
172
172
|
T(3149,3980,10.0,"Center","input size");
|
173
173
|
T(3149,201,10.0,"Center","Sort vs BubbleSort");
|
174
174
|
if (typeof(gnuplot.hide_gp_plot_1) == "undefined"|| !gnuplot.hide_gp_plot_1) {
|
@@ -223,29 +223,29 @@ ctx.beginPath();
|
|
223
223
|
M(5179,589);
|
224
224
|
L(5599,589);
|
225
225
|
M(540,3680);
|
226
|
-
L(1062,
|
227
|
-
L(1584,
|
228
|
-
L(2106,
|
229
|
-
L(2628,
|
230
|
-
L(3150,
|
231
|
-
L(3671,
|
232
|
-
L(4193,
|
233
|
-
L(4715,
|
234
|
-
L(5237,
|
235
|
-
L(5759,
|
226
|
+
L(1062,3648);
|
227
|
+
L(1584,3554);
|
228
|
+
L(2106,3390);
|
229
|
+
L(2628,3172);
|
230
|
+
L(3150,2885);
|
231
|
+
L(3671,2518);
|
232
|
+
L(4193,2108);
|
233
|
+
L(4715,1616);
|
234
|
+
L(5237,1078);
|
235
|
+
L(5759,439);
|
236
236
|
ctx.stroke();
|
237
237
|
ctx.closePath();
|
238
238
|
Pt(1,540,3680,60.0);
|
239
|
-
Pt(1,1062,
|
240
|
-
Pt(1,1584,
|
241
|
-
Pt(1,2106,
|
242
|
-
Pt(1,2628,
|
243
|
-
Pt(1,3150,
|
244
|
-
Pt(1,3671,
|
245
|
-
Pt(1,4193,
|
246
|
-
Pt(1,4715,
|
247
|
-
Pt(1,5237,
|
248
|
-
Pt(1,5759,
|
239
|
+
Pt(1,1062,3648,60.0);
|
240
|
+
Pt(1,1584,3554,60.0);
|
241
|
+
Pt(1,2106,3390,60.0);
|
242
|
+
Pt(1,2628,3172,60.0);
|
243
|
+
Pt(1,3150,2885,60.0);
|
244
|
+
Pt(1,3671,2518,60.0);
|
245
|
+
Pt(1,4193,2108,60.0);
|
246
|
+
Pt(1,4715,1616,60.0);
|
247
|
+
Pt(1,5237,1078,60.0);
|
248
|
+
Pt(1,5759,439,60.0);
|
249
249
|
Pt(1,5389,589,60.0);
|
250
250
|
} // End gp_plot_2
|
251
251
|
ctx.lineWidth = 2;
|
data/graph-function.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'graph/function/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'graph-function'
|
8
8
|
spec.version = Graph::Function::VERSION
|
9
|
-
spec.authors = ['Alex Moore-Niemi']
|
9
|
+
spec.authors = ['Alex Moore-Niemi', 'Cora Johnson-Roberson']
|
10
10
|
spec.email = ['moore.niemi@gmail.com']
|
11
11
|
|
12
12
|
spec.summary = %q{Graph function performance.}
|
@@ -9,38 +9,61 @@ module Graph
|
|
9
9
|
@data_generator = generator
|
10
10
|
end
|
11
11
|
|
12
|
-
def of(*
|
13
|
-
fail unless
|
12
|
+
def of(*functions)
|
13
|
+
fail unless functions.all? {|f| f.respond_to?(:call) }
|
14
14
|
|
15
|
-
|
16
|
-
self.class.send(:define_method, m.name, proc(&m))
|
17
|
-
end
|
15
|
+
results = {}
|
18
16
|
|
19
17
|
Gnuplot.open do |gp|
|
20
18
|
Gnuplot::Plot.new(gp) do |plot|
|
21
|
-
|
22
|
-
plot.title
|
23
|
-
|
19
|
+
title = functions_to_title(functions)
|
20
|
+
plot.title title
|
21
|
+
set_up(plot)
|
24
22
|
|
25
23
|
x = Graph::Function.configuration.step
|
24
|
+
trials = Graph::Function.configuration.trials
|
26
25
|
pb = ProgressBar.create(title: title, total: x.size)
|
27
26
|
|
28
|
-
|
27
|
+
functions.each do |f|
|
29
28
|
pb.reset
|
29
|
+
|
30
|
+
name = fname(f)
|
31
|
+
results[name] = {}
|
32
|
+
|
30
33
|
y = x.collect do |v|
|
31
34
|
pb.increment
|
32
35
|
data = data_generator.call(v)
|
33
|
-
|
34
|
-
|
36
|
+
current_trials = (1..trials).collect do |_|
|
37
|
+
Benchmark.measure { f.call(data) }.real
|
38
|
+
end
|
39
|
+
results[name][v] = current_trials
|
40
|
+
current_trials.reduce(0.0, :+) / trials
|
35
41
|
end
|
36
42
|
|
37
43
|
plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
|
38
44
|
ds.with = "linespoints"
|
39
|
-
ds.title = "#{escape_underscores(
|
45
|
+
ds.title = "#{escape_underscores(name)}"
|
40
46
|
end
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
50
|
+
results
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def fname(function)
|
55
|
+
function.respond_to?(:name) ? function.name : extract_filename(function.to_s)
|
56
|
+
end
|
57
|
+
|
58
|
+
def functions_to_title(functions)
|
59
|
+
case functions.size
|
60
|
+
when 1
|
61
|
+
"#{camel_title(fname(functions[0]))}"
|
62
|
+
when 2
|
63
|
+
"#{camel_title(fname(functions[0]))} vs #{camel_title(fname(functions[1]))}"
|
64
|
+
else
|
65
|
+
"#{functions.map {|f| camel_title(fname(f)) }.join(', ') }"
|
66
|
+
end
|
44
67
|
end
|
45
68
|
end
|
46
69
|
end
|
@@ -1,48 +1,13 @@
|
|
1
1
|
module Graph
|
2
2
|
module Function
|
3
|
-
class IntsComparison
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def self.of(method_one, method_two)
|
8
|
-
fail unless method_one.is_a?(Method) && method_two.is_a?(Method)
|
9
|
-
|
10
|
-
Gnuplot.open do |gp|
|
11
|
-
Gnuplot::Plot.new(gp) do |plot|
|
12
|
-
self.class.send(:define_method, :a, proc(&method_one))
|
13
|
-
self.class.send(:define_method, :b, proc(&method_two))
|
14
|
-
|
15
|
-
plot.title (title = "#{camel_title(method_one.name)} vs #{camel_title(method_two.name)}")
|
16
|
-
set_up(plot)
|
17
|
-
|
18
|
-
x = Graph::Function.configuration.step
|
19
|
-
pb = ProgressBar.create(title: title, total: x.size)
|
20
|
-
|
21
|
-
y = x.collect do |v|
|
22
|
-
pb.increment
|
23
|
-
array = (0..v - 1).to_a.shuffle
|
24
|
-
Benchmark.measure { a(array) }.real
|
25
|
-
end
|
26
|
-
|
27
|
-
plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
|
28
|
-
ds.with = "linespoints"
|
29
|
-
ds.title = "#{escape_underscores(method_one.name)}"
|
30
|
-
end
|
31
|
-
|
32
|
-
pb.reset
|
33
|
-
|
34
|
-
z = x.collect do |v|
|
35
|
-
pb.increment
|
36
|
-
array = (0..v - 1).to_a.shuffle
|
37
|
-
Benchmark.measure { b(array) }.real
|
38
|
-
end
|
3
|
+
class IntsComparison < Comparison
|
4
|
+
def initialize
|
5
|
+
@data_generator = Proc.new {|v| (-v/2 + 1 .. v/2).to_a.shuffle }
|
6
|
+
end
|
39
7
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
8
|
+
def self.of(*methods)
|
9
|
+
comparison = self.new
|
10
|
+
comparison.of(*methods)
|
46
11
|
end
|
47
12
|
end
|
48
13
|
end
|
@@ -2,7 +2,7 @@ module Graph
|
|
2
2
|
module Function
|
3
3
|
module PlotConfig
|
4
4
|
def set_up(plot)
|
5
|
-
plot.ylabel 'execution time (seconds)'
|
5
|
+
plot.ylabel (Graph::Function.configuration.trials == 1 ? 'execution time (seconds)' : 'average execution time (seconds)')
|
6
6
|
plot.xlabel 'input size'
|
7
7
|
plot.terminal (t = Graph::Function.configuration.terminal)
|
8
8
|
plot.output Graph::Function.configuration.output unless t == 'x11'
|
data/lib/graph/function.rb
CHANGED
@@ -7,7 +7,6 @@ require 'ruby-progressbar'
|
|
7
7
|
require 'graph/function/version'
|
8
8
|
require 'graph/function/reformat_string'
|
9
9
|
require 'graph/function/plot_config'
|
10
|
-
require 'graph/function/only'
|
11
10
|
require 'graph/function/comparison'
|
12
11
|
require 'graph/function/ints_comparison'
|
13
12
|
|
@@ -25,14 +24,18 @@ module Graph
|
|
25
24
|
|
26
25
|
singleton_class.send(:alias_method, :as_x11, :configure)
|
27
26
|
|
28
|
-
def self.
|
27
|
+
def self.file_location(ext)
|
28
|
+
File.new("#{$0.slice(0..-4)}.#{ext}", 'w').path
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.as_gif(file = file_location('gif'))
|
29
32
|
self.configure do |config|
|
30
33
|
config.terminal = 'gif'
|
31
34
|
config.output = file
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
35
|
-
def self.as_canvas(file =
|
38
|
+
def self.as_canvas(file = file_location('html'))
|
36
39
|
self.configure do |config|
|
37
40
|
config.terminal = 'canvas'
|
38
41
|
config.output = file
|
@@ -42,6 +45,7 @@ module Graph
|
|
42
45
|
class Configuration
|
43
46
|
attr_accessor :terminal, :output
|
44
47
|
attr_accessor :step
|
48
|
+
attr_accessor :trials
|
45
49
|
|
46
50
|
# defaults
|
47
51
|
# see https://github.com/rdp/ruby_gnuplot/blob/master/examples/output_image_file.rb
|
@@ -50,6 +54,7 @@ module Graph
|
|
50
54
|
@terminal = 'x11'
|
51
55
|
@output = '.'
|
52
56
|
@step = (0..10_000).step(1000).to_a
|
57
|
+
@trials = 1
|
53
58
|
end
|
54
59
|
end
|
55
60
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graph-function
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Moore-Niemi
|
8
|
+
- Cora Johnson-Roberson
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
12
|
+
date: 2016-10-04 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: gnuplot
|
@@ -162,7 +163,6 @@ files:
|
|
162
163
|
- lib/graph/function.rb
|
163
164
|
- lib/graph/function/comparison.rb
|
164
165
|
- lib/graph/function/ints_comparison.rb
|
165
|
-
- lib/graph/function/only.rb
|
166
166
|
- lib/graph/function/plot_config.rb
|
167
167
|
- lib/graph/function/reformat_string.rb
|
168
168
|
- lib/graph/function/version.rb
|
data/lib/graph/function/only.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module Graph
|
2
|
-
module Function
|
3
|
-
class Only
|
4
|
-
include ReformatString
|
5
|
-
include PlotConfig
|
6
|
-
attr_accessor :data_generator
|
7
|
-
|
8
|
-
def initialize(generator)
|
9
|
-
@data_generator = generator
|
10
|
-
end
|
11
|
-
|
12
|
-
def of(method_obj)
|
13
|
-
fail unless method_obj.is_a?(Method)
|
14
|
-
|
15
|
-
self.class.send(:define_method, :a, proc(&method_obj))
|
16
|
-
|
17
|
-
Gnuplot.open do |gp|
|
18
|
-
Gnuplot::Plot.new(gp) do |plot|
|
19
|
-
|
20
|
-
plot.title "#{title = camel_title(method_obj.name)}"
|
21
|
-
set_up(plot)
|
22
|
-
|
23
|
-
x = Graph::Function.configuration.step
|
24
|
-
pb = ProgressBar.create(title: title, total: x.size)
|
25
|
-
|
26
|
-
y = x.collect do |v|
|
27
|
-
pb.increment
|
28
|
-
data = data_generator.call(v)
|
29
|
-
Benchmark.measure { a(data) }.real
|
30
|
-
end
|
31
|
-
|
32
|
-
plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
|
33
|
-
ds.with = "linespoints"
|
34
|
-
ds.title = "#{escape_underscores(method_obj.name)}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|