Module: BigBench::PostProcessor::Environment

Included in:
Context, Appearings, AttributeCluster, Cluster, NormalDistribution, Statistics
Defined in:
lib/bigbench/post_processor/environment.rb

Overview

The environment in which the post processors are evaluated. Every method defined here is available in the post_process block and run! methods of the predefined post processors

Defined Under Namespace

Classes: Appearings, AttributeCluster, BenchmarkNotFound, Cluster, NormalDistribution, PolynomialRegression, Statistics

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.reset!Object

Resets the whole post processor environment



9
10
11
12
13
# File 'lib/bigbench/post_processor/environment.rb', line 9

def self.reset!
  @@clusters, @@statistics, @@normal_distribution, @@regressions, @@appearings = {}, {}, {}, {}, {}
  @@trackings = []
  @@scope = :all
end

Instance Method Details

#appearing(timebase = 1.second) ⇒ Object

Returns an array of appearing attributes in the selected tracking scope.

appearing.statuses  # => [200, 404]
appearing.methods   # => ["get", "post"]
appearing.paths     # => ["/", "/basic/auth"


143
144
145
146
147
# File 'lib/bigbench/post_processor/environment.rb', line 143

def appearing(timebase = 1.second)
  return @@appearings[scope] unless @@appearings[scope].nil?
  
  @@appearings[scope] ||= Appearings.new(scope)
end

#cluster(timebase = 1.second, extra_scope = nil) ⇒ Object

Returns a clustered overview of all trackings. By default the trackings are clustered by second, but you can also specify any ammount of seconds to group together. A cluster then has the following methods:

# Duration was 120 seconds
cluster.timesteps           # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,..., 120]                                  (seconds)
cluster.durations           # => [50.3, 51.2, 40.3, 51.3, 50.3, 55.3, 52.3, 50.3, 51.3, 50.3, 54.3,..., 50.3] (average duration in milliseconds)
cluster.requests            # => [580, 569, 540, 524, 524, 525, 528, 520, 529, 527, 523,..., 524]             (requests in that second)
cluster.methods(:get)       # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (GET requests in that second)
cluster.methods(:post)      # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (POST requests in that second)
cluster.statuses(200)       # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (successful - requests in that second)
cluster.statuses(404)       # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (not founds - requests in that second)
cluster.paths("/")          # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (requests to a path in that second)
cluster.paths("/home")      # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (requests to "/home" path in that second)
cluster.benchmark("index")  # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (requests from the index benchmark in that second)
cluster.benchmark("user")   # => [400, 509, 340, 424, 324, 525, 528, 520, 529, 527, 523,..., 524]             (requests from the user benchmark in that second)

# Duration was 120 seconds = 2 minutes
cluster(1.minute).timesteps   # => [0, 1]         (minutes)
cluster(1.minute).durations   # => [50.3, 51.2]   (average duration in milliseconds)
cluster(1.minute).requests    # => [27836, 27684] (requests in that minute)


129
130
131
132
133
134
135
# File 'lib/bigbench/post_processor/environment.rb', line 129

def cluster(timebase = 1.second, extra_scope = nil)
  cluster_scope = extra_scope || scope
  timebase_and_scope = [timebase.to_i, cluster_scope]
  
  return @@clusters[timebase_and_scope] unless @@clusters[timebase_and_scope].nil?
  @@clusters[timebase_and_scope] = Cluster.new(timebase, cluster_scope)
end

#each_benchmarkObject

Iterates over all benchmarks and automatically executes all methods in the benchmark scope like this:

# For all benchmarks
cluster.durations
cluster.requests

# For each benchmark
each_benchmark do |benchmark|
  cluster.durations
  cluster.requests
end


100
101
102
103
104
105
106
# File 'lib/bigbench/post_processor/environment.rb', line 100

def each_benchmark
  BigBench.benchmarks.each do |benchmark|
    scope_to_benchmark(benchmark.name) do
      yield benchmark
    end
  end
end

#each_trackingObject

Iterates through every tracking and returns a tracking hash of the following form:

{
  :elapsed    => 2.502132,
  :start      => 1333986292.1755981,
  :stop       => 1333986293.618884,
  :duration   => 1443,
  :benchmark  => "index page",
  :url        => "http://www.google.de/",
  :path       => "/",
  :method     => "get",
  :status     => 200
}


54
55
56
57
58
# File 'lib/bigbench/post_processor/environment.rb', line 54

def each_tracking
  File.open(BigBench.config.output, "r+") do |file|
    file.each_line { |line| yield JSON.parse(line).inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} unless line.blank? }
  end
end

#normal_distribution(timebase = 1.second) ⇒ Object

Returns a gaussian distribution for the specified attribute. It automatically calculates the neccessary mean and variance values and adapts the x values to fit the bell curve best.

normal_distribution.durations.x         # => [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ...]
normal_distribution.durations.y         # => [0, 0, 7, 8, 9, 10, 11, 10, 9, 8, 7, 4, ...]


186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/bigbench/post_processor/environment.rb', line 186

