Permalink
Browse files

Initial release

  • Loading branch information...
dim committed Jan 29, 2012
0 parents commit 447886ed916ba8cd7950a10fb5fb7889c67c9d2e
Showing with 273 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +1 −0 .rspec
  3. +3 −0 Gemfile
  4. +30 −0 Gemfile.lock
  5. +11 −0 Rakefile
  6. +23 −0 benchmark_ext.gemspec
  7. +51 −0 ext/benchmark_ext/benchmark_ext.c
  8. +9 −0 ext/benchmark_ext/extconf.rb
  9. +22 −0 lib/benchmark_ext.rb
  10. +90 −0 spec/benchmark_ext_spec.rb
  11. +26 −0 spec/setup_spec.rb
  12. +5 −0 spec/spec_helper.rb
@@ -0,0 +1,2 @@
+tmp/
+*.so
1 .rspec
@@ -0,0 +1 @@
+--colour
@@ -0,0 +1,3 @@
+source :rubygems
+
+gemspec
@@ -0,0 +1,30 @@
+PATH
+ remote: .
+ specs:
+ benchmark_ext (0.2.0)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ diff-lcs (1.1.3)
+ rake (0.9.2.2)
+ rake-compiler (0.7.9)
+ rake
+ rspec (2.8.0)
+ rspec-core (~> 2.8.0)
+ rspec-expectations (~> 2.8.0)
+ rspec-mocks (~> 2.8.0)
+ rspec-core (2.8.0)
+ rspec-expectations (2.8.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.8.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ benchmark_ext!
+ bundler
+ rake
+ rake-compiler
+ rspec
@@ -0,0 +1,11 @@
+require "rake"
+require "rake/clean"
+require 'rake/extensiontask'
+require 'rbconfig'
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec)
+Rake::ExtensionTask.new('benchmark_ext')
+
+desc 'Default: compile and run specs.'
+task :default => [:compile, :spec]
@@ -0,0 +1,23 @@
+Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.required_ruby_version = '>= 1.9.2'
+ s.required_rubygems_version = ">= 1.3.6"
+
+ s.name = "benchmark_ext"
+ s.summary = "Benchmark everything, just faster!"
+ s.description = "Native implementation of Benchmark with much better performance."
+ s.version = '0.2.0'
+
+ s.authors = ["Dimitrij Denissenko"]
+ s.email = "dimitrij@blacksquaremedia.com"
+ s.homepage = "https://github.com/bsm/benchmark_ext"
+
+ s.require_path = 'lib'
+ s.files = Dir['ext/**/*']
+ s.extensions << 'ext/benchmark_ext/extconf.rb'
+
+ s.add_development_dependency "rake"
+ s.add_development_dependency "rake-compiler"
+ s.add_development_dependency "bundler"
+ s.add_development_dependency "rspec"
+end
@@ -0,0 +1,51 @@
+#include <ruby.h>
+
+VALUE bmm;
+
+long bm_timestamp()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+double bm_tms_value(VALUE tms, const char *name)
+{
+ return NUM2DBL(rb_funcall(tms, rb_intern(name), 0));
+}
+
+/* call-seq:
+ timestamp -> Float
+
+ Returns the current UNIX timestamp, as a Float.
+*/
+VALUE bmm_timestamp(VALUE obj) {
+ return rb_float_new((double) bm_timestamp() / 1000000);
+}
+
+/* call-seq:
+ realtime {block} -> Float
+
+ Returns the elapsed real time used to execute the given block.
+*/
+VALUE bmm_realtime(VALUE obj)
+{
+ long t1, t2;
+
+ if (!rb_block_given_p())
+ {
+ rb_raise(rb_eArgError, "A block must be provided.");
+ }
+
+ t1 = bm_timestamp();
+ rb_yield(obj);
+ t2 = bm_timestamp();
+ return rb_float_new((double) (t2 - t1) / 1000000);
+}
+
+void Init_benchmark_ext() {
+ rb_require("benchmark");
+ bmm = rb_define_module("Benchmark");
+ rb_define_module_function(bmm, "timestamp", bmm_timestamp, 0);
+ rb_define_module_function(bmm, "realtime", bmm_realtime, 0);
+}
@@ -0,0 +1,9 @@
+require 'mkmf'
+
+$CFLAGS << " -DRUBY19" if RUBY_VERSION >= '1.9.0'
+$CFLAGS << " -DRUBY186" if RUBY_VERSION < '1.8.7'
+$CFLAGS << " -Wall " unless RUBY_PLATFORM =~ /solaris/
+$CFLAGS << ' -g -ggdb -rdynamic -O0 -DDEBUG' if ENV['DEBUG']
+$CFLAGS << " -Wconversion -Wsign-compare -Wno-unused-parameter -Wwrite-strings -Wpointer-arith -fno-common -pedantic -Wno-long-long" if ENV['STRICT']
+$CFLAGS << (ENV['CFLAGS'] || '')
+create_makefile("benchmark_ext")
@@ -0,0 +1,22 @@
+require 'benchmark_ext.so'
+
+module Benchmark
+
+ #
+ # Returns the time used to execute the given block as a
+ # Benchmark::Tms object.
+ #
+ def measure(label = "", &block) # :yield:
+ t0 = Process.times
+ rl = realtime(&block)
+ t1 = Process.times
+ Benchmark::Tms.new(t1.utime - t0.utime,
+ t1.stime - t0.stime,
+ t1.cutime - t0.cutime,
+ t1.cstime - t0.cstime,
+ rl,
+ label)
+ end
+ module_function :measure
+
+end
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe Benchmark do
+
+ subject do
+ described_class
+ end
+
+ let :native_instance do
+ klass = Class.new
+ klass.send :include, described_class
+ klass.new
+ end
+
+ let :ruby_instance do
+ klass = Class.new
+ klass.send :include, RubyBenchmark
+ klass.new
+ end
+
+ def meter(cycles, &block)
+ described_class.realtime { cycles.times(&block) }
+ end
+
+ it { should respond_to(:timestamp) }
+ it { should respond_to(:realtime) }
+
+ it 'should allow module inclusion of native implementation' do
+ native_instance.private_methods.should include(:realtime)
+ ruby_instance.private_methods.should include(:realtime)
+
+ native = meter(10_000) { native_instance.send(:realtime) { nil } }
+ ruby = meter(10_000) { ruby_instance.send(:realtime) { nil } }
+ native.should < ruby
+ (ruby / native).should > 5
+ end
+
+ describe "timestamp" do
+
+ it 'should return a timestamp' do
+ ts = described_class.timestamp
+ ts.should be_instance_of(Float)
+ now = Time.now.to_f
+ ts.should < now
+ ts.should > (now - 0.1)
+ end
+
+ end
+
+ describe "realtime" do
+
+ it 'should require a block' do
+ lambda {
+ described_class.realtime
+ }.should raise_error(ArgumentError, "A block must be provided.")
+ end
+
+ it 'should calculate the realtime' do
+ ms = described_class.realtime { sleep(0.001) }
+ ms.should be_instance_of(Float)
+ ms.should >= 0.001
+ ms.should < 0.0015
+ end
+
+ it 'should be faster than the pure-ruby implementation' do
+ native = meter(10_000) { described_class.realtime { } }
+ ruby = meter(10_000) { RubyBenchmark.realtime { } }
+ native.should < ruby
+ (ruby / native).should > 2
+ end
+
+ end
+
+ describe "measure" do
+
+ it 'should return a measurement' do
+ tms = described_class.measure { sleep(0.001) }
+ tms.should be_instance_of(Benchmark::Tms)
+ tms.real.should >= 0.001
+ tms.real.should < 0.0015
+ end
+
+ it 'should use native realtime' do
+ Time.should_not_receive(:now)
+ described_class.measure {}
+ end
+
+ end
+
+end
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe "sanity checks" do
+
+ describe 'ruby benchmark' do
+
+ it 'should use Time to calculate intervals' do
+ Time.should_receive(:now).and_return(Time.at(0))
+ Time.should_receive(:now).and_return(Time.at(3))
+ ms = RubyBenchmark.realtime {}
+ ms.should == 3.0
+ end
+
+ end
+
+ describe 'native benchmark' do
+
+ it 'should not use Time' do
+ Time.should_not_receive(:now)
+ ms = Benchmark.realtime {}
+ ms.should < 0.1
+ end
+
+ end
+
+end
@@ -0,0 +1,5 @@
+require 'benchmark'
+RubyBenchmark = ::Benchmark.dup
+
+require 'benchmark_ext'
+require 'rspec'

0 comments on commit 447886e

Please sign in to comment.