graph-function 0.1.5 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 421167ce11bda065293d3e2d411366c005f6dac8
4
- data.tar.gz: 611fa94c29782e68e69a9c40e643caa7ab4fb9be
3
+ metadata.gz: b8156ac336aa21fcec58bb48b1a126a56858ef0a
4
+ data.tar.gz: d3cac5ba48aa74d4454e93c0155713543052a33a
5
5
  SHA512:
6
- metadata.gz: ee22e9f5c505376d33b136582a4afd7ab3a5f0d8a2902a80e18372fcf8f82623f8a11648b8642dad3b0cdaceaa1c1ee2c5160e16394c03a1946ae5f275dd3eb1
7
- data.tar.gz: daa1821d691bc300454e8e683f0f2667f2392938db475ac2a6b6057383e98870a8d572b58010c6267fab0da94b2780993cab1f59a610601cd8c7929375bde4a4
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
+ ![comparing ints](examples/comparing_ints.gif)
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`, you just need to provide them with a file to output to:
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 also 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.
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
  ![comparison](spec/graph/two_func.gif)
106
111
 
107
- For more complex use cases, you'll be creating a `Graph::Function::Comparison` (or `Graph::Function::Only` if you want to graph a single function) with some generator of data, and executing `#of` with `Method` objects 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.)
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
- To generate values of the type needed by your function, use the provided dependency [Rantly](https://github.com/hayeah/rantly). There's great documentation on generating many different kinds of data in their documentation, but here's an example of comparing two functions that take `Hash{String => Integer}`:
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
- # using Only here, but anything that takes a generator can take one with Faker
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.
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,3650);
227
- L(1584,3558);
228
- L(2106,3406);
229
- L(2628,3187);
230
- L(3150,2903);
231
- L(3671,2555);
232
- L(4193,2170);
233
- L(4715,1722);
234
- L(5237,1168);
235
- L(5759,453);
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,3650,60.0);
240
- Pt(1,1584,3558,60.0);
241
- Pt(1,2106,3406,60.0);
242
- Pt(1,2628,3187,60.0);
243
- Pt(1,3150,2903,60.0);
244
- Pt(1,3671,2555,60.0);
245
- Pt(1,4193,2170,60.0);
246
- Pt(1,4715,1722,60.0);
247
- Pt(1,5237,1168,60.0);
248
- Pt(1,5759,453,60.0);
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;
@@ -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(*methods)
13
- fail unless methods.all? {|m| m.is_a?(Method) }
12
+ def of(*functions)
13
+ fail unless functions.all? {|f| f.respond_to?(:call) }
14
14
 
15
- methods.each do |m|
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 "#{title = methods.map {|m| camel_title(m.name) }.join(', ') }"
23
- set_up(plot)
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
- methods.each do |m|
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
- # FIXME can i get ride of the cost of `send`?
34
- Benchmark.measure { self.send(m.name, data) }.real
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(m.name)}"
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
- extend ReformatString
5
- extend PlotConfig
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
- plot.data << Gnuplot::DataSet.new( [x, z] ) do |ds|
41
- ds.with = "linespoints"
42
- ds.title = "#{escape_underscores(method_two.name)}"
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'
@@ -7,6 +7,10 @@ module Graph
7
7
  def camel_title(s)
8
8
  s.to_s.split('_').collect(&:capitalize).join
9
9
  end
10
+ def extract_filename(s)
11
+ matches = /([^\/]+:\d+)\>$/.match(s)
12
+ !matches.nil? ? matches[1] : s
13
+ end
10
14
  end
11
15
  end
12
16
  end
@@ -1,5 +1,5 @@
1
1
  module Graph
2
2
  module Function
3
- VERSION = '0.1.5'
3
+ VERSION = '0.1.7'
4
4
  end
5
5
  end
@@ -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.as_gif(file = File.expand_path("../#{$0.slice(0..-4)}.gif", __FILE__))
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 = File.expand_path("../#{$0.slice(0..-4)}.html", __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.5
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-02 00:00:00.000000000 Z
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
@@ -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