Class: Bullshit::Clock
- Inherits:
-
Object
- Object
- Bullshit::Clock
- Defined in:
- lib/bullshit.rb
Overview
A Clock instance is used to take measurements while benchmarking.
Constant Summary collapse
- TIMES =
[ :real, :total, :user, :system ]
- ALL_COLUMNS =
[ :scatter ] + TIMES + [ :repeat ]
- TIMES_MAX =
TIMES.map { |t| t.to_s.size }.max
Instance Attribute Summary collapse
-
#bc_method ⇒ Object
readonly
Returns the benchmark method for this Clock instance.
-
#repeat ⇒ Object
Number of repetitions this clock has measured.
-
#slopes ⇒ Object
readonly
Return all the slopes of linear regressions computed during data truncation phase.
-
#time ⇒ Object
readonly
Last time object used for real time measurement.
Class Method Summary collapse
-
.repeat(bc_method) ⇒ Object
Use a Clock instance to measure the time necessary to do bc_method.case.iterations repetitions of
bc_method
. -
.scale_range(bc_method) ⇒ Object
Iterate over the
range
of the RangeCase instance of thisbc_method
and take measurements (including scattering). -
.time(bc_method) ⇒ Object
Use a Clock instance to measure how many repetitions of
bc_method
can be done in bc_method.case.duration seconds (a float value). -
.times ⇒ Object
The times which should be displayed in the output.
-
.to_a ⇒ Object
Return column names in relation to Clock#to_a method.
Instance Method Summary collapse
-
#<<(times) ⇒ Object
Add the array
times
to this clock’s time measurements. -
#analysis ⇒ Object
Returns a Hash of Analysis object for all of TIMES’s time keys.
-
#arithmetic_mean(time) ⇒ Object
(also: #mean)
Returns the arithmetic mean of
time
. -
#autocorrelation(time) ⇒ Object
Return the array of autocorrelation values for
time
. -
#autocorrelation_plot(time) ⇒ Object
Returns the arrays for the autocorrelation plot, the first array for the numbers of lag measured, the second for the autocorrelation value.
-
#call_time_mean ⇒ Object
Seconds per call (mean).
-
#call_time_median ⇒ Object
Seconds per call (median).
-
#calls(call_time_type) ⇒ Object
Calls per second of the
call_time_type
, e. -
#calls_mean ⇒ Object
Calls per second (mean).
-
#calls_median ⇒ Object
Calls per second (median).
-
#case ⇒ Object
The benchmark case class this clock belongs to (via bc_method).
-
#cover?(other) ⇒ Boolean
Return true, if other’s mean value is indistinguishable from this object’s mean after filtering out the noise from the measurements with a Welch’s t-Test.
-
#detect_autocorrelation(time) ⇒ Object
Returns the q value for the Ljung-Box statistic of this
time
‘s analysis.detect_autocorrelation method. -
#detect_outliers(time) ⇒ Object
Return a result hash with the number of :very_low, :low, :high, and :very_high outliers, determined by the box plotting algorithm run with :median and :iqr parameters.
-
#file_path(*args) ⇒ Object
Return the result of CaseMethod#file_path for this clock’s bc_method.
-
#find_truncation_offset ⇒ Object
Find an offset from the start of the measurements in this clock to truncate the initial data until a stable state has been reached and return it as an integer.
-
#geometric_mean(time) ⇒ Object
Returns the geometric mean of
time
. -
#harmonic_mean(time) ⇒ Object
Returns the harmonic mean of
time
. -
#histogram(time) ⇒ Object
Return the Histogram for the
time
values. -
#inc_scatter ⇒ Object
Increment scatter counter by one.
-
#initialize(bc_method) ⇒ Clock
constructor
Returns a Clock instance for CaseMethod instance
bc_method
. -
#max(time) ⇒ Object
Returns the maximum for the
time
(one of TIMES’ symbols). -
#measure ⇒ Object
Take a single measurement.
-
#median(time) ⇒ Object
Returns the median of the
time
values (one of TIMES’ symbols). -
#min(time) ⇒ Object
Returns the minimum for the
time
(one of TIMES’ symbols). -
#percentile(time, p = 50) ⇒ Object
Returns the
p
-percentile of thetime
values (one of TIMES’ symbols). -
#sample_standard_deviation(time) ⇒ Object
Returns the sample standard deviation for the
time
(one of TIMES’ symbols). -
#sample_standard_deviation_percentage(time) ⇒ Object
Returns the sample standard deviation for the
time
(one of TIMES’ symbols) in percentage of its arithmetic mean. -
#take_time ⇒ Object
Takes the times an returns an array, consisting of the times in the order of enumerated in the TIMES constant.
-
#to_a ⇒ Object
Returns the measurements as an array of arrays.
-
#truncate_data(offset) ⇒ Object
Truncate the measurements stored in this clock starting from the integer
offset
.
Constructor Details
#initialize(bc_method) ⇒ Clock
Returns a Clock instance for CaseMethod instance bc_method
.
242 243 244 245 246 247 |
# File 'lib/bullshit.rb', line 242 def initialize(bc_method) @bc_method = bc_method @times = Hash.new { |h, k| h[k] = [] } @repeat = 0 @scatter = 0 end |
Instance Attribute Details
#bc_method ⇒ Object (readonly)
Returns the benchmark method for this Clock instance.
324 325 326 |
# File 'lib/bullshit.rb', line 324 def bc_method @bc_method end |
#repeat ⇒ Object
Number of repetitions this clock has measured.
327 328 329 |
# File 'lib/bullshit.rb', line 327 def repeat @repeat end |
#slopes ⇒ Object (readonly)
Return all the slopes of linear regressions computed during data truncation phase.
334 335 336 |
# File 'lib/bullshit.rb', line 334 def slopes @slopes end |
#time ⇒ Object (readonly)
Last time object used for real time measurement.
330 331 332 |
# File 'lib/bullshit.rb', line 330 def time @time end |
Class Method Details
.repeat(bc_method) ⇒ Object
Use a Clock instance to measure the time necessary to do bc_method.case.iterations repetitions of bc_method
.
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/bullshit.rb', line 251 def self.repeat(bc_method) clock = new(bc_method) bs = clock.case.batch_size.abs bs = 1 if !bs or bs < 0 clock.case.iterations.times do bc_method.before_run $DEBUG and warn "Calling #{bc_method.name}." clock.inc_scatter clock.measure do bs.times { yield } end bc_method.after_run end clock end |
.scale_range(bc_method) ⇒ Object
Iterate over the range
of the RangeCase instance of this bc_method
and take measurements (including scattering).
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/bullshit.rb', line 294 def self.scale_range(bc_method) clock = new(bc_method) my_case = clock.case bs = my_case.batch_size.abs bs = 1 if !bs or bs < 0 for a in my_case.range begin my_case.args = (a.dup rescue a).freeze clock.inc_scatter my_case.scatter.times do bc_method.before_run $DEBUG and warn "Calling #{bc_method.name}." clock.measure do bs.times { yield } end bc_method.after_run end ensure my_case.args = nil end end clock end |
.time(bc_method) ⇒ Object
Use a Clock instance to measure how many repetitions of bc_method
can be done in bc_method.case.duration seconds (a float value). If the bc_method.case.batch_size is >1 duration is multiplied by batch_size because the measured times are averaged by batch_size.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/bullshit.rb', line 271 def self.time(bc_method) clock = new(bc_method) duration = clock.case.duration.abs if bs = clock.case.batch_size and bs > 1 duration *= bs end until_at = Time.now + duration bs = clock.case.batch_size.abs bs = 1 if !bs or bs < 0 begin bc_method.before_run $DEBUG and warn "Calling #{bc_method.name}." clock.inc_scatter clock.measure do bs.times { yield } end bc_method.after_run end until clock.time > until_at clock end |
.times ⇒ Object
The times which should be displayed in the output.
510 511 512 |
# File 'lib/bullshit.rb', line 510 def self.times TIMES.map { |t| t.to_s } end |
.to_a ⇒ Object
Return column names in relation to Clock#to_a method.
366 367 368 |
# File 'lib/bullshit.rb', line 366 def self.to_a %w[ #scatter ] + TIMES + %w[ repeat ] end |
Instance Method Details
#<<(times) ⇒ Object
Add the array times
to this clock’s time measurements. times
consists of the time measurements in float values in order of TIMES.
338 339 340 341 342 343 344 |
# File 'lib/bullshit.rb', line 338 def <<(times) r = times.shift @repeat += 1 if @times[:repeat].last != r @times[:repeat] << r TIMES.zip(times) { |t, time| @times[t] << time.to_f } self end |
#analysis ⇒ Object
Returns a Hash of Analysis object for all of TIMES’s time keys.
347 348 349 350 351 352 353 |
# File 'lib/bullshit.rb', line 347 def analysis @analysis ||= Hash.new do |h, time| time = time.to_sym times = @times[time] h[time] = Analysis.new(times) end end |
#arithmetic_mean(time) ⇒ Object Also known as: mean
Returns the arithmetic mean of time
.
493 494 495 |
# File 'lib/bullshit.rb', line 493 def arithmetic_mean(time) analysis[time.to_sym].mean end |
#autocorrelation(time) ⇒ Object
Return the array of autocorrelation values for time
.
520 521 522 |
# File 'lib/bullshit.rb', line 520 def autocorrelation(time) analysis[time.to_sym].autocorrelation end |
#autocorrelation_plot(time) ⇒ Object
Returns the arrays for the autocorrelation plot, the first array for the numbers of lag measured, the second for the autocorrelation value.
526 527 528 529 530 531 |
# File 'lib/bullshit.rb', line 526 def autocorrelation_plot(time) r = autocorrelation time start = @times[:repeat].first ende = (start + r.size) (start...ende).to_a.zip(r) end |
#call_time_mean ⇒ Object
Seconds per call (mean)
467 468 469 |
# File 'lib/bullshit.rb', line 467 def call_time_mean mean(self.case.compare_time) end |
#call_time_median ⇒ Object
Seconds per call (median)
483 484 485 |
# File 'lib/bullshit.rb', line 483 def call_time_median median(self.case.compare_time) end |
#calls(call_time_type) ⇒ Object
Calls per second of the call_time_type
, e. g. :call_time_mean or :call_time_median.
473 474 475 |
# File 'lib/bullshit.rb', line 473 def calls(call_time_type) __send__(call_time_type) ** -1 end |
#calls_mean ⇒ Object
Calls per second (mean)
478 479 480 |
# File 'lib/bullshit.rb', line 478 def calls_mean call_time_mean ** -1 end |
#calls_median ⇒ Object
Calls per second (median)
488 489 490 |
# File 'lib/bullshit.rb', line 488 def calls_median call_time_median ** -1 end |
#case ⇒ Object
The benchmark case class this clock belongs to (via bc_method).
319 320 321 |
# File 'lib/bullshit.rb', line 319 def case @bc_method.case.class end |
#cover?(other) ⇒ Boolean
Return true, if other’s mean value is indistinguishable from this object’s mean after filtering out the noise from the measurements with a Welch’s t-Test. This mean’s that differences in the mean of both clocks might not inidicate a real performance difference and may be caused by chance.
360 361 362 363 |
# File 'lib/bullshit.rb', line 360 def cover?(other) time = self.case.compare_time.to_sym analysis[time].cover?(other.analysis[time], self.case.covering.alpha_level.abs) end |
#detect_autocorrelation(time) ⇒ Object
Returns the q value for the Ljung-Box statistic of this time
‘s analysis.detect_autocorrelation method.
448 449 450 451 452 |
# File 'lib/bullshit.rb', line 448 def detect_autocorrelation(time) analysis[time.to_sym].detect_autocorrelation( self.case.autocorrelation.max_lags.to_i, self.case.autocorrelation.alpha_level.abs) end |
#detect_outliers(time) ⇒ Object
Return a result hash with the number of :very_low, :low, :high, and :very_high outliers, determined by the box plotting algorithm run with :median and :iqr parameters. If no outliers were found or the iqr is less than epsilon, nil is returned.
458 459 460 |
# File 'lib/bullshit.rb', line 458 def detect_outliers(time) analysis[time.to_sym].detect_outliers(self.case.outliers_factor.abs) end |
#file_path(*args) ⇒ Object
Return the result of CaseMethod#file_path for this clock’s bc_method.
534 535 536 |
# File 'lib/bullshit.rb', line 534 def file_path(*args) @bc_method.file_path(*args) end |
#find_truncation_offset ⇒ Object
Find an offset from the start of the measurements in this clock to truncate the initial data until a stable state has been reached and return it as an integer.
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 |
# File 'lib/bullshit.rb', line 553 def find_truncation_offset truncation = self.case.truncate_data slope_angle = self.case.truncate_data.slope_angle.abs time = self.case.compare_time.to_sym ms = analysis[time].measurements.reverse offset = ms.size - 1 @slopes = [] ModuleFunctions.array_window(ms, truncation.window_size) do |data| lr = LinearRegression.new(data) a = lr.a @slopes << [ offset, a ] a.abs > slope_angle and break offset -= 1 end offset < 0 ? 0 : offset end |
#geometric_mean(time) ⇒ Object
Returns the geometric mean of time
.
505 506 507 |
# File 'lib/bullshit.rb', line 505 def geometric_mean(time) analysis[time.to_sym].geometric_mean end |
#harmonic_mean(time) ⇒ Object
Returns the harmonic mean of time
.
500 501 502 |
# File 'lib/bullshit.rb', line 500 def harmonic_mean(time) analysis[time.to_sym].harmonic_mean end |
#histogram(time) ⇒ Object
Return the Histogram for the time
values.
515 516 517 |
# File 'lib/bullshit.rb', line 515 def histogram(time) analysis[time.to_sym].histogram(self.case.histogram.bins) end |
#inc_scatter ⇒ Object
Increment scatter counter by one.
392 393 394 |
# File 'lib/bullshit.rb', line 392 def inc_scatter @scatter += 1 end |
#max(time) ⇒ Object
Returns the maximum for the time
(one of TIMES’ symbols).
432 433 434 |
# File 'lib/bullshit.rb', line 432 def max(time) analysis[time.to_sym].max end |
#measure ⇒ Object
Take a single measurement. This method should be called with the code to benchmark in a block.
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'lib/bullshit.rb', line 398 def measure before = take_time yield after = take_time @repeat += 1 @times[:repeat] << @repeat @times[:scatter] << @scatter bs = self.case.batch_size.abs if bs and bs > 1 TIMES.each_with_index { |t, i| @times[t] << (after[i] - before[i]) / bs } else TIMES.each_with_index { |t, i| @times[t] << after[i] - before[i] } end @analysis = nil end |
#median(time) ⇒ Object
Returns the median of the time
values (one of TIMES’ symbols).
437 438 439 |
# File 'lib/bullshit.rb', line 437 def median(time) analysis[time.to_sym].median end |
#min(time) ⇒ Object
Returns the minimum for the time
(one of TIMES’ symbols).
427 428 429 |
# File 'lib/bullshit.rb', line 427 def min(time) analysis[time.to_sym].min end |
#percentile(time, p = 50) ⇒ Object
Returns the p
-percentile of the time
values (one of TIMES’ symbols).
442 443 444 |
# File 'lib/bullshit.rb', line 442 def percentile(time, p = 50) analysis[time.to_sym].percentile p end |
#sample_standard_deviation(time) ⇒ Object
Returns the sample standard deviation for the time
(one of TIMES’ symbols).
416 417 418 |
# File 'lib/bullshit.rb', line 416 def sample_standard_deviation(time) analysis[time.to_sym].sample_standard_deviation end |
#sample_standard_deviation_percentage(time) ⇒ Object
Returns the sample standard deviation for the time
(one of TIMES’ symbols) in percentage of its arithmetic mean.
422 423 424 |
# File 'lib/bullshit.rb', line 422 def sample_standard_deviation_percentage(time) analysis[time.to_sym].sample_standard_deviation_percentage end |
#take_time ⇒ Object
Takes the times an returns an array, consisting of the times in the order of enumerated in the TIMES constant.
383 384 385 386 387 388 389 |
# File 'lib/bullshit.rb', line 383 def take_time @time, times = Time.now, Process.times user_time = times.utime + times.cutime # user time of this process and its children system_time = times.stime + times.cstime # system time of this process and its children total_time = user_time + system_time # total time of this process and its children [ @time.to_f, total_time, user_time, system_time ] end |
#to_a ⇒ Object
Returns the measurements as an array of arrays.
371 372 373 374 375 376 377 378 379 |
# File 'lib/bullshit.rb', line 371 def to_a if @repeat >= 1 (::Bullshit::Clock::ALL_COLUMNS).map do |t| analysis[t].measurements end.transpose else [] end end |
#truncate_data(offset) ⇒ Object
Truncate the measurements stored in this clock starting from the integer offset
.
540 541 542 543 544 545 546 547 548 |
# File 'lib/bullshit.rb', line 540 def truncate_data(offset) for t in ALL_COLUMNS times = @times[t] @times[t] = @times[t][offset, times.size] @repeat = @times[t].size end @analysis = nil self end |