fruity 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -98,8 +98,8 @@ In addition, all benchmarking tools require the user to either write their own i
98
98
 
99
99
  == Algorithm
100
100
 
101
- We first determine the number of inner iterations needed to get a meaningful clock measurement (see sufficient_magnitude).
102
- When timing an execution, we always compare to a baseline (the time taken by an empty loop of the same magnitude).
101
+ We first determine the number of inner iterations needed to get a meaningful clock measurement (see sufficient_magnification).
102
+ When timing an execution, we always compare to a baseline (the time taken by an empty loop of the same magnification).
103
103
  We call the different executables in succession, to minimize the impact on the order of execution.
104
104
  We calculate the error by taking into account the standard deviation of the time samples.
105
105
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "fruity"
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Marc-Andre Lafortune"]
12
+ s.date = "2012-02-08"
13
+ s.description = "Comparing apples with apples"
14
+ s.email = "github@marc-andre.ca"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".irbrc",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "fruity.gemspec",
28
+ "lib/fruity.rb",
29
+ "lib/fruity/base.rb",
30
+ "lib/fruity/baseline.rb",
31
+ "lib/fruity/comparison_run.rb",
32
+ "lib/fruity/group.rb",
33
+ "lib/fruity/named_block_collector.rb",
34
+ "lib/fruity/runner.rb",
35
+ "lib/fruity/util.rb",
36
+ "spec/baseline_spec.rb",
37
+ "spec/comparison_run_spec.rb",
38
+ "spec/find_thresold.rb",
39
+ "spec/fruity_spec.rb",
40
+ "spec/group_spec.rb",
41
+ "spec/manual_test.rb",
42
+ "spec/runner_spec.rb",
43
+ "spec/spec.opts",
44
+ "spec/spec_helper.rb",
45
+ "spec/util_spec.rb"
46
+ ]
47
+ s.homepage = "http://github.com/marcandre/fruity"
48
+ s.licenses = ["MIT"]
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = "1.8.10"
51
+ s.summary = "Cool performance comparison tool"
52
+
53
+ if s.respond_to? :specification_version then
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
58
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
59
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
60
+ else
61
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
62
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
63
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
67
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
68
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
69
+ end
70
+ end
71
+
@@ -12,12 +12,12 @@ module Fruity
12
12
  :on => GLOBAL_SCOPE,
13
13
  :samples => 20,
14
14
  :disable_gc => false,
15
- :filter => [0, 0.2],
16
- :baseline => :split,
15
+ :filter => [0, 0.2], # Proportion of samples to discard [lower_end, upper_end]
16
+ :baseline => :single, # Either :none, :single or :split
17
17
  }
18
18
 
