pax_global_header00006660000000000000000000000064134204050150014504gustar00rootroot0000000000000052 comment=bdfb2cb21fe32fe46ced6211a9edbcfe46442c77 hitimes-1.3.1/000077500000000000000000000000001342040501500131505ustar00rootroot00000000000000hitimes-1.3.1/.gitignore000066400000000000000000000002271342040501500151410ustar00rootroot00000000000000*~ *.swp *.swo *.o *.a ext/hitimes/Makefile ext/hitimes/rbconfig.rb *.log a.out *.so *.bundle *.class *.jar coverage doc log pkg tags tmp *.lock .gem/ hitimes-1.3.1/.travis.yml000066400000000000000000000002411342040501500152560ustar00rootroot00000000000000language: ruby before_install: - sudo apt-get -qq update - sudo apt-get install -y libgmp-dev rvm: - "2.3" - "2.4" - "2.5" - "2.6" - jruby-9.2.5.0 hitimes-1.3.1/CONTRIBUTING.md000066400000000000000000000044051342040501500154040ustar00rootroot00000000000000# Hi there! I see you are interested in contributing. That is wonderful. I love contributions. I guarantee that there are bugs in this software. And I guarantee that there is a feature you want that is not in here yet. As such, any and all bugs reports are gratefully accepted, bugfixes even more so. Helping out with bugs is the easiest way to contribute. ## The Quick Version * Have a [GitHub Account][]. * Search the [GitHub Issues][] and see if your issue already present. If so add your comments, :thumbsup:, etc. * Issue not there? Not a problem, open up a [new issue][]. * **Bug reports** please be as detailed as possible. Include: * full ruby engine and version: `ruby -e 'puts RUBY_DESCRIPTION'` * operating system and version * version of hitimes `ruby -rubygems -e "require 'hitimes'; puts Hitimes::VERSION"` * as much detail about the bug as possible so I can replicated it. Feel free to link in a [gist][] * **New Feature** * What the new feature should do. * What benefit the new feature brings to the project. * Fork the [repo][]. * Create a new branch for your issue: `git checkout -b issue/my-issue` * Lovingly craft your contribution: * `rake develop` to get started * `rake test` to run tests * Make sure that `rake test` passes. Its important, I said it twice. * Add yourself to the contributors section below. * Submit your [pull request][]. ## Building Windows Binaries This is done using https://github.com/rake-compiler/rake-compiler-dock 1. have VirtualBox installed 2. have Docker Machine installed (https://docs.docker.com/engine/installation/) 3. `gem install rake-compiler-dock` 4. `rake-compiler-dock` (this could take a while) 5. `bundle` 6. `rake cross native gem` # Contributors * Jeremy Hinegardner * Wojciech Piekutowski [GitHub Account]: https://github.com/signup/free "GitHub Signup" [GitHub Issues]: https://github.com/copiousfreetime/hitimes/issues "Hitimes Issues" [new issue]: https://github.com/copiousfreetime/hitimes/issues/new "New Hitimes Issue" [gist]: https://gist.github.com/ "New Gist" [repo]: https://github.com/copiousfreetime/hitimes "hitimes Repo" [pull request]: https://help.github.com/articles/using-pull-requests "Using Pull Requests" hitimes-1.3.1/Gemfile000066400000000000000000000002321342040501500144400ustar00rootroot00000000000000# DO NOT EDIT - This file is automatically generated # Make changes to Manifest.txt and/or Rakefile and regenerate source "https://rubygems.org/" gemspec hitimes-1.3.1/HISTORY.md000066400000000000000000000070321342040501500146350ustar00rootroot00000000000000# Hitimes Changelog ## Version 1.3.1 2019-01-18 * Update jruby extension to not use global runtime state (thanks @kares) (#70) * Update travis CI config * Update documentataion for Ruby 2.6 ## Version 1.3.0 2018-06-15 * Add api method `Hitimes.raw_instant` to expose raw OS instant value * Add api constant `Hitimes::INSTANT_CONVERSION_FACTOR` to expose raw OS instant conversion factor * other development cleanup tasks ## Version 1.2.6 2017-08-04 * Resolve version number issue (#61) (thanks @anthraxx) ## Version 1.2.5 2017-05-25 * Update dependencies * Add ruby 2.4 to windows fatbinary * Update docs to indicate windows ruby before 2.0 is no longer supported ## Version 1.2.4 2016-05-01 * Fix finding the extension on ruby 2.1.10 (thanks @wpiekutowski) * Add more readable load error (thanks @mbautin) * Update README with what versions of ruby are supported. ## Version 1.2.3 2015-09-13 * Release new fatbinary version for windows * Update README to indicate duration units * Provide a more friendly error message if the gem is not installed correctly ## Version 1.2.2 2014-07-09 * fix compilation issue with clock_gettime in libc (reported by eradman and virtualfunction) * Switch to minispec for tests ## Version 1.2.1 2013-03-12 * Update dependencies * Ruby 2.0 fixes * Switch to Markdown, Yeah RDoc 4.0! ## Version 1.2.0 2013-02-09 * Update dependencies * Documentation cleanup * Fix use of deprecated JRuby API in java extension * Fix use of deprecated OSX system calls in C extension * Make hitimes -w clean * Fix ambiguity of calling duration on non-started Interval * Use RbConfig instead of Config (eregon) * Added Hitimes.measure * Switch to using rake-compiler for cross compilation of gems ## Version 1.1.1 2010-09-04 * Remove the unnecessary dependencies that should be development dependencies ## Version 1.1.0 2010-07-28 * Add a pure java extension so hitimes may be used in jruby with the same API ## Version 1.0.5 2010-07-20 * Fix 'circular require considered harmful' warnings in 1.9.x (reported by Roger Pack) * Fix 'method redefined' warnings in 1.9.x (reported by Roger Pack) ## Version 1.0.4 2009-08-01 * Add in support for x86-mingw32 gem * Add version subdirectory for extension on all platforms ## Version 1.0.3 2009-06-28 * Fix bug with time.h on linode (reported by Roger Pack) * Fix potential garbage collection issue with Interval class * Windows gem is now a fat binary to support installing in 1.8 or 1.9 from the same gem ## Version 1.0.1 2009-06-12 * Fix examples * performance tuning, new Hitimes::Metric derived classes are faster than old Timer class ## Version 1.0.0 2009-06-12 * Major version bump with complete refactor of the metric collection API * 3 types of metrics now instead of just 1 Timer * Hitimes::ValueMetric * Hitimes::TimedMetric * Hitimes::TimedValueMetric * The ability to convert all metrics #to_hash * Updated documentation with examples using each metric type ## Version 0.4.1 2009-02-19 * change to ISC License * fix bug in compilation on gentoo ## Version 0.4.0 2008-12-20 * Added new stat 'rate' * Added new stat method to_hash * Added Hitimes::MutexedStats class for threadsafe stats collection - not needed when used in MRI 1.8.x * remove stale dependency on mkrf ## Version 0.3.0 * switched to extconf for building extensions * first release of windows binary gem * reverted back to normal rdoc ## Version 0.2.1 * added Timer#rate method * switched to darkfish rdoc ## Version 0.2.0 * Performance improvements * Added Hitimes::Stats class ## Version 0.1.0 * Initial completion hitimes-1.3.1/LICENSE000066400000000000000000000014441342040501500141600ustar00rootroot00000000000000ISC LICENSE - http://opensource.org/licenses/isc-license.txt Copyright (c) 2008-2015 Jeremy Hinegardner Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. hitimes-1.3.1/Manifest.txt000066400000000000000000000021371342040501500154620ustar00rootroot00000000000000CONTRIBUTING.md HISTORY.md LICENSE Manifest.txt README.md Rakefile examples/benchmarks.rb examples/stats.rb ext/hitimes/c/extconf.rb ext/hitimes/c/hitimes.c ext/hitimes/c/hitimes_instant_clock_gettime.c ext/hitimes/c/hitimes_instant_osx.c ext/hitimes/c/hitimes_instant_windows.c ext/hitimes/c/hitimes_interval.c ext/hitimes/c/hitimes_interval.h ext/hitimes/c/hitimes_stats.c ext/hitimes/c/hitimes_stats.h ext/hitimes/java/src/hitimes/Hitimes.java ext/hitimes/java/src/hitimes/HitimesInterval.java ext/hitimes/java/src/hitimes/HitimesService.java ext/hitimes/java/src/hitimes/HitimesStats.java lib/hitimes.rb lib/hitimes/metric.rb lib/hitimes/mutexed_stats.rb lib/hitimes/paths.rb lib/hitimes/stats.rb lib/hitimes/timed_metric.rb lib/hitimes/timed_value_metric.rb lib/hitimes/value_metric.rb lib/hitimes/version.rb spec/hitimes_spec.rb spec/interval_spec.rb spec/metric_spec.rb spec/mutex_stats_spec.rb spec/paths_spec.rb spec/spec_helper.rb spec/stats_spec.rb spec/timed_metric_spec.rb spec/timed_value_metric_spec.rb spec/value_metric_spec.rb spec/version_spec.rb tasks/default.rake tasks/extension.rake tasks/this.rb hitimes-1.3.1/README.md000066400000000000000000000117661342040501500144420ustar00rootroot00000000000000# Hitimes [![Build Status](https://travis-ci.org/copiousfreetime/hitimes.svg?branch=master)](https://travis-ci.org/copiousfreetime/hitimes) ## Description A fast, high resolution timer library for recording peformance metrics. * [Homepage](http://github.com/copiousfreetime/hitimes) * [Github project](http://github.com.org/copiousfreetime/hitimes) * email jeremy at copiousfreetime dot org * `git clone url git://github.com/copiousfreetime/hitimes.git` ## Table of Contents * [Requirements](#requirements) * [Usage](#usage) * [Contributing](#contributing) * [Support](#support) * [License](#license) ## Requirements Hitimes requires the following to run: * Ruby ## Usage Hitimes easiest to use when installed with `rubygems`: ```sh gem install hitimes ``` Or as part of your bundler `Gemfile`: ```ruby gem 'hitimes' ``` You can load it with the standard ruby require statement. ```ruby require 'hitimes' ``` ### Interval Use `Hitimes::Interval` to calculate only the duration of a block of code. Returns the time as seconds. ```ruby duration = Hitimes::Interval.measure do 1_000_000.times do |x| 2 + 2 end end puts duration # => 0.047414297 (seconds) ``` ### TimedMetric Use a `Hitimes::TimedMetric` to calculate statistics about an iterative operation ```ruby timed_metric = Hitimes::TimedMetric.new('operation on items') ``` Explicitly use `start` and `stop`: ```ruby collection.each do |item| timed_metric.start # .. do something with item timed_metric.stop end ``` Or use the block. In `TimedMetric` the return value of `measure` is the return value of the block. ```ruby collection.each do |item| result_of_do_something = timed_metric.measure { do_something( item ) } end ``` And then look at the stats ```ruby puts timed_metric.mean puts timed_metric.max puts timed_metric.min puts timed_metric.stddev puts timed_metric.rate ``` ### ValueMetric Use a `Hitimes::ValueMetric` to calculate statistics about measured samples. ``` ruby value_metric = Hitimes::ValueMetric.new( 'size of thing' ) loop do # ... do stuff changing sizes of 'thing' value_metric.measure( thing.size ) # ... do other stuff that may change size of thing end puts value_metric.mean puts value_metric.max puts value_metric.min puts value_metric.stddev puts value_metric.rate ``` ### TimedValueMetric Use a `Hitimes::TimedValueMetric` to calculate statistics about batches of samples. ``` ruby timed_value_metric = Hitimes::TimedValueMetric.new( 'batch times' ) loop do batch = ... # get a batch of things timed_value_metric.start # .. do something with batch timed_value_metric.stop( batch.size ) end puts timed_value_metric.rate puts timed_value_metric.timed_stats.mean puts timed_value_metric.timed_stats.max puts timed_value_metric.timed_stats.min puts timed_value_metric.timed_stats.stddev puts timed_value_metric.value_stats.mean puts timed_value_metric.value_stats.max puts timed_value_metric.value_stats.min puts timed_value_metric.value_stats.stddev ``` ### Implementation details Hitimes use the appropriate low-level system call for each operating system to get the highest granularity time increment possible. Generally this is nanosecond resolution, or whatever the hardware chip in the CPU supports. It currently supports any of the following systems: * any system with the POSIX call `clock_gettime()` * Mac OS X * Windows * JRuby ## Support Hitimes is supported on whatever versions of ruby are currently supported. Hitimes also follows [semantic versioning](http://semver.org/). The current officially supported versions of Ruby are: * MRI Ruby (all platforms) 2.3 - 2.6 * JRuby 9.1.17.0, 9.2.X.X Unofficially supported versions, these have been supported in the past when they were the primary rubies around. In all likelihood they still work, but are not supported. * MRI Ruby (linux/mac/bsd/unix/etc) - everything from 1.8.7 to 2.2 * MRI Ruby (windows) - 2.0 and up * Ruby 1.8 and 1.9 for windows are supported in hitimes version 1.2.4 or earlier * JRuby - I think everything back to 1.4 * Rubinius ## Contributing Please read the [CONTRIBUTING.md](CONTRIBUTING.md) ## Credits * [Bruce Williams](https://github.com/bruce) for suggesting the idea ## License Hitimes is licensed under the [ISC](https://opensource.org/licenses/ISC) license. Copyright (c) 2008-2016 Jeremy Hinegardner Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. hitimes-1.3.1/Rakefile000066400000000000000000000016631342040501500146230ustar00rootroot00000000000000# vim: syntax=ruby load 'tasks/this.rb' This.name = "hitimes" This.author = "Jeremy Hinegardner" This.email = "jeremy@copiousfreetime.org" This.homepage = "http://github.com/copiousfreetime/#{ This.name }" This.ruby_gemspec do |spec| spec.add_development_dependency( 'rake' , '~> 12.0') spec.add_development_dependency( 'minitest' , '~> 5.5' ) spec.add_development_dependency( 'rdoc' , '~> 5.0' ) spec.add_development_dependency( 'json' , '~> 2.0' ) spec.add_development_dependency( 'rake-compiler', '~> 1.0' ) spec.add_development_dependency( 'rake-compiler-dock', '~> 0.6' ) spec.add_development_dependency( 'simplecov' , '~> 0.14' ) spec.extensions.concat This.extension_conf_files spec.license = "ISC" end This.java_gemspec( This.ruby_gemspec ) do |spec| spec.extensions.clear spec.files << "lib/hitimes/hitimes.jar" end load 'tasks/default.rake' load 'tasks/extension.rake' hitimes-1.3.1/examples/000077500000000000000000000000001342040501500147665ustar00rootroot00000000000000hitimes-1.3.1/examples/benchmarks.rb000066400000000000000000000054331342040501500174350ustar00rootroot00000000000000require 'benchmark' require 'time' # # this is all here in case this example is run from the examples directory # begin require 'hitimes' rescue LoadError => le ext_path = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "ext" ) ) lib_path = File.expand_path( File.join( File.dirname( __FILE__ ), "..", "lib" ) ) if $:.include?( ext_path ) then raise le end $: << ext_path $: << lib_path retry end #---------------------------------------------------------------------- # test program to look at the performance sampling time durations using # different methods #---------------------------------------------------------------------- include Benchmark # # Normal apprach to Interval usage # def hitimes_duration_i1 i = Hitimes::Interval.new i.start i.stop end # # Use the easy access method to start stop an interval # def hitimes_duration_i2 Hitimes::Interval.now.stop end # # Use a new timer each time # def hitimes_duration_t1 Hitimes::TimedMetric.now('duration_t1').stop end # # reuse the same timer over and over # HT2= Hitimes::TimedMetric.new( 'duration_t2' ) def hitimes_duration_t2 HT2.start HT2.stop end HT3 = Hitimes::TimedMetric.new( 'duration_t3' ) def hitimes_duration_t3 HT3.measure { nil } end # # Check out the speed of the TimedValueMetric too # def hitimes_duration_tv1 Hitimes::TimedValueMetric.now( 'duration_tv1' ).stop( 42 ) end HTV2 = Hitimes::TimedValueMetric.new( 'duration_tv2' ) def hitimes_duration_tv2 HTV2.start HTV2.stop( 42 ) end HTV3 = Hitimes::TimedValueMetric.new( 'duration_tv3' ) def hitimes_duration_tv3 HTV3.measure( 42 ) { nil } end # # use the Struct::Tms values and return the difference in User time between 2 # successive calls # def process_duration t1 = Process.times.utime Process.times.utime - t1 end # # Take 2 times and subtract one from the other # def time_duration t1 = Time.now.to_f Time.now.to_f - t1 end puts "Testing time sampling 100,000 times" bm(30) do |x| x.report("Process") { 100_000.times { process_duration } } x.report("Time") { 100_000.times { time_duration } } x.report("Hitimes::TimedMetric 1") { 100_000.times { hitimes_duration_t1 } } x.report("Hitimes::TimedMetric 2") { 100_000.times { hitimes_duration_t2 } } x.report("Hitimes::TimedMetric 3") { 100_000.times { hitimes_duration_t3 } } x.report("Hitimes::Interval 1") { 100_000.times { hitimes_duration_i1 } } x.report("Hitimes::Interval 2") { 100_000.times { hitimes_duration_i2 } } x.report("Hitimes::TimedValueMetric 1") { 100_000.times { hitimes_duration_tv1 } } x.report("Hitimes::TimedValueMetric 2") { 100_000.times { hitimes_duration_tv2 } } x.report("Hitimes::TimedValueMetric 3") { 100_000.times { hitimes_duration_tv3 } } end hitimes-1.3.1/examples/stats.rb000066400000000000000000000011211342040501500164440ustar00rootroot00000000000000# # this is all here in case this example is run from the examples directory # begin require 'hitimes' rescue LoadError => le %w[ ext lib ].each do |p| path = File.expand_path( File.join( File.dirname( __FILE__ ), "..", p ) ) if $:.include?( path ) then raise le end $: << path end retry end s = Hitimes::Stats.new dir = ARGV.shift || Dir.pwd Dir.entries( dir ).each do |entry| fs = File.stat( entry ) if fs.file? then s.update( fs.size ) end end Hitimes::Stats::STATS.each do |m| puts "#{m.rjust(6)} : #{s.send( m ) }" end puts s.to_hash.inspect hitimes-1.3.1/ext/000077500000000000000000000000001342040501500137505ustar00rootroot00000000000000hitimes-1.3.1/ext/hitimes/000077500000000000000000000000001342040501500154125ustar00rootroot00000000000000hitimes-1.3.1/ext/hitimes/c/000077500000000000000000000000001342040501500156345ustar00rootroot00000000000000hitimes-1.3.1/ext/hitimes/c/extconf.rb000066400000000000000000000015201342040501500176250ustar00rootroot00000000000000require 'rbconfig' require 'mkmf' if RbConfig::CONFIG['host_os'] =~ /darwin/ then $CFLAGS += " -DUSE_INSTANT_OSX=1 -Wall" $LDFLAGS += " -framework CoreServices" elsif RbConfig::CONFIG['host_os'] =~ /win(32|64)/ or RbConfig::CONFIG['host_os'] =~ /mingw/ then $CFLAGS += " -DUSE_INSTANT_WINDOWS=1" else if have_library("rt", "clock_gettime") then $CFLAGS += " -DUSE_INSTANT_CLOCK_GETTIME=1" elsif have_library("c", "clock_gettime") then $CFLAGS += " -DUSE_INSTANT_CLOCK_GETTIME=1" else raise NotImplementedError, <<-_ Unable to find the function 'clock_gettime' in either libc or librt. Please file an issue at https://github.com/copiousfreetime/hitimes. _ end end # put in a different location if on windows so we can have fat binaries subdir = RUBY_VERSION.gsub(/\.\d+$/,'') create_makefile("hitimes/#{subdir}/hitimes") hitimes-1.3.1/ext/hitimes/c/hitimes.c000066400000000000000000000015761342040501500174530ustar00rootroot00000000000000#include #include "hitimes_interval.h" /* Module and Classes */ VALUE mH; /* module Hitimes */ VALUE eH_Error; /* class Hitimes::Error */ /** * call-seq: * Hitimes.raw_instant -> Integer * * Return the raw instant value from the operating system */ VALUE hitimes_instant_raw( ) { unsigned long long i = (unsigned long long)hitimes_get_current_instant( ); return ULL2NUM(i); } /* * Document-class: Hitimes::Error * * General error class for the Hitimes module */ void Init_hitimes( ) { mH = rb_define_module("Hitimes"); eH_Error = rb_define_class_under(mH, "Error", rb_eStandardError); rb_define_const( mH, "INSTANT_CONVERSION_FACTOR", DBL2NUM( HITIMES_INSTANT_CONVERSION_FACTOR )); rb_define_module_function( mH, "raw_instant", hitimes_instant_raw, 0 ); Init_hitimes_interval(); Init_hitimes_stats( ); } hitimes-1.3.1/ext/hitimes/c/hitimes_instant_clock_gettime.c000066400000000000000000000011421342040501500240710ustar00rootroot00000000000000#ifdef USE_INSTANT_CLOCK_GETTIME #include "hitimes_interval.h" #include #ifndef CLOCK_MONOTONIC # include # ifndef CLOCK_MONOTONIC # ifdef __linux__ # include # endif # endif #endif hitimes_instant_t hitimes_get_current_instant( ) { struct timespec time; int rc; rc = clock_gettime( CLOCK_MONOTONIC, &time); if ( 0 != rc ) { char* e = strerror( rc ); rb_raise(eH_Error, "Unable to retrieve time for CLOCK_MONOTONIC : %s", e ); } return ( ( NANOSECONDS_PER_SECOND * (long)time.tv_sec ) + time.tv_nsec ); } #endif hitimes-1.3.1/ext/hitimes/c/hitimes_instant_osx.c000066400000000000000000000023671342040501500221030ustar00rootroot00000000000000#ifdef USE_INSTANT_OSX #include "hitimes_interval.h" #include #include /* All this OSX code is adapted from * https://developer.apple.com/library/archive/qa/qa1398/_index.html */ /* * returns the conversion factor, this value is used to convert * the value from hitimes_get_current_instant() into seconds */ long double hitimes_instant_conversion_factor() { static mach_timebase_info_data_t s_timebase_info; static long double conversion_factor; static uint64_t nano_conversion; /** * If this is the first time we've run, get the timebase. * We can use denom == 0 to indicate that s_timebase_info is * uninitialised because it makes no sense to have a zero * denominator is a fraction. */ if ( s_timebase_info.denom == 0 ) { mach_timebase_info(&s_timebase_info); nano_conversion = s_timebase_info.numer / s_timebase_info.denom; conversion_factor = (long double) (nano_conversion) * (1e9l); } return conversion_factor; } /* * returns the mach absolute time, which has no meaning outside of a conversion * factor. */ hitimes_instant_t hitimes_get_current_instant() { return mach_absolute_time(); } #endif hitimes-1.3.1/ext/hitimes/c/hitimes_instant_windows.c000066400000000000000000000011061342040501500227520ustar00rootroot00000000000000#ifdef USE_INSTANT_WINDOWS #include "hitimes_interval.h" /* * returns the conversion factor, this value is used to convert * the value from hitimes_get_current_instant() into seconds */ long double hitimes_instant_conversion_factor() { LARGE_INTEGER ticks_per_second; QueryPerformanceFrequency( &ticks_per_second ); return (double)ticks_per_second.QuadPart; } /* * returns the number of ticks */ hitimes_instant_t hitimes_get_current_instant() { LARGE_INTEGER tick; QueryPerformanceCounter(&tick); return (hitimes_instant_t)tick.QuadPart; } #endif hitimes-1.3.1/ext/hitimes/c/hitimes_interval.c000066400000000000000000000237341342040501500213570ustar00rootroot00000000000000/** * Copyright (c) 2008 Jeremy Hinegardner * All rights reserved. See LICENSE and/or COPYING for details. * * vim: shiftwidth=4 */ #include "hitimes_interval.h" /* Modules and Classes -- defined here */ VALUE cH_Interval; /* class Hitimes::Interval */ /** * Allocator and Deallocator for Interval classes */ VALUE hitimes_interval_free(hitimes_interval_t* i) { xfree( i ); return Qnil; } VALUE hitimes_interval_alloc(VALUE klass) { VALUE obj; hitimes_interval_t* i = xmalloc( sizeof( hitimes_interval_t ) ); i->start_instant = 0L; i->stop_instant = 0L; i->duration = -1.0l; obj = Data_Wrap_Struct(klass, NULL, hitimes_interval_free, i); return obj; } /** * call-seq: * Interval.now -> Interval * * Create an interval that has already started */ VALUE hitimes_interval_now( ) { VALUE obj; hitimes_interval_t *i = xmalloc( sizeof( hitimes_interval_t ) ); i->start_instant = hitimes_get_current_instant( ); i->stop_instant = 0L; i->duration = -1.0l; obj = Data_Wrap_Struct(cH_Interval, NULL, hitimes_interval_free, i); return obj; } /** * call-seq: * Interval.measure { } -> Float * * Times the execution of the block returning the number of seconds it took */ VALUE hitimes_interval_measure( ) { hitimes_instant_t before; hitimes_instant_t after; long double duration; if ( !rb_block_given_p() ) { rb_raise(eH_Error, "No block given to Interval.measure" ); } before = hitimes_get_current_instant( ); rb_yield( Qnil ); after = hitimes_get_current_instant( ); duration = ( after - before ) / HITIMES_INSTANT_CONVERSION_FACTOR; return rb_float_new( duration ); } /** * call-seq: * interval.split -> Interval * * Immediately stop the current interval and start a new interval that has a * start_instant equivalent to the stop_interval of self. */ VALUE hitimes_interval_split( VALUE self ) { hitimes_interval_t *first; hitimes_interval_t *second = xmalloc( sizeof( hitimes_interval_t ) ); VALUE obj; Data_Get_Struct( self, hitimes_interval_t, first ); first->stop_instant = hitimes_get_current_instant( ); second->start_instant = first->stop_instant; second->stop_instant = 0L; second->duration = -1.0l; obj = Data_Wrap_Struct(cH_Interval, NULL, hitimes_interval_free, second); return obj; } /** * call-seq: * interval.start -> boolean * * mark the start of the interval. Calling start on an already started * interval has no effect. An interval can only be started once. If the * interval is truely started +true+ is returned otherwise +false+. */ VALUE hitimes_interval_start( VALUE self ) { hitimes_interval_t *i; VALUE rc = Qfalse; Data_Get_Struct( self, hitimes_interval_t, i ); if ( 0L == i->start_instant ) { i->start_instant = hitimes_get_current_instant( ); i->stop_instant = 0L; i->duration = -1.0l; rc = Qtrue; } return rc; } /** * call-seq: * interval.stop -> bool or Float * * mark the stop of the interval. Calling stop on an already stopped interval * has no effect. An interval can only be stopped once. If the interval is * truely stopped then the duration is returned, otherwise +false+. */ VALUE hitimes_interval_stop( VALUE self ) { hitimes_interval_t *i; VALUE rc = Qfalse; Data_Get_Struct( self, hitimes_interval_t, i ); if ( 0L == i->start_instant ) { rb_raise(eH_Error, "Attempt to stop an interval that has not started" ); } if ( 0L == i->stop_instant ) { i->stop_instant = hitimes_get_current_instant( ); i->duration = ( i->stop_instant - i->start_instant ) / HITIMES_INSTANT_CONVERSION_FACTOR; rc = rb_float_new( i->duration ); } return rc; } /** * call-seq: * interval.duration_so_far -> Float or false * * return how the duration so far. This will return the duration from the time * the Interval was started if the interval is running, otherwise it will return * false. */ VALUE hitimes_interval_duration_so_far( VALUE self ) { hitimes_interval_t *i; VALUE rc = Qfalse; Data_Get_Struct( self, hitimes_interval_t, i ); if ( 0L == i->start_instant ) { return rc; } if ( 0L == i->stop_instant ) { long double d; hitimes_instant_t now = hitimes_get_current_instant( ); d = ( now - i->start_instant ) / HITIMES_INSTANT_CONVERSION_FACTOR; rc = rb_float_new( d ); } return rc; } /** * call-seq: * interval.started? -> boolean * * returns whether or not the interval has been started */ VALUE hitimes_interval_started( VALUE self ) { hitimes_interval_t *i; Data_Get_Struct( self, hitimes_interval_t, i ); return ( 0L == i->start_instant ) ? Qfalse : Qtrue; } /** * call-seq: * interval.stopped? -> boolean * * returns whether or not the interval has been stopped */ VALUE hitimes_interval_stopped( VALUE self ) { hitimes_interval_t *i; Data_Get_Struct( self, hitimes_interval_t, i ); return ( 0L == i->stop_instant ) ? Qfalse : Qtrue; } /** * call-seq: * interval.running? -> boolean * * returns whether or not the interval is running or not. This means that it * has started, but not stopped. */ VALUE hitimes_interval_running( VALUE self ) { hitimes_interval_t *i; VALUE rc = Qfalse; Data_Get_Struct( self, hitimes_interval_t, i ); if ( ( 0L != i->start_instant ) && ( 0L == i->stop_instant ) ) { rc = Qtrue; } return rc; } /** * call-seq: * interval.start_instant -> Integer * * The integer representing the start instant of the Interval. This value * is not useful on its own. It is a platform dependent value. */ VALUE hitimes_interval_start_instant( VALUE self ) { hitimes_interval_t *i; Data_Get_Struct( self, hitimes_interval_t, i ); return ULL2NUM( i->start_instant ); } /** * call-seq: * interval.stop_instant -> Integer * * The integer representing the stop instant of the Interval. This value * is not useful on its own. It is a platform dependent value. */ VALUE hitimes_interval_stop_instant( VALUE self ) { hitimes_interval_t *i; Data_Get_Struct( self, hitimes_interval_t, i ); return ULL2NUM( i->stop_instant ); } /** * call-seq: * interval.duration -> Float * interval.to_f -> Float * interval.to_seconds -> Float * interval.length -> Float * * Returns the Float value of the interval, the value is in seconds. If the * interval has not had stop called yet, it will report the number of seconds * in the interval up to the current point in time. * * Raises Error if duration is called on an interval that has not started yet. */ VALUE hitimes_interval_duration ( VALUE self ) { hitimes_interval_t *i; Data_Get_Struct( self, hitimes_interval_t, i ); /* raise an error if the internval is not started */ if ( 0L == i->start_instant ) { rb_raise(eH_Error, "Attempt to report a duration on an interval that has not started" ); } /** * if stop has not yet been called, then return the amount of time so far */ if ( 0L == i->stop_instant ) { long double d; hitimes_instant_t now = hitimes_get_current_instant( ); d = ( now - i->start_instant ) / HITIMES_INSTANT_CONVERSION_FACTOR; return rb_float_new( d ); } /* * stop has been called, calculate the duration and save the result */ if ( i->duration < 0.0 ) { i->duration = ( i->stop_instant - i->start_instant ) / HITIMES_INSTANT_CONVERSION_FACTOR; } return rb_float_new( i->duration ); } /** * Document-class: Hitimes::Interval * * This is the lowest level timing mechanism available. It allows for easy * measuring based upon a block: * * duration = Interval.measure { ... } * * Or measuring something specifically * * interval = Interval.new * interval.start * duration = interval.stop * * Allocating and starting an interval can be done in one method call with * * interval = Interval.now * * Interval is useful when you only need to track a single interval of time, or * if you do not want to track statistics about an operation. * */ void Init_hitimes_interval() { mH = rb_define_module("Hitimes"); cH_Interval = rb_define_class_under( mH, "Interval", rb_cObject ); rb_define_alloc_func( cH_Interval, hitimes_interval_alloc ); rb_define_module_function( cH_Interval, "now", hitimes_interval_now, 0 ); /* in hitimes_interval.c */ rb_define_module_function( cH_Interval, "measure", hitimes_interval_measure, 0 ); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "duration", hitimes_interval_duration, 0 ); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "length", hitimes_interval_duration, 0 ); rb_define_method( cH_Interval, "to_f", hitimes_interval_duration, 0 ); rb_define_method( cH_Interval, "to_seconds", hitimes_interval_duration, 0 ); rb_define_method( cH_Interval, "duration_so_far", hitimes_interval_duration_so_far, 0); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "started?", hitimes_interval_started, 0 ); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "running?", hitimes_interval_running, 0 ); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "stopped?", hitimes_interval_stopped, 0 ); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "start_instant", hitimes_interval_start_instant, 0 ); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "stop_instant", hitimes_interval_stop_instant, 0 ); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "start", hitimes_interval_start, 0); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "stop", hitimes_interval_stop, 0); /* in hitimes_interval.c */ rb_define_method( cH_Interval, "split", hitimes_interval_split, 0); /* in hitimes_interval.c */ } hitimes-1.3.1/ext/hitimes/c/hitimes_interval.h000066400000000000000000000041761342040501500213630ustar00rootroot00000000000000/** * Copyright (c) 2008 Jeremy Hinegardner * All rights reserved. See LICENSE and/or COPYING for details. * * vim: shiftwidth=4 */ #ifndef __HITIMES_INTERVAL_H__ #define __HITIMES_INTERVAL_H__ #include #define NANOSECONDS_PER_SECOND 1e9l #ifdef USE_INSTANT_CLOCK_GETTIME # define HITIMES_U64INT unsigned long long int # define HITIMES_INSTANT_CONVERSION_FACTOR 1e9l #elif USE_INSTANT_OSX # define HITIMES_U64INT uint64_t # define HITIMES_INSTANT_CONVERSION_FACTOR hitimes_instant_conversion_factor() #elif USE_INSTANT_WINDOWS # define HITIMES_U64INT unsigned __int64 # define HITIMES_INSTANT_CONVERSION_FACTOR hitimes_instant_conversion_factor() #else # error "Unable to build hitimes, no Instance backend available" #endif /* an alias for a 64bit unsigned integer. The various sytem dependenent * files must define hitimes_u64int_t */ typedef HITIMES_U64INT hitimes_instant_t; typedef struct hitimes_interval { hitimes_instant_t start_instant; hitimes_instant_t stop_instant; long double duration; } hitimes_interval_t; /* all the backends must define these methods */ hitimes_instant_t hitimes_get_current_instant( ); long double hitimes_instant_conversion_factor( ); /* init methods */ void Init_hitimes_stats(); void Init_hitimes_interval(); /* Module and Classes -- defined at the top level */ extern VALUE mH; /* module Hitimes */ extern VALUE eH_Error; /* class Hitimes::Error */ extern VALUE cH_Interval; /* class Hitimes::Interval */ /** * Methods for Interval */ VALUE hitimes_interval_free(hitimes_interval_t* i) ; VALUE hitimes_interval_alloc(VALUE klass); VALUE hitimes_interval_now( ); VALUE hitimes_interval_measure( ); VALUE hitimes_interval_split( VALUE self ); VALUE hitimes_interval_start( VALUE self ); VALUE hitimes_interval_stop( VALUE self ); VALUE hitimes_interval_started( VALUE self ); VALUE hitimes_interval_stopped( VALUE self ); VALUE hitimes_interval_running( VALUE self ); VALUE hitimes_interval_start_instant( VALUE self ); VALUE hitimes_interval_stop_instant( VALUE self ); VALUE hitimes_interval_duration ( VALUE self ); #endif hitimes-1.3.1/ext/hitimes/c/hitimes_stats.c000066400000000000000000000147771342040501500207000ustar00rootroot00000000000000/** * Copyright (c) 2008 Jeremy Hinegardner * All rights reserved. See LICENSE and/or COPYING for details. * * vim: shiftwidth=4 */ #include "hitimes_stats.h" /* classes defined here */ VALUE cH_Stats; /* Hitimes::Stats */ /** * Allocator and Deallocator for Stats classes */ VALUE hitimes_stats_free(hitimes_stats_t* s) { xfree( s ); return Qnil; } VALUE hitimes_stats_alloc(VALUE klass) { VALUE obj; hitimes_stats_t* s = xmalloc( sizeof( hitimes_stats_t ) ); s->min = 0.0; s->max = 0.0; s->count = 0; s->sum = 0.0; s->sumsq = 0.0; obj = Data_Wrap_Struct(klass, NULL, hitimes_stats_free, s); return obj; } /** * call-seq: * stat.update( val ) -> val * * Update the running stats with the new value. * Return the input value. */ VALUE hitimes_stats_update( VALUE self, VALUE v ) { long double new_v; hitimes_stats_t *stats; Data_Get_Struct( self, hitimes_stats_t, stats ); new_v = NUM2DBL( v ); if ( 0 == stats->count ) { stats->min = new_v; stats->max = new_v; } else { stats->min = ( new_v < stats->min) ? ( new_v ) : ( stats->min ); stats->max = ( new_v > stats->max) ? ( new_v ) : ( stats->max ); } stats->count += 1; stats->sum += new_v; stats->sumsq += ( new_v * new_v ); return v; } /** * call-seq: * stat.mean -> Float * * Return the arithmetic mean of the values put into the Stats object. If no * values have passed through the stats object then 0.0 is returned; */ VALUE hitimes_stats_mean( VALUE self ) { hitimes_stats_t *stats; long double mean = 0.0; Data_Get_Struct( self, hitimes_stats_t, stats ); if ( stats->count > 0 ) { mean = stats->sum / stats->count ; } return rb_float_new( mean ); } /** * call-seq: * stat.rate -> Float * * Return the +count+ divided by +sum+. * * In many cases when Stats#update( _value_ ) is called, the _value_ is a unit * of time, typically seconds or microseconds. #rate is a convenience for those * times. In this case, where _value_ is a unit if time, then count divided by * sum is a useful value, i.e. +something per unit of time+. * * In the case where _value_ is a non-time related value, then the value * returned by _rate_ is not really useful. * */ VALUE hitimes_stats_rate( VALUE self ) { hitimes_stats_t *stats; long double rate = 0.0; Data_Get_Struct( self, hitimes_stats_t, stats ); if ( stats->sum > 0.0 ) { rate = stats->count / stats->sum; } return rb_float_new( rate ); } /** * call-seq: * stat.max -> Float * * Return the maximum value that has passed through the Stats object */ VALUE hitimes_stats_max( VALUE self ) { hitimes_stats_t *stats; Data_Get_Struct( self, hitimes_stats_t, stats ); return rb_float_new( stats->max ); } /** * call-seq: * stat.min -> Float * * Return the minimum value that has passed through the Stats object */ VALUE hitimes_stats_min( VALUE self ) { hitimes_stats_t *stats; Data_Get_Struct( self, hitimes_stats_t, stats ); return rb_float_new( stats->min ); } /** * call-seq: * stat.count -> Integer * * Return the number of values that have passed through the Stats object. */ VALUE hitimes_stats_count( VALUE self ) { hitimes_stats_t *stats; Data_Get_Struct( self, hitimes_stats_t, stats ); return LONG2NUM( stats->count ); } /** * call-seq: * stat.sum -> Float * * Return the sum of all the values that have passed through the Stats object. */ VALUE hitimes_stats_sum( VALUE self ) { hitimes_stats_t *stats; Data_Get_Struct( self, hitimes_stats_t, stats ); return rb_float_new( stats->sum ); } /** * call-seq: * stat.sumsq -> Float * * Return the sum of the squars of all the values that passed through the Stats * object. */ VALUE hitimes_stats_sumsq( VALUE self ) { hitimes_stats_t *stats; Data_Get_Struct( self, hitimes_stats_t, stats ); return rb_float_new( stats->sumsq ); } /** * call-seq: * stat.stddev -> Float * * Return the standard deviation of all the values that have passed through the * Stats object. The standard deviation has no meaning unless the count is > 1, * therefore if the current _stat.count_ is < 1 then 0.0 will be returned; */ VALUE hitimes_stats_stddev ( VALUE self ) { hitimes_stats_t *stats; long double stddev = 0.0; Data_Get_Struct( self, hitimes_stats_t, stats ); if ( stats->count > 1 ) { stddev = sqrt( ( stats->sumsq - ( stats->sum * stats->sum / stats->count ) ) / ( stats->count - 1 ) ); } return rb_float_new( stddev ); } /** * Document-class: Hitimes::Stats * * The Stats class encapulsates capturing and reporting statistics. It is * modeled after the RFuzz::Sampler class, but implemented in C. For general use * you allocate a new Stats object, and then update it with new values. The * Stats object will keep track of the _min_, _max_, _count_, _sum_ and _sumsq_ * and when you want you may also retrieve the _mean_, _stddev_ and _rate_. * * this contrived example shows getting a list of all the files in a directory * and running stats on file sizes. * * s = Hitimes::Stats.new * dir = ARGV.shift || Dir.pwd * Dir.entries( dir ).each do |entry| * fs = File.stat( entry ) * if fs.file? then * s.update( fs.size ) * end * end * * %w[ count min max mean sum stddev rate ].each do |m| * puts "#{m.rjust(6)} : #{s.send( m ) }" * end */ void Init_hitimes_stats() { mH = rb_define_module("Hitimes"); cH_Stats = rb_define_class_under( mH, "Stats", rb_cObject ); /* in hitimes_stats.c */ rb_define_alloc_func( cH_Stats, hitimes_stats_alloc ); rb_define_method( cH_Stats, "update", hitimes_stats_update, 1 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "count", hitimes_stats_count, 0 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "max", hitimes_stats_max, 0 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "mean", hitimes_stats_mean, 0 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "min", hitimes_stats_min, 0 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "rate", hitimes_stats_rate, 0 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "sum", hitimes_stats_sum, 0 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "sumsq", hitimes_stats_sumsq, 0 ); /* in hitimes_stats.c */ rb_define_method( cH_Stats, "stddev", hitimes_stats_stddev, 0 ); /* in hitimes_stats.c */ } hitimes-1.3.1/ext/hitimes/c/hitimes_stats.h000066400000000000000000000011011342040501500206560ustar00rootroot00000000000000/** * Copyright (c) 2008 Jeremy Hinegardner * All rights reserved. See LICENSE and/or COPYING for details. * * vim: shiftwidth=4 */ #ifndef __HITIMES_STATS_H__ #define __HITIMES_STATS_H__ #include #include /* classes and modules defined elswhere */ extern VALUE mH; /* Hitimes */ extern VALUE eH_Error; /* Hitimes::Error */ extern VALUE cH_Stats; /* Hitimes::Stats */ typedef struct hitimes_stats { long double min; long double max; long double sum; long double sumsq; long long count; } hitimes_stats_t; #endif hitimes-1.3.1/ext/hitimes/java/000077500000000000000000000000001342040501500163335ustar00rootroot00000000000000hitimes-1.3.1/ext/hitimes/java/src/000077500000000000000000000000001342040501500171225ustar00rootroot00000000000000hitimes-1.3.1/ext/hitimes/java/src/hitimes/000077500000000000000000000000001342040501500205645ustar00rootroot00000000000000hitimes-1.3.1/ext/hitimes/java/src/hitimes/Hitimes.java000066400000000000000000000044001342040501500230270ustar00rootroot00000000000000package hitimes; import java.lang.Math; import java.lang.System; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyException; import org.jruby.RubyModule; import org.jruby.RubyObject; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; import org.jruby.anno.JRubyModule; import org.jruby.anno.JRubyConstant; import org.jruby.exceptions.RaiseException; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; /** * @author Jeremy Hinegardner */ @JRubyModule( name = "Hitimes" ) public class Hitimes { public static final double INSTANT_CONVERSION_FACTOR = 1000000000d; public static RubyClass hitimesIntervalClass; /** * Create the Hitimes module and add it to the Ruby runtime. */ public static RubyModule createHitimesModule( Ruby runtime ) { RubyModule mHitimes = runtime.defineModule("Hitimes"); mHitimes.defineConstant("INSTANT_CONVERSION_FACTOR", runtime.newFloat(INSTANT_CONVERSION_FACTOR)); mHitimes.defineAnnotatedMethods( Hitimes.class ); RubyClass cStandardError = runtime.getStandardError(); RubyClass cHitimesError = mHitimes.defineClassUnder("Error", cStandardError, cStandardError.getAllocator()); RubyClass cHitimesStats = mHitimes.defineClassUnder("Stats", runtime.getObject(), HitimesStats.ALLOCATOR ); cHitimesStats.defineAnnotatedMethods( HitimesStats.class ); RubyClass cHitimesInterval = mHitimes.defineClassUnder("Interval", runtime.getObject(), HitimesInterval.ALLOCATOR ); Hitimes.hitimesIntervalClass = cHitimesInterval; cHitimesInterval.defineAnnotatedMethods( HitimesInterval.class ); return mHitimes; } static RaiseException newHitimesError( Ruby runtime, String message ) { RubyClass errorClass = runtime.getModule("Hitimes").getClass( "Error" ); return new RaiseException( RubyException.newException( runtime, errorClass, message ), true ); } @JRubyMethod( name = "raw_instant", module = true ) public static IRubyObject rawInstant(ThreadContext context, IRubyObject self) { return context.runtime.newFixnum( System.nanoTime() ); } } hitimes-1.3.1/ext/hitimes/java/src/hitimes/HitimesInterval.java000066400000000000000000000131161342040501500245400ustar00rootroot00000000000000package hitimes; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyObject; import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; @JRubyClass( name = "Hitimes::Interval" ) public class HitimesInterval extends RubyObject { private static final long INSTANT_NOT_SET = Long.MIN_VALUE; private static final double DURATION_NOT_SET = Double.NaN; public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new HitimesInterval( runtime, klass ); } }; public HitimesInterval( Ruby runtime, RubyClass klass ) { super( runtime, klass ); } public HitimesInterval( Ruby runtime, RubyClass klass, long start ) { super( runtime, klass ); this.start_instant = start; } private long start_instant = INSTANT_NOT_SET; private long stop_instant = INSTANT_NOT_SET; private double duration = DURATION_NOT_SET; @JRubyMethod( name = "duration", alias = { "length", "to_f", "to_seconds" } ) public IRubyObject duration() { /* * if start has not yet been called, then raise an exception. */ if ( INSTANT_NOT_SET == this.start_instant ) { throw Hitimes.newHitimesError( getRuntime(), "Attempt to report a duration on an interval that has not started"); } /* * if stop has not yet been called, then return the amount of time so far */ if ( INSTANT_NOT_SET == this.stop_instant ) { double d = ( System.nanoTime() - this.start_instant ) / Hitimes.INSTANT_CONVERSION_FACTOR; return getRuntime().newFloat( d ); } /* * if stop has been called, then calculate the duration and return */ if ( DURATION_NOT_SET == this.duration ) { this.duration = (this.stop_instant - this.start_instant) / Hitimes.INSTANT_CONVERSION_FACTOR; } return getRuntime().newFloat( this.duration ); } @JRubyMethod( name = "duration_so_far" ) public IRubyObject duration_so_far() { IRubyObject rc = getRuntime().getFalse(); if ( INSTANT_NOT_SET == this.start_instant ) { return rc; } if ( INSTANT_NOT_SET == this.stop_instant ) { double d = ( System.nanoTime() - this.start_instant ) / Hitimes.INSTANT_CONVERSION_FACTOR; return getRuntime().newFloat( d ); } return rc; } @JRubyMethod( name = "started?" ) public IRubyObject is_started() { if ( INSTANT_NOT_SET == this.start_instant ) { return getRuntime().getFalse(); } return getRuntime().getTrue(); } @JRubyMethod( name = "running?" ) public IRubyObject is_running() { if ( ( INSTANT_NOT_SET != this.start_instant ) && ( INSTANT_NOT_SET == this.stop_instant ) ) { return getRuntime().getTrue(); } return getRuntime().getFalse(); } @JRubyMethod( name = "stopped?" ) public IRubyObject is_stopped() { if ( INSTANT_NOT_SET == this.stop_instant ) { return getRuntime().getFalse(); } return getRuntime().getTrue(); } @JRubyMethod( name = "start_instant" ) public IRubyObject start_instant() { return getRuntime().newFixnum( this.start_instant ); } @JRubyMethod( name = "stop_instant" ) public IRubyObject stop_instant() { return getRuntime().newFixnum( this.stop_instant ); } @JRubyMethod( name = "start" ) public IRubyObject start() { if ( INSTANT_NOT_SET == this.start_instant ) { this.start_instant = System.nanoTime(); return getRuntime().getTrue(); } return getRuntime().getFalse(); } @JRubyMethod( name = "stop" ) public IRubyObject stop() { if ( INSTANT_NOT_SET == this.start_instant ) { throw Hitimes.newHitimesError( getRuntime(), "Attempt to stop an interval that has not started" ); } if ( INSTANT_NOT_SET == this.stop_instant ) { this.stop_instant = System.nanoTime(); this.duration = (this.stop_instant - this.start_instant) / Hitimes.INSTANT_CONVERSION_FACTOR; return getRuntime().newFloat( this.duration ); } return getRuntime().getFalse(); } @JRubyMethod( name = "split" ) public IRubyObject split() { this.stop(); return new HitimesInterval( getRuntime(), Hitimes.hitimesIntervalClass, this.stop_instant ); } @JRubyMethod( name = "now", module = true ) public static IRubyObject now( IRubyObject self ) { return new HitimesInterval( self.getRuntime(), Hitimes.hitimesIntervalClass, System.nanoTime() ); } @JRubyMethod( name = "measure", module = true, frame = true ) public static IRubyObject measure( IRubyObject self, Block block ) { Ruby runtime = self.getRuntime(); if ( block.isGiven() ) { IRubyObject nil = runtime.getNil(); ThreadContext context = runtime.getCurrentContext(); HitimesInterval interval = new HitimesInterval( runtime, Hitimes.hitimesIntervalClass ); interval.start(); block.yield( context, nil ); interval.stop(); return interval.duration(); } else { throw Hitimes.newHitimesError( runtime, "No block given to Interval.measure" ); } } } hitimes-1.3.1/ext/hitimes/java/src/hitimes/HitimesService.java000066400000000000000000000005171342040501500243550ustar00rootroot00000000000000package hitimes; import java.io.IOException; import org.jruby.Ruby; import org.jruby.runtime.load.BasicLibraryService; public class HitimesService implements BasicLibraryService { public boolean basicLoad( final Ruby runtime ) throws IOException { Hitimes.createHitimesModule( runtime ); return true; } } hitimes-1.3.1/ext/hitimes/java/src/hitimes/HitimesStats.java000066400000000000000000000053341342040501500240550ustar00rootroot00000000000000package hitimes; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyObject; import org.jruby.RubyNumeric; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.ObjectAllocator; import org.jruby.anno.JRubyMethod; import org.jruby.anno.JRubyClass; @JRubyClass( name = "Hitimes::Stats" ) public class HitimesStats extends RubyObject { private double min = 0.0; private double max = 0.0; private double sum = 0.0; private double sumsq = 0.0; private long count = 0; public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new HitimesStats( runtime, klass ); } }; public HitimesStats( Ruby runtime, RubyClass klass ) { super( runtime, klass ); } @JRubyMethod( name = "update", required = 1, argTypes = RubyNumeric.class ) public IRubyObject update( IRubyObject val ) { double v = RubyNumeric.num2dbl( val ); if ( 0 == this.count ) { this.min = this.max = v; } else { this.min = ( v < this.min ) ? v : this.min; this.max = ( v > this.max ) ? v : this.max; } this.count += 1; this.sum += v; this.sumsq += (v * v); return val; } @JRubyMethod( name = "mean" ) public IRubyObject mean() { double mean = 0.0; if ( this.count > 0 ) { mean = this.sum / this.count; } return getRuntime().newFloat( mean ); } @JRubyMethod( name = "rate" ) public IRubyObject rate() { double rate = 0.0; if ( this.sum > 0.0 ) { rate = this.count / this.sum ; } return getRuntime().newFloat( rate ); } @JRubyMethod( name = "stddev" ) public IRubyObject stddev() { double stddev = 0.0; if ( this.count > 1 ) { double sq_sum = this.sum * this.sum; stddev = Math.sqrt( ( this.sumsq - ( sq_sum / this.count ) ) / ( this.count - 1 ) ); } return getRuntime().newFloat( stddev ); } @JRubyMethod( name = "min" ) public IRubyObject min() { return getRuntime().newFloat( this.min ); } @JRubyMethod( name = "max" ) public IRubyObject max() { return getRuntime().newFloat( this.max ); } @JRubyMethod( name = "sum" ) public IRubyObject sum() { return getRuntime().newFloat( this.sum ); } @JRubyMethod( name = "sumsq" ) public IRubyObject sumsq() { return getRuntime().newFloat( this.sumsq ); } @JRubyMethod( name = "count" ) public IRubyObject count() { return getRuntime().newFixnum( this.count ); } } hitimes-1.3.1/hitimes.gemspec000066400000000000000000000113121342040501500161550ustar00rootroot00000000000000# DO NOT EDIT - This file is automatically generated # Make changes to Manifest.txt and/or Rakefile and regenerate # -*- encoding: utf-8 -*- # stub: hitimes 1.3.1 ruby lib # stub: ext/hitimes/c/extconf.rb Gem::Specification.new do |s| s.name = "hitimes".freeze s.version = "1.3.1" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Jeremy Hinegardner".freeze] s.date = "2019-01-18" s.description = "A fast, high resolution timer library for recording peformance metrics. * (http://github.com/copiousfreetime/hitimes) * (http://github.com.org/copiousfreetime/hitimes) * email jeremy at copiousfreetime dot org * `git clone url git://github.com/copiousfreetime/hitimes.git`".freeze s.email = "jeremy@copiousfreetime.org".freeze s.extensions = ["ext/hitimes/c/extconf.rb".freeze] s.extra_rdoc_files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "Manifest.txt".freeze, "README.md".freeze] s.files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "LICENSE".freeze, "Manifest.txt".freeze, "README.md".freeze, "Rakefile".freeze, "examples/benchmarks.rb".freeze, "examples/stats.rb".freeze, "ext/hitimes/c/extconf.rb".freeze, "ext/hitimes/c/hitimes.c".freeze, "ext/hitimes/c/hitimes_instant_clock_gettime.c".freeze, "ext/hitimes/c/hitimes_instant_osx.c".freeze, "ext/hitimes/c/hitimes_instant_windows.c".freeze, "ext/hitimes/c/hitimes_interval.c".freeze, "ext/hitimes/c/hitimes_interval.h".freeze, "ext/hitimes/c/hitimes_stats.c".freeze, "ext/hitimes/c/hitimes_stats.h".freeze, "ext/hitimes/java/src/hitimes/Hitimes.java".freeze, "ext/hitimes/java/src/hitimes/HitimesInterval.java".freeze, "ext/hitimes/java/src/hitimes/HitimesService.java".freeze, "ext/hitimes/java/src/hitimes/HitimesStats.java".freeze, "lib/hitimes.rb".freeze, "lib/hitimes/metric.rb".freeze, "lib/hitimes/mutexed_stats.rb".freeze, "lib/hitimes/paths.rb".freeze, "lib/hitimes/stats.rb".freeze, "lib/hitimes/timed_metric.rb".freeze, "lib/hitimes/timed_value_metric.rb".freeze, "lib/hitimes/value_metric.rb".freeze, "lib/hitimes/version.rb".freeze, "spec/hitimes_spec.rb".freeze, "spec/interval_spec.rb".freeze, "spec/metric_spec.rb".freeze, "spec/mutex_stats_spec.rb".freeze, "spec/paths_spec.rb".freeze, "spec/spec_helper.rb".freeze, "spec/stats_spec.rb".freeze, "spec/timed_metric_spec.rb".freeze, "spec/timed_value_metric_spec.rb".freeze, "spec/value_metric_spec.rb".freeze, "spec/version_spec.rb".freeze, "tasks/default.rake".freeze, "tasks/extension.rake".freeze, "tasks/this.rb".freeze] s.homepage = "http://github.com/copiousfreetime/hitimes".freeze s.licenses = ["ISC".freeze] s.rdoc_options = ["--main".freeze, "README.md".freeze, "--markup".freeze, "tomdoc".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.2.2".freeze) s.rubygems_version = "3.0.1".freeze s.summary = "A fast, high resolution timer library for recording peformance metrics.".freeze s.test_files = ["spec/hitimes_spec.rb".freeze, "spec/interval_spec.rb".freeze, "spec/metric_spec.rb".freeze, "spec/mutex_stats_spec.rb".freeze, "spec/paths_spec.rb".freeze, "spec/spec_helper.rb".freeze, "spec/stats_spec.rb".freeze, "spec/timed_metric_spec.rb".freeze, "spec/timed_value_metric_spec.rb".freeze, "spec/value_metric_spec.rb".freeze, "spec/version_spec.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, ["~> 12.0"]) s.add_development_dependency(%q.freeze, ["~> 5.5"]) s.add_development_dependency(%q.freeze, ["~> 5.0"]) s.add_development_dependency(%q.freeze, ["~> 2.0"]) s.add_development_dependency(%q.freeze, ["~> 1.0"]) s.add_development_dependency(%q.freeze, ["~> 0.6"]) s.add_development_dependency(%q.freeze, ["~> 0.14"]) else s.add_dependency(%q.freeze, ["~> 12.0"]) s.add_dependency(%q.freeze, ["~> 5.5"]) s.add_dependency(%q.freeze, ["~> 5.0"]) s.add_dependency(%q.freeze, ["~> 2.0"]) s.add_dependency(%q.freeze, ["~> 1.0"]) s.add_dependency(%q.freeze, ["~> 0.6"]) s.add_dependency(%q.freeze, ["~> 0.14"]) end else s.add_dependency(%q.freeze, ["~> 12.0"]) s.add_dependency(%q.freeze, ["~> 5.5"]) s.add_dependency(%q.freeze, ["~> 5.0"]) s.add_dependency(%q.freeze, ["~> 2.0"]) s.add_dependency(%q.freeze, ["~> 1.0"]) s.add_dependency(%q.freeze, ["~> 0.6"]) s.add_dependency(%q.freeze, ["~> 0.14"]) end end hitimes-1.3.1/lib/000077500000000000000000000000001342040501500137165ustar00rootroot00000000000000hitimes-1.3.1/lib/hitimes.rb000066400000000000000000000030161342040501500157050ustar00rootroot00000000000000#-- # Copyright (c) 2008 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ # # The top level module containing the contents of the hitimes library # # use the library with: # # require 'hitimes' # module Hitimes # # Base class of all errors in Hitimes # class Error < ::StandardError; end # Hitimes.measure { } -> Float # # Times the execution of the block, returning the number of seconds it took def self.measure(&block) Hitimes::Interval.measure(&block) end end require 'hitimes/paths' require 'hitimes/version' # Load the binary extension, try loading one for the specific version of ruby # and if that fails, then fall back to one in the top of the library. # this is the method recommended by rake-compiler attempts = [ "hitimes/#{RUBY_VERSION.sub(/\.\d+$/,'')}/hitimes", "hitimes/hitimes" ] loaded = false path_exceptions = [] attempts.each do |path| begin require path loaded = true break rescue LoadError => load_error full_path = File.expand_path(path) path_exceptions << [ full_path, load_error.message ] end end if !loaded then msg = ["Unable to find binary extension, was hitimes installed correctly? The following paths were tried."] path_exceptions.each do |path, message| msg << "#{path} : #{message}" end raise LoadError, msg.join("\n") end require 'hitimes/stats' require 'hitimes/mutexed_stats' require 'hitimes/metric' require 'hitimes/value_metric' require 'hitimes/timed_metric' require 'hitimes/timed_value_metric' hitimes-1.3.1/lib/hitimes/000077500000000000000000000000001342040501500153605ustar00rootroot00000000000000hitimes-1.3.1/lib/hitimes/metric.rb000066400000000000000000000064761342040501500172050ustar00rootroot00000000000000 #-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ module Hitimes # # Metric hold the common meta information for all derived metric classes # # All metrics hold the meta information of: # # * The name of the metric # * The time of day the first measurement is taken # * The time of day the last measurement is taken # * additional data # # Each derived class is assumed to set the sampling_start_time and # sampling_stop_time appropriately. # # Metric itself should generally not be used. Only use the derived classes. # class Metric # the number of seconds as a float since the sampling_start_time attr_reader :sampling_delta # An additional hash of data to associate with the metric attr_reader :additional_data # The 'name' to associate with the metric attr_reader :name # # :call-seq: # Metric.new( 'my_metric' ) -> Metric # Metric.new( 'my_metric', 'foo' => 'bar', 'this' => 42 ) -> Metric # # Create a new ValueMetric giving it a name and additional data. # # +additional_data+ may be anything that follows the +to_hash+ protocol. # +name+ may be anything that follows the +to_s+ protocol. # def initialize( name, additional_data = {} ) @sampling_start_time = nil @sampling_start_interval = nil @sampling_delta = 0 @name = name.to_s @additional_data = additional_data.to_hash end # # :call-seq: # metric.sampling_start_time -> Float or nil # # The time at which the first sample was taken. # This is the number of microseconds since UNIX epoch UTC as a Float # # If the metric has not started measuring then the start time is nil. # def sampling_start_time if @sampling_start_interval then @sampling_start_time ||= self.utc_microseconds() else nil end end # # :call-seq: # metric.sampling_stop_time -> Float or nil # # The time at which the last sample was taken # This is the number of microseconds since UNIX epoch UTC as a Float # # If the metric has not completely measured at least one thing then # stop time is nil. # # Because accessing the actual 'time of day' is an expesive operation, we # only get the time of day at the beginning of the first measurement and we # keep track of the offset from that point in @sampling_delta. # # When sampling_stop_time is called, the actual time of day is caculated. # def sampling_stop_time if @sampling_delta > 0 then (self.sampling_start_time + (@sampling_delta * 1_000_000)) else nil end end # # :call-seq: # metric.to_hash -> Hash # metric.to_hash # # Convert the metric to a Hash. # def to_hash { 'sampling_start_time' => self.sampling_start_time, 'sampling_stop_time' => self.sampling_stop_time, 'additional_data' => self.additional_data, 'name' => self.name } end # # :call-seq: # metric.utc_microseconds -> Float # # The current time in microseconds from the UNIX Epoch in the UTC # def utc_microseconds Time.now.gmtime.to_f * 1_000_000 end end end hitimes-1.3.1/lib/hitimes/mutexed_stats.rb000066400000000000000000000012261342040501500205770ustar00rootroot00000000000000#-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ require 'thread' module Hitimes # # MutexedStats is the start of a threadsafe Stats class. Currently, on MRI # Ruby the Stats object is already threadsafe, so there is no need to use # MutexedStats. # class MutexedStats < Stats def initialize @mutex = Mutex.new end # call-seq: # mutex_stat.update( val ) -> nil # # Update the running stats with the new value in a threadsafe manner. # def update( value ) @mutex.synchronize do super( value ) end end end end hitimes-1.3.1/lib/hitimes/paths.rb000066400000000000000000000030571342040501500170310ustar00rootroot00000000000000#-- # Copyright (c) 2008 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ # module Hitimes # # Access to various paths inside the project programatically # module Paths # # :call-seq: # Hitimes::Paths.root_dir -> String # # Returns The full expanded path of the parent directory of +lib+ # going up the path from the current file. A trailing File::SEPARATOR # is guaranteed. # def self.root_dir @root_dir ||=( path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR) lib_index = path_parts.rindex("lib") @root_dir = path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR ) end # # :call-seq: # Hitimes::Paths.lib_path( *args ) -> String # # Returns The full expanded path of the +lib+ directory below # _root_dir_. All parameters passed in are joined onto the # result. A trailing File::SEPARATOR is guaranteed if # _args_ are *not* present. # def self.lib_path(*args) self.sub_path("lib", *args) end # # :call-seq: # Hitimes::Paths.sub_path( sub, *args ) -> String # # Returns the full expanded path of the +sub+ directory below _root_dir. All # _arg_ parameters passed in are joined onto the result. A trailing # File::SEPARATOR is guaranteed if _args_ are *not* present. # def self.sub_path(sub,*args) sp = ::File.join(root_dir, sub) + File::SEPARATOR sp = ::File.join(sp, *args) if args end end end hitimes-1.3.1/lib/hitimes/stats.rb000066400000000000000000000027221342040501500170460ustar00rootroot00000000000000#-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ require 'stringio' module Hitimes class Stats # A list of the available stats STATS = %w[ count max mean min rate stddev sum sumsq ] # # call-seq: # stat.to_hash -> Hash # stat.to_hash( %w[ count max mean ]) -> Hash # # return a hash of the stats. By default this returns a hash of all stats # but passing in an array of items will limit the stats returned to only # those in the Array. # # If passed in an empty array or nil to to_hash then STATS is assumed to be # the list of stats to return in the hash. # def to_hash( *args ) h = {} args = [ args ].flatten args = STATS if args.empty? args.each do |meth| h[meth] = self.send( meth ) end return h end # # call-seq: # stat.to_json -> String # stat.to_json( *args ) -> String # # return a json string of the stats. By default this returns a json string # of all the stats. If an array of items is passed in, those that match the # known stats will be all that is included in the json output. # def to_json( *args ) h = to_hash( *args ) a = [] s = StringIO.new s.print "{ " h.each_pair do |k,v| a << "\"#{k}\": #{v}" end s.print a.join(", ") s.print "}" return s.string end end end hitimes-1.3.1/lib/hitimes/timed_metric.rb000066400000000000000000000111521342040501500203520ustar00rootroot00000000000000#-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ require 'forwardable' module Hitimes # # A TimedMetric holds the metrics on how long it takes to do something. For # example, measuring how long a method takes to operate. # # tm = TimedMetric.new( 'my-method' ) # # 200.times do # my_method_result = tm.measure do # my_method( ... ) # end # end # # puts "#{ tm.name } operated at a rate of #{ tm.rate } calls per second" # # Since TimedMetric is a child class of Metric make sure to look at the # Metric API also. # # A TimedMetric measures the execution time of an option with the Interval # class. # # A TimedMetric contains a Stats object, therefore TimedMetric has +count+, +max+, # +mean+, +min+, +rate+, +stddev+, +sum+, +sumsq+ methods that delegate to that Stats # object for convenience. # # class TimedMetric < Metric # holds all the statistics attr_reader :stats class << TimedMetric # # :call-seq: # TimedMetric.now -> TimedMetric # # Return a TimedMetric that has been started # def now( name, additional_data = {} ) t = TimedMetric.new( name, additional_data ) t.start return t end end # # :call-seq: # TimedMetric.new( 'name') -> TimedMetric # TimedMetric.new( 'name', 'other' => 'data') -> TimedMetric # # Create a new TimedMetric giving it a name and additional data. # +additional_data+ may be anything that follows the +to_hash+ protocol # def initialize( name, additional_data = {} ) super( name, additional_data ) @stats = Stats.new @current_interval = Interval.new end # # :call-seq: # timed_metric.running? -> true or false # # return whether or not the timer is currently running. # def running? @current_interval.running? end # # :call-seq: # timed_metric.start -> nil # # Start the current metric, if the current metric is already started, then # this is a noop. # def start if not @current_interval.running? then @current_interval.start @sampling_start_time ||= self.utc_microseconds() @sampling_start_interval ||= Interval.now end nil end # # :call-seq: # timed_metric.stop -> Float or nil # # Stop the current metric. This updates the stats and removes the current # interval. If the timer was stopped then the duration of the last Interval # is returned. If the timer was already stopped then false is returned and # no stats are updated. # def stop if @current_interval.running? then d = @current_interval.stop @stats.update( d ) @current_interval = Interval.new # update the length of time we have been sampling @sampling_delta = @sampling_start_interval.duration_so_far return d end return false end # # :call-seq: # timed_metric.measure { ... } -> Object # # Measure the execution of a block and add those stats to the running stats. # The return value is the return value of the block # def measure( &block ) return_value = nil begin start return_value = yield ensure stop end return return_value end # # :call-seq: # timed_metric.split -> Float # # Split the current TimedMetric. Essentially, mark a split time. This means # stop the current interval and create a new interval, but make sure # that the new interval lines up exactly, timewise, behind the previous # interval. # # If the timer is running, then split returns the duration of the previous # interval, i.e. the split-time. If the timer is not running, nothing # happens and false is returned. # def split if @current_interval.running? then next_interval = @current_interval.split d = @current_interval.duration @stats.update( d ) @current_interval = next_interval return d end return false end # # :call-seq: # metric.to_hash -> Hash # # Convert the metric to a hash # def to_hash h = super Stats::STATS.each do |s| h[s] = self.send( s ) end return h end # forward appropriate calls directly to the stats object extend Forwardable def_delegators :@stats, :count, :max, :mean, :min, :rate, :stddev, :sum, :sumsq alias :duration :sum end end hitimes-1.3.1/lib/hitimes/timed_value_metric.rb000066400000000000000000000154601342040501500215540ustar00rootroot00000000000000#-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ module Hitimes # # A TimedValueMetric holds the metrics on how long it takes to do a batch of something. # something. For measuring how long a method takes to operate on N items. # # tm = TimedValueMetric.new( 'my-batch-method' ) # # 42.times do # tm.start # number_of_items_processed = do_something # tm.stop( number_of_items_processed ) # end # # puts "#{ tm.name } operated at a rate of #{ tm.rate } calls per second" # # TimedValueMetric combines the usefulness of a ValueMetric and a TimedMetric. # The stats are available for both the time it took to do the operation and # the sizes of the batches that were run. # # A TimedValueMetric keeps track of both the time it took to do an operation # and the size of the batch that was operated on. These metrics are kept # separately as +timed_stats+ and +value_stats+ accessors. # class TimedValueMetric < Metric # holds all the Timed statistics attr_reader :timed_stats # holds all the Value statistics attr_reader :value_stats class << TimedValueMetric # # :call-seq: # TimedValueMetric.now( 'name' ) -> TimedValueMetric # # Return a TimedValueMetric that has been started # def now( name, additional_data = {} ) t = TimedValueMetric.new( name, additional_data ) t.start return t end end # # :call-seq: # TimedValueMetric.new( 'name') -> TimedValueMetric # TimedValueMetric.new( 'name', 'other' => 'data') -> TimedValueMetric # # Create a new TimedValueMetric giving it a name and additional data. # +additional_data+ may be anything that follows the +to_hash+ protocol # def initialize( name, additional_data = {} ) super( name, additional_data ) @timed_stats = Stats.new @value_stats = Stats.new @current_interval = Interval.new end # # :call-seq: # timed_value_metric.running? -> true or false # # return whether or not the metric is currently timing something. # def running? @current_interval.running? end # # :call-seq: # timed_value_metric.start -> nil # # Start the current timer, if the current timer is already started, then # this is a noop. # def start if not @current_interval.running? then @current_interval.start @sampling_start_time ||= self.utc_microseconds() @sampling_start_interval ||= Interval.now end nil end # # :call-seq: # timed_value_metric.stop( count ) -> Float or nil # # Stop the current metric. The +count+ parameter must be a # value to update to the _value_ portion of the TimedValueMetric. Generally # this is probably the number of things that were operated upon since # +start+ was invoked. # # This updates both the +value_stats+ and +timed_stats+ stats and removes # the current interval. If the metric is stopped then the duration of the # last Interval is returned. If the metric was already stopped before this # call, then false is returned and no stats are updated. # # def stop( value ) if @current_interval.running? then d = @current_interval.stop @timed_stats.update( d ) @current_interval = Interval.new @value_stats.update( value ) # update the lenght of time we have been sampling @sampling_delta = @sampling_start_interval.duration_so_far return d end return false end # # :call-seq: # timed_value_metric.measure( value ) { ... } -> Object # # Measure the execution of a block and add those stats to the running stats. # The return value is the return value of the block. A value must be passed # into +measure+ to update the +value_stats+ portion of the TimedValueMetric. # def measure( value, &block ) return_value = nil begin start return_value = yield ensure stop( value ) end return return_value end # # :call-seq: # timed_value_metric.split( value ) -> Float # # Split the current metric. Essentially, mark a split time. This means # stop the current interval, with the givein +value+ and create a new # interval, but make sure that the new interval lines up exactly, timewise, # behind the previous interval. # # If the metric is running, then split returns the duration of the previous # interval, i.e. the split-time. If the metric is not running, nothing # happens, no stats are updated, and false is returned. # # def split( value ) if @current_interval.running? then next_interval = @current_interval.split d = @current_interval.duration @timed_stats.update( d ) @value_stats.update( value ) @current_interval = next_interval return d end return false end # # :call-seq: # timed_value_metric.duration -> Float # # The duration of measured time from the metric. # def duration @timed_stats.sum end # # :call-seq: # timed_value_metric.unit_count -> Float # # The sum of all values passed to +stop+ or +skip+ or +measure+ # def unit_count @value_stats.sum end # # :call-seq: # timed_value_metric.rate -> Float # # Rate in the context of the TimedValueMetric is different than the # TimedMetric. In the TimedValueMetric, each measurement of time is # associated with a quantity of things done during that unit of time. So # the +rate+ for a TimedValueMetric is the (sum of all quantities sampled) / # ( sum of all durations measured ) # # For example, say you were measuring, using a TimedValueMetric batch jobs # that had individual units of work. # # tvm = TimedValueMetric.new( 'some-batch' ) # tvm.start # # process a batch of 12 units # duration1 = tvm.stop( 12 ) # # tvm.start # # process a larger batch of 42 units # duration2 = tvm.stop( 42 ) # # At this point the rate of units per second is calculated as ( 12 + 42 ) / ( duration1 + duration2 ) # # some_batch_rate = tvm.rate # returns ( 34 / ( duration1+duration2 ) ) # def rate @value_stats.sum / @timed_stats.sum end # # :call-seq: # metric.to_hash -> Hash # # Convert the metric to a hash # def to_hash h = super h['timed_stats'] = @timed_stats.to_hash h['value_stats'] = @value_stats.to_hash( Stats::STATS - %w[ rate ] ) h['rate'] = self.rate h['unit_count'] = self.unit_count return h end end end hitimes-1.3.1/lib/hitimes/value_metric.rb000066400000000000000000000037351342040501500203740ustar00rootroot00000000000000#-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ require 'forwardable' module Hitimes # # A ValueMetric holds the data from measuring a single value over a period of # time. In most cases this may be a single measurement at a single point in # time. # # A good example of a ValueMetric is measuring the number of items in a queue. # # A ValueMetric contains a Stats object, therefore ValueMetric has +count+, +max+, # +mean+, +min+, +stddev+, +sum+, +sumsq+ methods that delegate to that Stats # object for convenience. # class ValueMetric < Metric # holds all the statistics attr_reader :stats # # :call-seq: # ValueMetric.new( 'my_metric' ) -> ValueMetric # ValueMetric.new( 'my_metric', 'foo' => 'bar', 'this' => 42 ) -> ValueMetric # # Create a new ValueMetric giving it a name and additional data. # +additional_data+ may be anything that follows the +to_hash+ protocol. # def initialize( name, additional_data = {} ) super( name, additional_data ) @stats = Stats.new end # # :call-seq: # metric.measure( value ) -> Float # # Give the +value+ as the measurement to the metric. The value is returned # def measure( value ) @sampling_start_time ||= self.utc_microseconds() @sampling_start_interval ||= Interval.now @stats.update( value ) # update the length of time we have been sampling @sampling_delta = @sampling_start_interval.duration_so_far end # # :call-seq: # metric.to_hash -> Hash # # Convert the metric to a hash # def to_hash h = super (Stats::STATS - %w[ rate ]).each do |s| h[s] = self.send( s ) end return h end # forward appropriate calls directly to the stats object extend Forwardable def_delegators :@stats, :count, :max, :mean, :min, :stddev, :sum, :sumsq end end hitimes-1.3.1/lib/hitimes/version.rb000066400000000000000000000002271342040501500173730ustar00rootroot00000000000000#-- # Copyright (c) 2008 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details #++ module Hitimes VERSION = "1.3.1" end hitimes-1.3.1/spec/000077500000000000000000000000001342040501500141025ustar00rootroot00000000000000hitimes-1.3.1/spec/hitimes_spec.rb000066400000000000000000000010041342040501500170760ustar00rootroot00000000000000require 'spec_helper' describe Hitimes do it "can time a block of code" do d = Hitimes.measure do sleep 0.2 end d.must_be_close_to(0.2, 0.002) end it "raises an error if measure is called with no block" do lambda{ Hitimes.measure }.must_raise( Hitimes::Error ) end it "has the raw instant value" do v = Hitimes.raw_instant v.must_be :>, 0 end it "has access to the instant conversion factor" do f = Hitimes::INSTANT_CONVERSION_FACTOR f.must_be :>, 0 end end hitimes-1.3.1/spec/interval_spec.rb000066400000000000000000000063411342040501500172710ustar00rootroot00000000000000require "spec_helper" describe Hitimes::Interval do it "raises an error if duration is called on a non-started interval" do i = Hitimes::Interval.new lambda{ i.duration }.must_raise( Hitimes::Error, /\AAttempt to report a duration on an interval that has not started\Z/ ) end it "raises an error if stop is called on a non-started interval" do i = Hitimes::Interval.new lambda { i.stop }.must_raise( Hitimes::Error, /\AAttempt to stop an interval that has not started\Z/ ) end it "knows if it has been started" do i = Hitimes::Interval.new i.started?.must_equal false i.start i.started?.must_equal true end it "knows if it has been stopped" do i = Hitimes::Interval.new i.start i.stopped?.must_equal false i.stop i.stopped?.must_equal true end it "knows if it is currently running" do i = Hitimes::Interval.new i.running?.must_equal false i.start i.running?.must_equal true i.stop i.running?.must_equal false end it "can time a block of code" do d = Hitimes::Interval.measure do sleep 0.2 end d.must_be_close_to(0.2, 0.002) end it "raises an error if measure is called with no block" do lambda{ Hitimes::Interval.measure }.must_raise( Hitimes::Error, /\ANo block given to Interval.measure\Z/ ) end it "creates an interval via #now" do i = Hitimes::Interval.now i.started?.must_equal true i.stopped?.must_equal false end it "calling duration multiple times returns successivly grater durations" do i = Hitimes::Interval.new i.start y = i.duration z = i.duration z.must_be :>, y end it "calling start multiple times on has no effect after the first call" do i = Hitimes::Interval.new i.start.must_equal true x = i.start_instant i.start_instant.must_be :>, 0 i.start.must_equal false x.must_equal i.start_instant end it "returns the duration on the first call to stop" do i = Hitimes::Interval.now d = i.stop d.must_be_instance_of( Float ) end it "calling stop multiple times on has no effect after the first call" do i = Hitimes::Interval.new i.start.must_equal true i.stop x = i.stop_instant i.stop_instant.must_be :>, 0 i.stop.must_equal false x.must_equal i.stop_instant end it "duration does not change after stop is calledd" do i = Hitimes::Interval.new i.start x = i.stop y = i.duration i.stop.must_equal false z = i.duration x.must_equal y x.must_equal z y.must_equal z end it "can return how much time has elapsed from the start without stopping the interval" do i = Hitimes::Interval.new i.start x = i.duration_so_far i.running?.must_equal true y = i.duration_so_far i.stop x.must_be :<, y x.must_be :<, i.duration y.must_be :<, i.duration end describe "#split" do it "creates a new Interval object" do i = Hitimes::Interval.new i.start i2 = i.split i.object_id.wont_equal i2.object_id end it "with the stop instant equivialent to the previous Interval's start instant" do i = Hitimes::Interval.new i.start i2 = i.split i.stop_instant.must_equal i2.start_instant end end end hitimes-1.3.1/spec/metric_spec.rb000066400000000000000000000013461342040501500167300ustar00rootroot00000000000000require "spec_helper" describe Hitimes::Metric do before( :each ) do @metric = Hitimes::Metric.new( "testing" ) end it 'has a name' do @metric.name.must_equal "testing" end it "has associated data from initialization" do m = Hitimes::Metric.new( "more-data", 'foo' => 'bar', 'this' => 'that' ) m.additional_data['foo'].must_equal 'bar' m.additional_data['this'].must_equal 'that' m = Hitimes::Metric.new( "more-data", { 'foo' => 'bar', 'this' => 'that' } ) m.additional_data['foo'].must_equal 'bar' m.additional_data['this'].must_equal 'that' end it "initially has no sampling times" do @metric.sampling_start_time.must_be_nil @metric.sampling_stop_time.must_be_nil end end hitimes-1.3.1/spec/mutex_stats_spec.rb000066400000000000000000000016671342040501500200330ustar00rootroot00000000000000require 'spec_helper' describe Hitimes::MutexedStats do before( :each ) do @threads = 5 @iters = 10_000 @final_value = @threads * @iters end def run_with_scissors( stats, threads, iters ) spool = [] threads.times do |t| spool << Thread.new { iters.times{ stats.update( 1 ) } } end spool.each { |t| t.join } return stats end if (not defined? RUBY_ENGINE) or (RUBY_ENGINE == "ruby") then it "Hitimes::Stats is threadsafe" do stats = run_with_scissors( ::Hitimes::Stats.new, @threads, @iters ) stats.count.must_equal @final_value end else it "Hitimes::Stats is not threadsafe" do stats = run_with_scissors( ::Hitimes::Stats.new, @threads, @iters ) stats.count.wont_equal @final_value end end it "has a threadsafe update" do stats = run_with_scissors( ::Hitimes::MutexedStats.new, @threads, @iters ) stats.count.must_equal @final_value end end hitimes-1.3.1/spec/paths_spec.rb000066400000000000000000000006421342040501500165620ustar00rootroot00000000000000require 'spec_helper' describe Hitimes::Paths do it "can access the root dir of the project" do Hitimes::Paths.root_dir.must_equal File.expand_path( File.join( File.dirname( __FILE__ ), ".." ) ) + ::File::SEPARATOR end it "can access the lib path of the project" do Hitimes::Paths.lib_path.must_equal File.expand_path( File.join( File.dirname( __FILE__ ), "..", "lib" ) ) + ::File::SEPARATOR end end hitimes-1.3.1/spec/spec_helper.rb000066400000000000000000000003171342040501500167210ustar00rootroot00000000000000if RUBY_VERSION >= '1.9.2' then require 'simplecov' puts "Using coverage!" SimpleCov.start if ENV['COVERAGE'] end gem 'minitest' require 'hitimes' require 'minitest/autorun' require 'minitest/pride' hitimes-1.3.1/spec/stats_spec.rb000066400000000000000000000046641342040501500166110ustar00rootroot00000000000000require 'spec_helper' require 'json' describe Hitimes::Stats do before( :each ) do @stats = Hitimes::Stats.new @full_stats = Hitimes::Stats.new [ 1, 2, 3].each { |i| @full_stats.update( i ) } end it "is initialized with 0 values" do @stats.count.must_equal 0 @stats.min.must_equal 0.0 @stats.max.must_equal 0.0 @stats.sum.must_equal 0.0 @stats.rate.must_equal 0.0 end it "calculates the mean correctly" do @full_stats.mean.must_equal 2.0 end it "calculates the rate correctly" do @full_stats.rate.must_equal 0.5 end it "tracks the maximum value" do @full_stats.max.must_equal 3.0 end it "tracks the minimum value" do @full_stats.min.must_equal 1.0 end it "tracks the count" do @full_stats.count.must_equal 3 end it "tracks the sum" do @full_stats.sum.must_equal 6.0 end it "calculates the standard deviation" do @full_stats.stddev.must_equal 1.0 end it "calculates the sum of squares " do @full_stats.sumsq.must_equal 14.0 end describe "#to_hash " do it "converts to a Hash" do h = @full_stats.to_hash h.size.must_equal ::Hitimes::Stats::STATS.size h.keys.sort.must_equal ::Hitimes::Stats::STATS end it "converts to a limited Hash if given arguments" do h = @full_stats.to_hash( "min", "max", "mean" ) h.size.must_equal 3 h.keys.sort.must_equal %w[ max mean min ] h = @full_stats.to_hash( %w[ count rate ] ) h.size.must_equal 2 h.keys.sort.must_equal %w[ count rate ] end it "raises NoMethodError if an invalid stat is used" do lambda { @full_stats.to_hash( "wibble" ) }.must_raise( NoMethodError ) end end describe "#to_json" do it "converts to a json string" do j = @full_stats.to_json h = JSON.parse( j ) h.size.must_equal ::Hitimes::Stats::STATS.size h.keys.sort.must_equal ::Hitimes::Stats::STATS end it "converts to a limited Hash if given arguments" do j = @full_stats.to_json( "min", "max", "mean" ) h = JSON.parse( j ) h.size.must_equal 3 h.keys.sort.must_equal %w[ max mean min ] j = @full_stats.to_json( %w[ count rate ] ) h = JSON.parse( j ) h.size.must_equal 2 h.keys.sort.must_equal %w[ count rate ] end it "raises NoMethodError if an invalid stat is used" do lambda { @full_stats.to_json( "wibble" ) }.must_raise( NoMethodError ) end end end hitimes-1.3.1/spec/timed_metric_spec.rb000066400000000000000000000104701342040501500201100ustar00rootroot00000000000000require 'spec_helper' describe Hitimes::TimedMetric do before( :each ) do @tm = Hitimes::TimedMetric.new( 'test-timed-metric' ) end it "knows if it is running or not" do @tm.running?.must_equal false @tm.start @tm.running?.must_equal true @tm.stop @tm.running?.must_equal false end it "#split returns the last duration and the timer is still running" do @tm.start d = @tm.split @tm.running?.must_equal true d.must_be :>, 0 @tm.count.must_equal 1 @tm.duration.must_equal d end it "#stop returns false if called more than once in a row" do @tm.start @tm.stop.must_be :>, 0 @tm.stop.must_equal false end it "does not count a currently running interval as an interval in calculations" do @tm.start @tm.count.must_equal 0 @tm.split @tm.count.must_equal 1 end it "#split called on a stopped timer does nothing" do @tm.start @tm.stop @tm.split.must_equal false end it "calculates the mean of the durations" do 2.times { @tm.start ; sleep 0.05 ; @tm.stop } @tm.mean.must_be_close_to(0.05, 0.002) end it "calculates the rate of the counts " do 5.times { @tm.start ; sleep 0.05 ; @tm.stop } @tm.rate.must_be_close_to(20.00, 0.5) end it "calculates the stddev of the durations" do 3.times { |x| @tm.start ; sleep(0.05 * x) ; @tm.stop } @tm.stddev.must_be_close_to(0.05) end it "returns 0.0 for stddev if there is no data" do @tm.stddev.must_equal 0.0 end it "keeps track of the min value" do 2.times { @tm.start ; sleep 0.05 ; @tm.stop } @tm.min.must_be_close_to(0.05, 0.01) end it "keeps track of the max value" do 2.times { @tm.start ; sleep 0.05 ; @tm.stop } @tm.max.must_be_close_to(0.05, 0.01) end it "keeps track of the sum value" do 2.times { @tm.start ; sleep 0.05 ; @tm.stop } @tm.sum.must_be_close_to(0.10, 0.01) end it "keeps track of the sum of squars value" do 3.times { @tm.start ; sleep 0.05 ; @tm.stop } @tm.sumsq.must_be_close_to(0.0075) end it "keeps track of the minimum start time of all the intervals" do f1 = Time.now.gmtime.to_f * 1_000_000 5.times { @tm.start ; sleep 0.05 ; @tm.stop } f2 = Time.now.gmtime.to_f * 1_000_000 @tm.sampling_start_time.must_be :>=, f1 @tm.sampling_start_time.must_be :<, f2 # distance from now to start time should be greater than the distance from # the start to the min start_time (f2 - @tm.sampling_start_time).must_be :>, ( @tm.sampling_start_time - f1 ) end it "keeps track of the last stop time of all the intervals" do f1 = Time.now.gmtime.to_f * 1_000_000 sleep 0.01 5.times { @tm.start ; sleep 0.05 ; @tm.stop } sleep 0.01 f2 = Time.now.gmtime.to_f * 1_000_000 @tm.sampling_stop_time.must_be :>, f1 @tm.sampling_stop_time.must_be :<=, f2 # distance from now to max stop time time should be less than the distance # from the start to the max stop time (f2 - @tm.sampling_stop_time).must_be :<, ( @tm.sampling_stop_time - f1 ) end it "can create an already running timer" do t = Hitimes::TimedMetric.now( 'already-running' ) t.running?.must_equal true end it "can measure a block of code from an instance" do t = Hitimes::TimedMetric.new( 'measure a block' ) 3.times { t.measure { sleep 0.05 } } t.duration.must_be_close_to(0.15, 0.01) t.count.must_equal 3 end it "returns the value of the block when measuring" do t = Hitimes::TimedMetric.new( 'measure a block' ) x = t.measure { sleep 0.05; 42 } t.duration.must_be_close_to(0.05, 0.002) x.must_equal 42 end describe "#to_hash" do it "has name value" do h = @tm.to_hash h['name'].must_equal "test-timed-metric" end it "has an empty hash for additional_data" do h = @tm.to_hash h['additional_data'].must_equal Hash.new h['additional_data'].size.must_equal 0 end it "has the right sum" do 10.times { |x| @tm.measure { sleep 0.01*x } } h = @tm.to_hash h['sum'].must_be_close_to(0.45, 0.01) end fields = ::Hitimes::Stats::STATS.dup + %w[ name additional_data sampling_start_time sampling_stop_time ] fields.each do |f| it "has a value for #{f}" do @tm.measure { sleep 0.001 } h = @tm.to_hash h[f].wont_be_nil end end end end hitimes-1.3.1/spec/timed_value_metric_spec.rb000066400000000000000000000122761342040501500213120ustar00rootroot00000000000000require 'spec_helper' describe Hitimes::TimedValueMetric do before( :each ) do @tm = Hitimes::TimedValueMetric.new( 'test-timed-value-metric' ) end it "knows if it is running or not" do @tm.running?.must_equal false @tm.start @tm.running?.must_equal true @tm.stop( 1 ) @tm.running?.must_equal false end it "#split returns the last duration and the timer is still running" do @tm.start d = @tm.split( 1 ) @tm.running?.must_equal true d.must_be :>, 0 @tm.value_stats.count.must_equal 1 @tm.timed_stats.count.must_equal 1 @tm.duration.must_equal d end it "#stop returns false if called more than once in a row" do @tm.start @tm.stop( 1 ).must_be :>, 0 @tm.stop( 1 ).must_equal false end it "does not count a currently running interval as an interval in calculations" do @tm.start @tm.value_stats.count.must_equal 0 @tm.timed_stats.count.must_equal 0 @tm.split( 1 ) @tm.value_stats.count.must_equal 1 @tm.timed_stats.count.must_equal 1 end it "#split called on a stopped timer does nothing" do @tm.start @tm.stop( 1 ) @tm.split( 1 ).must_equal false end it "calculates the mean of the durations" do 3.times { |x| @tm.start ; sleep 0.05 ; @tm.stop(x) } @tm.timed_stats.mean.must_be_close_to(0.05, 0.01) @tm.value_stats.mean.must_equal 1.00 end it "calculates the rate of the counts " do 5.times { |x| @tm.start ; sleep 0.05 ; @tm.stop( x ) } @tm.rate.must_be_close_to(40.0, 1.0) end it "calculates the stddev of the durations" do 3.times { |x| @tm.start ; sleep(0.05 * x) ; @tm.stop(x) } @tm.timed_stats.stddev.must_be_close_to(0.05, 0.001) @tm.value_stats.stddev.must_equal 1.0 end it "returns 0.0 for stddev if there is no data" do @tm.timed_stats.stddev.must_equal 0.0 @tm.value_stats.stddev.must_equal 0.0 end it "keeps track of the min value" do 3.times { |x| @tm.start ; sleep 0.05 ; @tm.stop( x ) } @tm.timed_stats.min.must_be_close_to( 0.05, 0.003 ) @tm.value_stats.min.must_equal 0 end it "keeps track of the max value" do 3.times { |x| @tm.start ; sleep 0.05 ; @tm.stop( x ) } @tm.timed_stats.max.must_be_close_to( 0.05, 0.003 ) @tm.value_stats.max.must_equal 2 end it "keeps track of the sum value" do 3.times { |x| @tm.start ; sleep 0.05 ; @tm.stop( x ) } @tm.timed_stats.sum.must_be_close_to( 0.15, 0.01 ) @tm.value_stats.sum.must_equal 3 end it "keeps track of the sum of squares value" do 3.times { |x| @tm.start ; sleep 0.05 ; @tm.stop( x ) } @tm.timed_stats.sumsq.must_be_close_to(0.0075, 0.0005) @tm.value_stats.sumsq.must_equal 5 end it "keeps track of the minimum start time of all the intervals" do f1 = Time.now.gmtime.to_f * 1000000 5.times { @tm.start ; sleep 0.05 ; @tm.stop( 1 ) } f2 = Time.now.gmtime.to_f * 1000000 @tm.sampling_start_time.must_be :>=, f1 @tm.sampling_start_time.must_be :<, f2 # distance from now to start time should be greater than the distance from # the start to the min start_time (f2 - @tm.sampling_start_time).must_be :>, ( @tm.sampling_start_time - f1 ) end it "keeps track of the last stop time of all the intervals" do f1 = Time.now.gmtime.to_f * 1_000_000 5.times { @tm.start ; sleep 0.05 ; @tm.stop( 1 ) } sleep 0.05 f2 = Time.now.gmtime.to_f * 1_000_000 @tm.sampling_stop_time.must_be :>, f1 @tm.sampling_stop_time.must_be :<=, f2 # distance from now to max stop time time should be less than the distance # from the start to the max stop time (f2 - @tm.sampling_stop_time).must_be :<, ( @tm.sampling_stop_time - f1 ) end it "can create an already running timer" do t = Hitimes::TimedValueMetric.now( 'already-running' ) t.running?.must_equal true end it "can measure a block of code from an instance" do t = Hitimes::TimedValueMetric.new( 'measure a block' ) 3.times { t.measure( 1 ) { sleep 0.05 } } t.duration.must_be_close_to(0.15, 0.004) t.timed_stats.count.must_equal 3 t.value_stats.count.must_equal 3 end it "returns the value of the block when measuring" do t = Hitimes::TimedValueMetric.new( 'measure a block' ) x = t.measure( 42 ) { sleep 0.05; 42 } t.duration.must_be_close_to(0.05, 0.002) x.must_equal 42 end describe "#to_hash" do it "has name value" do h = @tm.to_hash h['name'].must_equal "test-timed-value-metric" end it "has an empty has for additional_data" do h = @tm.to_hash h['additional_data'].must_equal Hash.new h['additional_data'].size.must_equal 0 end it "has a rate" do 5.times { |x| @tm.start ; sleep 0.05 ; @tm.stop( x ) } h = @tm.to_hash h['rate'].must_be_close_to(40.0, 1.0) end it "has a unit_count" do 5.times { |x| @tm.start ; sleep 0.05 ; @tm.stop( x ) } h = @tm.to_hash h['unit_count'].must_equal 10 end fields = %w[ name additional_data sampling_start_time sampling_stop_time value_stats timed_stats rate unit_count ] fields.each do |f| it "has a value for #{f}" do 3.times { |x| @tm.measure(x) { sleep 0.001 } } h = @tm.to_hash h[f].wont_be_nil end end end end hitimes-1.3.1/spec/value_metric_spec.rb000066400000000000000000000061321342040501500201220ustar00rootroot00000000000000require 'spec_helper' describe Hitimes::ValueMetric do before( :each ) do @metric = Hitimes::ValueMetric.new( "testing" ) 10.times { |x| @metric.measure( x ) } end it 'has a name' do @metric.name.must_equal "testing" end it "has associated data from initialization" do m = Hitimes::ValueMetric.new( "more-data", 'foo' => 'bar', 'this' => 'that' ) m.additional_data['foo'].must_equal 'bar' m.additional_data['this'].must_equal 'that' m = Hitimes::ValueMetric.new( "more-data", { 'foo' => 'bar', 'this' => 'that' } ) m.additional_data['foo'].must_equal 'bar' m.additional_data['this'].must_equal 'that' end it "calculates the mean of the measurements" do @metric.mean.must_equal 4.5 end it "calculates the stddev of the measurements" do @metric.stddev.must_be :>, 0.0 end it "returns 0.0 for stddev if there is no data" do m = Hitimes::ValueMetric.new('0-data') m.stddev.must_equal 0.0 end it "keeps track of the sum of data" do @metric.sum.must_equal 45.0 end it "keeps track of the sum of squars of data" do @metric.sumsq.must_equal 285.0 end it "retuns 0.0 for mean if there is no data" do Hitimes::ValueMetric.new('0-data').mean.must_equal 0.0 end it "keeps track of the min value" do @metric.min.must_equal 0 end it "keeps track of the max value" do @metric.max.must_equal 9 end it "keeps track of the first start time of all the measurements" do m = Hitimes::ValueMetric.new( "first-start-time" ) f1 = Time.now.gmtime.to_f * 1_000_000 10.times{ |x| m.measure( x ); sleep 0.1 } f2 = Time.now.gmtime.to_f * 1_000_000 m.sampling_start_time.must_be :>=, f1 m.sampling_start_time.must_be :<, f2 # distance from now to start time should be greater than the distance from # the start to the min start_time (f2 - m.sampling_start_time).must_be :>, ( m.sampling_start_time - f1 ) end it "keeps track of the last stop time of all the intervals" do m = Hitimes::ValueMetric.new( "last-stop-time" ) f1 = Time.now.gmtime.to_f * 1_000_000 10.times {|x| m.measure( x ); sleep 0.1 } f2 = Time.now.gmtime.to_f * 1_000_000 m.sampling_stop_time.must_be :>, f1 m.sampling_stop_time.must_be :<=, f2 # distance from now to max stop time time should be less than the distance # from the start to the max stop time (f2 - m.sampling_stop_time).must_be :<, ( m.sampling_stop_time - f1 ) end describe "#to_hash" do it "has name value" do h = @metric.to_hash h['name'].must_equal "testing" end it "has an empty has for additional_data" do h = @metric.to_hash h['additional_data'].must_equal Hash.new h['additional_data'].size.must_equal 0 end it "has the right sum" do h = @metric.to_hash h['sum'].must_equal 45 end fields = ::Hitimes::Stats::STATS.dup + %w[ name additional_data sampling_start_time sampling_stop_time ] fields = fields - [ 'rate' ] fields.each do |f| it "has a value for #{f}" do h = @metric.to_hash h[f].wont_be_nil end end end end hitimes-1.3.1/spec/version_spec.rb000066400000000000000000000002361342040501500171270ustar00rootroot00000000000000require "spec_helper" describe "Hitimes::Version" do it "should be accessable as a constant" do Hitimes::VERSION.must_match(/\d+\.\d+\.\d+/) end end hitimes-1.3.1/tasks/000077500000000000000000000000001342040501500142755ustar00rootroot00000000000000hitimes-1.3.1/tasks/default.rake000066400000000000000000000174501342040501500165740ustar00rootroot00000000000000# vim: syntax=ruby require 'rake/clean' require 'digest' #------------------------------------------------------------------------------ # If you want to Develop on this project just run 'rake develop' and you'll # have all you need to get going. If you want to use bundler for development, # then run 'rake develop:using_bundler' #------------------------------------------------------------------------------ namespace :develop do # Install all the development and runtime dependencies of this gem using the # gemspec. task :default => 'Gemfile' do require 'rubygems/dependency_installer' installer = ::Gem::DependencyInstaller.new puts "Installing bundler..." installer.install 'bundler' sh 'bundle install' puts "\n\nNow run 'rake test'" end # Create a Gemfile that just references the gemspec file 'Gemfile' => :gemspec do File.open( "Gemfile", "w+" ) do |f| f.puts "# DO NOT EDIT - This file is automatically generated" f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate" f.puts 'source "https://rubygems.org"' f.puts 'gemspec' end end end desc "Bootstrap development" task :develop => "develop:default" #------------------------------------------------------------------------------ # Minitest - standard TestTask #------------------------------------------------------------------------------ begin require 'rake/testtask' Rake::TestTask.new( :test ) do |t| t.ruby_opts = %w[ -w ] t.libs = %w[ lib spec test ] t.pattern = "{test,spec}/**/{test_*,*_spec}.rb" end task :test_requirements task :test => :test_requirements task :default => :test rescue LoadError This.task_warning( 'test' ) end #------------------------------------------------------------------------------ # RDoc - standard rdoc rake task, although we must make sure to use a more # recent version of rdoc since it is the one that has 'tomdoc' markup #------------------------------------------------------------------------------ begin gem 'rdoc' # otherwise we get the wrong task from stdlib require 'rdoc/task' RDoc::Task.new do |t| t.markup = 'tomdoc' t.rdoc_dir = 'doc' t.main = 'README.md' t.title = "#{This.name} #{This.version}" t.rdoc_files.include( FileList['*.{rdoc,md,txt}'], FileList['ext/**/*.c'], FileList['lib/**/*.rb'] ) end rescue StandardError, LoadError This.task_warning( 'rdoc' ) end #------------------------------------------------------------------------------ # Coverage - optional code coverage, rcov for 1.8 and simplecov for 1.9, so # for the moment only rcov is listed. #------------------------------------------------------------------------------ begin require 'simplecov' desc 'Run tests with code coverage' task :coverage do ENV['COVERAGE'] = 'true' Rake::Task[:test].execute end CLOBBER << 'coverage' if File.directory?( 'coverage' ) rescue LoadError This.task_warning( 'simplecov' ) end #------------------------------------------------------------------------------ # Manifest - We want an explicit list of thos files that are to be packaged in # the gem. Most of this is from Hoe. #------------------------------------------------------------------------------ namespace 'manifest' do desc "Check the manifest" task :check => :clean do files = FileList["**/*", ".*"].exclude( This.exclude_from_manifest ).to_a.sort files = files.select{ |f| File.file?( f ) } tmp = "Manifest.tmp" File.open( tmp, 'w' ) do |f| f.puts files.join("\n") end begin sh "diff -du Manifest.txt #{tmp}" ensure rm tmp end puts "Manifest looks good" end desc "Generate the manifest" task :generate => :clean do files = %x[ git ls-files ].split("\n").sort files.reject! { |f| f =~ This.exclude_from_manifest } File.open( "Manifest.txt", "w" ) do |f| f.puts files.join("\n") end end end #------------------------------------------------------------------------------ # Fixme - look for fixmes and report them #------------------------------------------------------------------------------ namespace :fixme do task :default => 'manifest:check' do This.manifest.each do |file| next if file == __FILE__ next unless file =~ %r/(txt|rb|md|rdoc|css|html|xml|css)\Z/ puts "FIXME: Rename #{file}" if file =~ /fixme/i IO.readlines( file ).each_with_index do |line, idx| prefix = "FIXME: #{file}:#{idx+1}".ljust(42) puts "#{prefix} => #{line.strip}" if line =~ /fixme/i end end end def fixme_project_root This.project_path( '../fixme' ) end def fixme_project_path( subtree ) fixme_project_root.join( subtree ) end def local_fixme_files This.manifest.select { |p| p =~ %r|^tasks/| } end def outdated_fixme_files local_fixme_files.select do |local| upstream = fixme_project_path( local ) upstream.exist? && ( Digest::SHA256.file( local ) != Digest::SHA256.file( upstream ) ) end end def fixme_up_to_date? outdated_fixme_files.empty? end desc "See if the fixme tools are outdated" task :outdated => :release_check do if fixme_up_to_date? then puts "Fixme files are up to date." else outdated_fixme_files.each do |f| puts "#{f} is outdated" end end end desc "Update outdated fixme files" task :update => :release_check do if fixme_up_to_date? then puts "Fixme files are already up to date." else puts "Updating fixme files:" outdated_fixme_files.each do |local| upstream = fixme_project_path( local ) puts " * #{local}" FileUtils.cp( upstream, local ) end puts "Use your git commands as appropriate." end end end desc "Look for fixmes and report them" task :fixme => "fixme:default" #------------------------------------------------------------------------------ # Gem Specification #------------------------------------------------------------------------------ # Really this is only here to support those who use bundler desc "Build the #{This.name}.gemspec file" task :gemspec do File.open( This.gemspec_file, "wb+" ) do |f| f.puts "# DO NOT EDIT - This file is automatically generated" f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate" f.write This.platform_gemspec.to_ruby end end # .rbc files from ruby 2.0 CLOBBER << FileList["**/*.rbc"] # The standard gem packaging task, everyone has it. require 'rubygems/package_task' ::Gem::PackageTask.new( This.platform_gemspec ) do # nothing end #------------------------------------------------------------------------------ # Release - the steps we go through to do a final release, this is pulled from # a compbination of mojombo's rakegem, hoe and hoe-git # # 1) make sure we are on the master branch # 2) make sure there are no uncommitted items # 3) check the manifest and make sure all looks good # 4) build the gem # 5) do an empty commit to have the commit message of the version # 6) tag that commit as the version # 7) push master # 8) push the tag # 7) pus the gem #------------------------------------------------------------------------------ task :release_check do unless `git branch` =~ /^\* master$/ abort "You must be on the master branch to release!" end unless `git status` =~ /^nothing to commit/m abort "Nope, sorry, you have unfinished business" end end desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_name} to rubygems.org" task :release => [ :release_check, 'manifest:check', :gem ] do sh "git commit --allow-empty -a -m 'Release #{This.version}'" sh "git tag -a -m 'v#{This.version}' v#{This.version}" sh "git push origin master" sh "git push origin v#{This.version}" sh "gem push pkg/#{This.platform_gemspec.full_name}.gem" end hitimes-1.3.1/tasks/extension.rake000066400000000000000000000023641342040501500171620ustar00rootroot00000000000000# To be used if the gem has extensions. # If this task set is inclueded then you will need to also have # # spec.add_development_dependency( 'rake-compiler', '~> 0.8.1' ) # # in your top level rakefile begin require 'rake/extensiontask' require 'rake/javaextensiontask' if RUBY_PLATFORM == "java" then Rake::JavaExtensionTask.new( This.name) do |ext| ext.ext_dir = File.join( 'ext', This.name, "java" ) ext.lib_dir = File.join( 'lib', This.name ) ext.gem_spec = This.java_gemspec end else Rake::ExtensionTask.new( This.name ) do |ext| ext.ext_dir = File.join( 'ext', This.name, "c" ) ext.lib_dir = File.join( 'lib', This.name ) ext.gem_spec = This.ruby_gemspec ext.cross_compile = true # enable cross compilation (requires cross compile toolchain) ext.cross_platform = %w[x86-mingw32 x64-mingw32] # forces the Windows platform instead of the default one # configure options only for cross compile end end task :test_requirements => :compile rescue LoadError This.task_warning( 'extension' ) end CLOBBER << FileList["lib/**/*.{jar,so,bundle}"] CLOBBER << FileList["lib/#{This.name}/{1,2}.*/"] hitimes-1.3.1/tasks/this.rb000066400000000000000000000137031342040501500155750ustar00rootroot00000000000000require 'pathname' # Public: A Class containing all the metadata and utilities needed to manage a # ruby project. class ThisProject # The name of this project attr_accessor :name # The author's name attr_accessor :author # The email address of the author(s) attr_accessor :email # The homepage of this project attr_accessor :homepage # The regex of files to exclude from the manifest attr_accessor :exclude_from_manifest # The hash of Gem::Specifications keyed' by platform attr_accessor :gemspecs # Public: Initialize ThisProject # # Yields self def initialize(&block) @exclude_from_manifest = Regexp.union(/\.(git|DS_Store)/, /^(doc|coverage|pkg|tmp|Gemfile(\.lock)?)/, /^[^\/]+\.gemspec/, /\.(swp|jar|bundle|so|rvmrc|travis.yml|byebug_history)$/, /~$/) @gemspecs = Hash.new yield self if block_given? end # Public: return the version of ThisProject # # Search the ruby files in the project looking for the one that has the # version string in it. This does not eval any code in the project, it parses # the source code looking for the string. # # Returns a String version def version [ "lib/#{ name }.rb", "lib/#{ name }/version.rb" ].each do |v| path = project_path( v ) line = path.read[/^\s*VERSION\s*=\s*.*/] if line then return line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] end end end # Internal: Return a section of an RDoc file with the given section name # # path - the relative path in the project of the file to parse # section_name - the section out of the file from which to parse data # # Retuns the text of the section as an array of paragrphs. def section_of( file, section_name ) re = /^[=#]+ (.*)$/ sectional = project_path( file ) parts = sectional.read.split( re )[1..-1] parts.map! { |p| p.strip } sections = Hash.new Hash[*parts].each do |k,v| sections[k] = v.split("\n\n") end return sections[section_name] end # Internal: print out a warning about the give task def task_warning( task ) warn "WARNING: '#{task}' tasks are not defined. Please run 'rake develop'" end # Internal: Return the full path to the file that is relative to the project # root. # # path - the relative path of the file from the project root # # Returns the Pathname of the file def project_path( *relative_path ) project_root.join( *relative_path ) end # Internal: The absolute path of this file # # Returns the Pathname of this file. def this_file_path Pathname.new( __FILE__ ).expand_path end # Internal: The root directory of this project # # This is defined as being the directory that is in the path of this project # that has the first Rakefile # # Returns the Pathname of the directory def project_root this_file_path.ascend do |p| rakefile = p.join( 'Rakefile' ) return p if rakefile.exist? end end # Internal: Returns the contents of the Manifest.txt file as an array # # Returns an Array of strings def manifest manifest_file = project_path( "Manifest.txt" ) abort "You need a Manifest.txt" unless manifest_file.readable? manifest_file.readlines.map { |l| l.strip } end # Internal: Return the files that define the extensions # # Returns an Array def extension_conf_files manifest.grep( /extconf.rb\Z/ ) end # Internal: Returns the gemspace associated with the current ruby platform def platform_gemspec gemspecs.fetch(platform) { This.ruby_gemspec } end def core_gemspec Gem::Specification.new do |spec| spec.name = name spec.version = version spec.author = author spec.email = email spec.homepage = homepage spec.summary = summary spec.description = description spec.license = license spec.files = manifest spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) } spec.test_files = spec.files.grep(/^spec/) spec.extra_rdoc_files += spec.files.grep(/(txt|rdoc|md)$/) spec.rdoc_options = [ "--main" , 'README.md', "--markup", "tomdoc" ] spec.required_ruby_version = '>= 2.2.2' end end # Internal: Return the gemspec for the ruby platform def ruby_gemspec( core = core_gemspec, &block ) yielding_gemspec( 'ruby', core, &block ) end # Internal: Return the gemspec for the jruby platform def java_gemspec( core = core_gemspec, &block ) yielding_gemspec( 'java', core, &block ) end # Internal: give an initial spec and a key, create a new gemspec based off of # it. # # This will force the new gemspecs 'platform' to be that of the key, since the # only reason you would have multiple gemspecs at this point is to deal with # different platforms. def yielding_gemspec( key, core ) spec = gemspecs[key] ||= core.dup spec.platform = key yield spec if block_given? return spec end # Internal: Return the platform of ThisProject at the current moment in time. def platform (RUBY_PLATFORM == "java") ? 'java' : Gem::Platform::RUBY end # Internal: Return the Description section of the README.rdoc file def description_section section_of( 'README.md', 'Description') end # Internal: Return the summary text from the README def summary description_section.first end # Internal: Return the full description text from the README def description description_section.join(" ").tr("\n", ' ').gsub(/[{}]/,'').gsub(/\[[^\]]+\]/,'') # strip rdoc end def license license_file = project_path("LICENSE") line = license_file.readlines.first line.split(/\s+/).first end # Internal: The path to the gemspec file def gemspec_file project_path( "#{ name }.gemspec" ) end end This = ThisProject.new