def normal_distribution(timebase = 1.second)
  timebase_and_scope = [timebase.to_i, scope]
  return @@normal_distribution[timebase_and_scope] unless @@normal_distribution[timebase_and_scope].nil?
  
  @@normal_distribution[timebase_and_scope] ||= AttributeCluster.new(NormalDistribution, :timebase => timebase, :scope => scope)
  
  # Duration is the only value that shouldn't be clustered in the statistics because we have real float values in it, and do not only count +1
  # for the request. Because of this, we'll exchange the clustered durations y values with an array of all unclustered tracking durations
  @@normal_distribution[timebase_and_scope].instance_eval do
    @durations.instance_eval do
      @y = trackings.map{ |tracking| tracking[:duration] if tracking[:benchmark] == scope or scope == :all }.compact
    end
  end
  
  @@normal_distribution[timebase_and_scope]
end

#polynomial_regression(new_options = {}) ⇒ Object

Returns a polynomial regression of a degree, a derivation and a timebase. Possible options are:

[:degree]       By default the degree is 1 which results in a linear regression. There's no limit to the degree.
[:derivation]   By default the normal function, which means no derivation is returned. Currently only the first derivation is supported.
[:timebase]     By default the cluster size is 1.second. Any timelimit can be added here, e.g. 1.minute

# Return a linear regression for the durations, clustered by seconds
polynomial_regression.durations.y
polynomial_regression(:degree => 1, :timebase => 1.second).durations.y
polynomial_regression(:degree => 1, :timebase => 1.second).durations.derivation(0)

# Return the first derivation of the linear regression for the durations, clustered by seconds
polynomial_regression(:degree => 1).durations.derivation(1)
polynomial_regression(:degree => 1, :timebase => 1.second).durations.derivation(1)

# Return a second degree polynomial regression for the durations, clustered by seconds
polynomial_regression(:degree => 2).durations.derivation(0)
polynomial_regression(:degree => 2, :timebase => 1.second).durations.derivation(0)

# Return the first derivation of the second degree polynomial regression for the durations, clustered by seconds
polynomial_regression(:degree => 2).durations.derivation(0)
polynomial_regression(:degree => 2, :timebase => 1.second).durations.derivation(0)


227
228
229
230
231
232
233
234
# File 'lib/bigbench/post_processor/environment.rb', line 227

def polynomial_regression(new_options = {})
  options = { :degree => 1, :timebase => 1.second }.merge(new_options)
  degree_and_timebase_and_scope = [options[:degree], options[:timebase].to_i, scope]
  
  return @@regressions[degree_and_timebase_and_scope] unless @@regressions[degree_and_timebase_and_scope].nil?
  
  @@regressions[degree_and_timebase_and_scope] ||= AttributeCluster.new(PolynomialRegression, :timebase => options[:timebase], :scope => scope, :degree => options[:degree])
end

#scopeObject

Returns the current scope the environment works in



69
70
71
# File 'lib/bigbench/post_processor/environment.rb', line 69

def scope
  @@scope
end

#scope_to_benchmark(name) ⇒ Object

Executes the including methods in the scope of the benchmark:

# For the "index page" benchmark
scope_to_benchmark "index page" do
  cluster.durations
  cluster.requests
end

Raises:



81
82
83
84
85
86
# File 'lib/bigbench/post_processor/environment.rb', line 81

def scope_to_benchmark name
  raise BenchmarkNotFound.new(name) unless BigBench.benchmarks.map{|b| b.name }.include?(name)
  @@scope = name
  yield
  @@scope = nil
end

#statistics(timebase = 1.second) ⇒ Object

Returns the default statistics for a given attribute. The following statistics are available:

statistics.durations.max                  # => 78.2
statistics.durations.min                  # => 12.3

statistics.durations.mean                 # => 45.2
statistics.durations.average              # => 45.2

statistics.durations.standard_deviation   # => 11.3
statistics.durations.sd                   # => 11.3

statistics.durations.squared_deviation    # => 60.7
statistics.durations.variance             # => 60.7


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/bigbench/post_processor/environment.rb', line 163

def statistics(timebase = 1.second)
  timebase_and_scope = [timebase.to_i, scope]
  return @@statistics[timebase_and_scope] unless @@statistics[timebase_and_scope].nil?
  
  @@statistics[timebase_and_scope] ||= AttributeCluster.new(Statistics, :timebase => timebase, :scope => scope)
  
  # Duration is the only value that shouldn't be clustered in the statistics because we have real float values in it, and do not only count +1
  # for the request. Because of this, we'll exchange the clustered durations y values with an array of all unclustered tracking durations
  @@statistics[timebase_and_scope].instance_eval do
    @durations.instance_eval do
      @y = trackings.map{ |tracking| tracking[:duration] if tracking[:benchmark] == scope or scope == :all }.compact
    end
  end
  
  @@statistics[timebase_and_scope]
end

#trackingsObject

Puts all tracking hashes into a huge array. Warning, this method call might take quite long! The results are cached, so you can call trackings in the future without any pain



62
63
64
65
66
# File 'lib/bigbench/post_processor/environment.rb', line 62

def trackings
  return @@trackings unless @@trackings.empty?
  each_tracking{ |tracking| @@trackings << tracking }
  @@trackings
end