19
19
  OTHER_OPTIONS = [
20
- :magnitude,
20
+ :magnify,
21
21
  :args,
22
22
  :self,
23
23
  :verbose,
@@ -37,19 +37,19 @@ module Fruity
37
37
  compare_block(block) if block
38
38
  end
39
39
 
40
- # Returns the maximal sufficient_magnitude for all elements
41
- # See Util.sufficient_magnitude
40
+ # Returns the maximal sufficient_magnification for all elements
41
+ # See Util.sufficient_magnification
42
42
  #
43
- def sufficient_magnitude
44
- elements.map{|name, exec| Util.sufficient_magnitude(exec, options) }.max
43
+ def sufficient_magnification
44
+ elements.map{|name, exec| Util.sufficient_magnification(exec, options) }.max
45
45
  end
46
46
 
47
- # Returns the maximal sufficient_magnitude for all elements
47
+ # Returns the maximal sufficient_magnification for all elements
48
48
  # and the approximate delay taken for the whole group
49
- # See Util.sufficient_magnitude
49
+ # See Util.sufficient_magnification
50
50
  #
51
- def sufficient_magnitude_and_delay
52
- mags_and_delays = elements.map{|name, exec| Util.sufficient_magnitude_and_delay(exec, options) }
51
+ def sufficient_magnification_and_delay
52
+ mags_and_delays = elements.map{|name, exec| Util.sufficient_magnification_and_delay(exec, options) }
53
53
  mag = mags_and_delays.map(&:first).max
54
54
  delay = mags_and_delays.map{|m, d| d * mag / m}.inject(:+)
55
55
  [mag, delay]
@@ -9,7 +9,7 @@ module Fruity
9
9
  end
10
10
 
11
11
  def feedback
12
- mess = "Running each test " << (options[:magnitude] == 1 ? "once." : "#{options[:magnitude]} times.")
12
+ mess = "Running each test " << (options[:magnify] == 1 ? "once." : "#{options[:magnify]} times.")
13
13
  if d = delay
14
14
  if d > 60
15
15
  d = (d / 60).round
@@ -23,8 +23,8 @@ module Fruity
23
23
  private
24
24
  def prepare(opt)
25
25
  @options = group.options.merge(opt)
26
- unless options[:magnitude]
27
- options[:magnitude], @delay = group.sufficient_magnitude_and_delay
26
+ unless options[:magnify]
27
+ options[:magnify], @delay = group.sufficient_magnification_and_delay
28
28
  @delay *= options.fetch(:samples)
29
29
  end
30
30
  end
@@ -38,30 +38,30 @@ module Fruity
38
38
  # due in big part to the imprecision of the measurement itself
39
39
  # or the inner loop itself.
40
40
  #
41
- def sufficient_magnitude(exec, options = {})
42
- mag, delay = sufficient_magnitude_and_delay(exec, options)
41
+ def sufficient_magnification(exec, options = {})
42
+ mag, delay = sufficient_magnification_and_delay(exec, options)
43
43
  mag
44
44
  end
45
45
 
46
46
  BASELINE_THRESHOLD = 1.02 # Ratio between two identical baselines is typically < 1.02, while {2+2} compared to baseline is typically > 1.02
47
47
 
48
- def sufficient_magnitude_and_delay(exec, options = {})
48
+ def sufficient_magnification_and_delay(exec, options = {})
49
49
  power = 0
50
50
  min_desired_delta = clock_precision * MEASUREMENTS_BY_PROPER_TIME / PROPER_TIME_RELATIVE_ERROR
51
51
  # First, make a gross approximation with a single sample and no baseline
52
52
  min_approx_delay = min_desired_delta / (1 << APPROX_POWER)
53
- while (delay = real_time(exec, options.merge(:magnitude => 1 << power))) < min_approx_delay
53
+ while (delay = real_time(exec, options.merge(:magnify => 1 << power))) < min_approx_delay
54
54
  power += [Math.log(min_approx_delay.div(delay + clock_precision), 2), 1].max.floor
55
55
  end
56
56
 
57
57
  # Then take a couple of samples, along with a baseline
58
58
  power += 1 unless delay > 2 * min_approx_delay
59
- group = Group.new(exec, Baseline[exec], options.merge(:baseline => :none, :samples => 5, :filter => [0, 0.25], :magnitude => 1 << power))
59
+ group = Group.new(exec, Baseline[exec], options.merge(:baseline => :none, :samples => 5, :filter => [0, 0.25], :magnify => 1 << power))
60
60
  stats = group.run.stats
61
61
  if stats[0][:mean] / stats[1][:mean] < 2
62
62
  # Quite close to baseline, which means we need to be more discriminant
63
63
  power += APPROX_POWER
64
- stats = group.run(:samples => 40, :magnitude => 1 << power).stats
64
+ stats = group.run(:samples => 40, :magnify => 1 << power).stats
65
65
  raise "Given callable can not be reasonably distinguished from an empty block" if stats[0][:mean] / stats[1][:mean] < BASELINE_THRESHOLD
66
66
  end
67
67
  delta = stats[0][:mean] - stats[1][:mean]
@@ -73,25 +73,25 @@ module Fruity
73
73
  end
74
74
 
75
75
  # The proper time is the real time taken by calling +exec+
76
- # number of times given by +options[:magnitude]+ minus
76
+ # number of times given by +options[:magnify]+ minus
77
77
  # the real time for calling an empty executable instead.
78
78
  #
79
- # If +options[:magnitude]+ is not given, it will be calculated to be meaningful.
79
+ # If +options[:magnify]+ is not given, it will be calculated to be meaningful.
80
80
  #
81
81
  def proper_time(exec, options = {})
82
- unless options.has_key?(:magnitude)
83
- options = {:magnitude => sufficient_magnitude(exec, options)}.merge(options)
82
+ unless options.has_key?(:magnify)
83
+ options = {:magnify => sufficient_magnification(exec, options)}.merge(options)
84
84
  end
85
85
  real_time(exec, options) - real_time(Baseline[exec], options)
86
86
  end
87
87
 
88
88
  # Returns the real time taken by calling +exec+
89
- # number of times given by +options[:magnitude]+
89
+ # number of times given by +options[:magnify]+
90
90
  #
91
91
  def real_time(exec, options = {})
92
92
  GC.start
93
93
  GC.disable if options[:disable_gc]
94
- n = options.fetch(:magnitude)
94
+ n = options.fetch(:magnify)
95
95
  if options.has_key?(:self)
96
96
  new_self = options[:self]
97
97
  if args = options[:args] and args.size > 0
@@ -7,7 +7,7 @@ min = 1
7
7
  max = 2
8
8
  s = 10
9
9
  n.times.map do
10
- group = Fruity::Group.new(*[->{}] * s, *[->{ 2 + 2 }] * s, :baseline => :none, :samples => 20, :magnitude => 1 << power)
10
+ group = Fruity::Group.new(*[->{}] * s, *[->{ 2 + 2 }] * s, :baseline => :none, :samples => 20, :magnify => 1 << power)
11
11
  means = group.run.stats.map{|s| s[:mean]}
12
12
  noops = means.first(s).sort
13
13
  plus = means.last(s).sort
@@ -6,19 +6,19 @@ module Fruity
6
6
  let(:runner){ Runner.new(group) }
7
7
 
8
8
  it "runs from a Group" do
9
- run = runner.run(:samples => 42, :magnitude => 100)
9
+ run = runner.run(:samples => 42, :magnify => 100)
10
10
  run.timings.should be_array_of_size(2, 42)
11
11
  run.baselines.should be_array_of_size(2, 42)
12
12
  end
13
13
 
14
14
  it "can use a single baseline" do
15
- run = runner.run(:samples => 42, :magnitude => 100, :baseline => :single)
15
+ run = runner.run(:samples => 42, :magnify => 100, :baseline => :single)
16
16
  run.timings.should be_array_of_size(2, 42)
17
17
  run.baselines.should be_array_of_size(42)
18
18
  end
19
19
 
20
20
  it "can use no baseline" do
21
- run = runner.run(:samples => 42, :magnitude => 100, :baseline => :none)
21
+ run = runner.run(:samples => 42, :magnify => 100, :baseline => :none)
22
22
  run.timings.should be_array_of_size(2, 42)
23
23
  run.baselines.should == nil
24
24
  end
@@ -6,18 +6,18 @@ module Fruity
6
6
 
7
7
  its(:proper_time_precision) { should == 4.0e-06 }
8
8
 
9
- describe :sufficient_magnitude do
9
+ describe :sufficient_magnification do
10
10
  it "returns a big value for a quick (but not trivial)" do
11
- Util.sufficient_magnitude(->{ 2.nil? }).should > 10000
11
+ Util.sufficient_magnification(->{ 2 + 2 }).should > 10000
12
12
  end
13
13
 
14
14
  it "return 1 for a sufficiently slow block" do
15
- Util.sufficient_magnitude(->{sleep(0.01)}).should == 1
15
+ Util.sufficient_magnification(->{sleep(0.01)}).should == 1
16
16
  end
17
17
 
18
18
  it "should raise an error for a trivial block" do
19
19
  ->{
20
- Util.sufficient_magnitude(Proc.new{})
20
+ Util.sufficient_magnification(->{})
21
21
  }.should raise_error
22
22
  end
23
23
  end
@@ -46,7 +46,7 @@ module Fruity
46
46
 
47
47
  it "gives similar results when comparing an exec and its baseline from stats on proper_time" do
48
48
  exec = ->{ 2 ** 3 ** 4 }
49
- options = {:magnitude => Util.sufficient_magnitude(exec) }
49
+ options = {:magnify => Util.sufficient_magnification(exec) }
50
50
  n = 100
51
51
  timings = [exec, ->{}].map do |e|
52
52
  n.times.map { Util.real_time(e, options) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fruity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-02-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &2170028460 !ruby/object:Gem::Requirement
16
+ requirement: &2158896760 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 2.3.0
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2170028460
24
+ version_requirements: *2158896760
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bundler
27
- requirement: &2170027780 !ruby/object:Gem::Requirement
27
+ requirement: &2158895780 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.0.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2170027780
35
+ version_requirements: *2158895780
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: jeweler
38
- requirement: &2170047220 !ruby/object:Gem::Requirement
38
+ requirement: &2158895160 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 1.6.4
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2170047220
46
+ version_requirements: *2158895160
47
47
  description: Comparing apples with apples
48
48
  email: github@marc-andre.ca
49
49
  executables: []
@@ -59,6 +59,7 @@ files:
59
59
  - README.rdoc
60
60
  - Rakefile
61
61
  - VERSION
62
+ - fruity.gemspec
62
63
  - lib/fruity.rb
63
64
  - lib/fruity/base.rb
64
65
  - lib/fruity/baseline.rb
@@ -92,7 +93,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
93
  version: '0'
93
94
  segments:
94
95
  - 0
95
- hash: -3031735817276906554
96
+ hash: 269285252031009011
96
97
  required_rubygems_version: !ruby/object:Gem::Requirement
97
98
  none: false
98
99
  requirements: