diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..245afcac --- /dev/null +++ b/.editorconfig @@ -0,0 +1,41 @@ +root = true + +[*] +end_of_line = lf +indent_style = space +insert_final_newline = true +tab_width = 8 +trim_trailing_whitespace = true + +# Build system + +[{Gemfile,Rakefile,*.gemspec}] +indent_size = 2 + +[depend] +indent_style = tab + +[*.sh] +indent_size = 2 + +# Product Code + +[*.rb] +indent_size = 2 + +[*.rl] +indent_size = 4 + +[*.java] +indent_size = 4 + +[*.{c,h}] +indent_size = 4 + +# Data + +[*.yml] +indent_size = 2 + +[*.json] +indent_size = 4 diff --git a/.gitignore b/.gitignore index 274cae36..c0e432d2 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,10 @@ Gemfile.lock .rbx .AppleDouble .DS_Store -*/**/Makefile -*/**/*.o -*/**/*.class -*/**/*.jar +Makefile +*.o +*.so +*.class +*.jar .byebug_history *.log diff --git a/Gemfile b/Gemfile index 30e1fce1..3f071791 100644 --- a/Gemfile +++ b/Gemfile @@ -18,3 +18,9 @@ gem "test-unit" gem "test-unit-ruby-core" gem "all_images", "~> 0" unless RUBY_PLATFORM =~ /java/ gem "benchmark-ips" + +group :benchmark, optional: true do + gem "bullshit", "~> 0.1.2" + gem "activesupport", "~> 7.1" + gem "yajl-ruby", "~> 1.4" +end diff --git a/README.md b/README.md index ce3c2457..26be0df4 100644 --- a/README.md +++ b/README.md @@ -260,124 +260,86 @@ JavaScript prototype library http://www.prototypejs.org works. ## Speed Comparisons -I have created some benchmark results (see the benchmarks/data-p4-3Ghz -subdir of the package) for the JSON-parser to estimate the speed up in the C +I have created some benchmark results (see the `benchmarks/` subdir of +the package) for the JSON-parser to estimate the speed up in the C extension: -``` - Comparing times (call_time_mean): - 1 ParserBenchmarkExt#parser 900 repeats: - 553.922304770 ( real) -> 21.500x - 0.001805307 - 2 ParserBenchmarkYAML#parser 1000 repeats: - 224.513358139 ( real) -> 8.714x - 0.004454078 - 3 ParserBenchmarkPure#parser 1000 repeats: - 26.755020642 ( real) -> 1.038x - 0.037376163 - 4 ParserBenchmarkRails#parser 1000 repeats: - 25.763381731 ( real) -> 1.000x - 0.038814780 - calls/sec ( time) -> speed covers - secs/call -``` - -In the table above 1 is `JSON::Ext::Parser`, 2 is `YAML.load` with YAML -compatible JSON document, 3 is is `JSON::Pure::Parser`, and 4 is -`ActiveSupport::JSON.decode`. The ActiveSupport JSON-decoder converts the -input first to YAML and then uses the YAML-parser, the conversion seems to -slow it down so much that it is only as fast as the `JSON::Pure::Parser`! - -If you look at the benchmark data you can see that this is mostly caused by -the frequent high outliers - the median of the Rails-parser runs is still -overall smaller than the median of the `JSON::Pure::Parser` runs: - -``` - Comparing times (call_time_median): - 1 ParserBenchmarkExt#parser 900 repeats: - 800.592479481 ( real) -> 26.936x - 0.001249075 - 2 ParserBenchmarkYAML#parser 1000 repeats: - 271.002390644 ( real) -> 9.118x - 0.003690004 - 3 ParserBenchmarkRails#parser 1000 repeats: - 30.227910865 ( real) -> 1.017x - 0.033082008 - 4 ParserBenchmarkPure#parser 1000 repeats: - 29.722384421 ( real) -> 1.000x - 0.033644676 - calls/sec ( time) -> speed covers - secs/call -``` - -I have benchmarked the `JSON-Generator` as well. This generated a few more -values, because there are different modes that also influence the achieved -speed: - -``` - Comparing times (call_time_mean): - 1 GeneratorBenchmarkExt#generator_fast 1000 repeats: - 547.354332608 ( real) -> 15.090x - 0.001826970 - 2 GeneratorBenchmarkExt#generator_safe 1000 repeats: - 443.968212317 ( real) -> 12.240x - 0.002252414 - 3 GeneratorBenchmarkExt#generator_pretty 900 repeats: - 375.104545883 ( real) -> 10.341x - 0.002665923 - 4 GeneratorBenchmarkPure#generator_fast 1000 repeats: - 49.978706968 ( real) -> 1.378x - 0.020008521 - 5 GeneratorBenchmarkRails#generator 1000 repeats: - 38.531868759 ( real) -> 1.062x - 0.025952543 - 6 GeneratorBenchmarkPure#generator_safe 1000 repeats: - 36.927649925 ( real) -> 1.018x 7 (>=3859) - 0.027079979 - 7 GeneratorBenchmarkPure#generator_pretty 1000 repeats: - 36.272134441 ( real) -> 1.000x 6 (>=3859) - 0.027569373 - calls/sec ( time) -> speed covers - secs/call -``` - -In the table above 1-3 are `JSON::Ext::Generator` methods. 4, 6, and 7 are -`JSON::Pure::Generator` methods and 5 is the Rails JSON generator. It is now a -bit faster than the `generator_safe` and `generator_pretty` methods of the pure -variant but slower than the others. - -To achieve the fastest JSON document output, you can use the `fast_generate` -method. Beware, that this will disable the checking for circular Ruby data -structures, which may cause JSON to go into an infinite loop. - -Here are the median comparisons for completeness' sake: - -``` - Comparing times (call_time_median): - 1 GeneratorBenchmarkExt#generator_fast 1000 repeats: - 708.258020939 ( real) -> 16.547x - 0.001411915 - 2 GeneratorBenchmarkExt#generator_safe 1000 repeats: - 569.105020353 ( real) -> 13.296x - 0.001757145 - 3 GeneratorBenchmarkExt#generator_pretty 900 repeats: - 482.825371244 ( real) -> 11.280x - 0.002071142 - 4 GeneratorBenchmarkPure#generator_fast 1000 repeats: - 62.717626652 ( real) -> 1.465x - 0.015944481 - 5 GeneratorBenchmarkRails#generator 1000 repeats: - 43.965681162 ( real) -> 1.027x - 0.022745013 - 6 GeneratorBenchmarkPure#generator_safe 1000 repeats: - 43.929073409 ( real) -> 1.026x 7 (>=3859) - 0.022763968 - 7 GeneratorBenchmarkPure#generator_pretty 1000 repeats: - 42.802514491 ( real) -> 1.000x 6 (>=3859) - 0.023363113 - calls/sec ( time) -> speed covers - secs/call -``` + + + + Ruby: ruby 3.2.5 (2024-07-26 revision 31d0f1a2e7) [x86_64-linux] + CPU : 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz + +### `generator_benchmark.rb` + + | Package | Function | mean µs/call | median µs/call | stddev % | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | `json/ext` v2.7.2-22-gf2b4586 | `generator_ascii` | 33.34 | 32.90 | 23.34 | + | | `generator_fast` | 33.82 | 33.38 | 26.88 | + | | `generator_pretty` | 39.99 | 39.34 | 24.25 | + | | `generator_safe` | 31.46 | 31.23 | 23.01 | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | `json_pure` v2.7.2-22-gf2b4586 | `generator_ascii` | 3028.81 | 3010.04 | 5.01 | + | | `generator_fast` | 847.85 | 840.90 | 7.86 | + | | `generator_pretty` | 964.20 | 964.40 | 5.95 | + | | `generator_safe` | 837.80 | 828.15 | 7.04 | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | [Rails 7.2.1](https://rubyonrails.org) | `generator` | 202.86 | 197.89 | 10.14 | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | [YAJL 1.4.3](https://github.com/brianmario/yajl-ruby) | `generator` | 44.50 | 44.11 | 8.98 | + | | `generator_gem_api` | 32.11 | 30.99 | 24.76 | + +### `generator2_benchmark.rb` + + | Package | Function | mean µs/call | median µs/call | stddev % | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | `json/ext` v2.7.2-22-gf2b4586 | `generator_ascii` | 119.77 | 118.97 | 5.19 | + | | `generator_fast` | 85.06 | 84.64 | 10.78 | + | | `generator_pretty` | 104.65 | 102.76 | 7.27 | + | | `generator_safe` | 82.49 | 81.78 | 9.27 | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | `json_pure` v2.7.2-22-gf2b4586 | `generator_ascii` | 2888.41 | 2871.99 | 4.15 | + | | `generator_fast` | 2119.49 | 2105.47 | 5.29 | + | | `generator_pretty` | 2329.46 | 2349.02 | 7.65 | + | | `generator_safe` | 2116.17 | 2109.05 | 5.41 | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | [Rails 7.2.1](https://rubyonrails.org) | `generator` | 308.34 | 300.17 | 12.18 | + |:------------------------------------------------------|:--------------------|-------------:|---------------:|---------:| + | [YAJL 1.4.3](https://github.com/brianmario/yajl-ruby) | `generator` | 96.29 | 91.31 | 24.39 | + | | `generator_gem_api` | 79.76 | 79.16 | 13.27 | + +### `parser_benchmark.rb` + + | Package | Function | mean µs/call | median µs/call | stddev % | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | `json/ext` v2.7.2-22-gf2b4586 | `parser` | 66.34 | 52.21 | 187.08 | + | | `parser_symbolic` | 71.60 | 57.22 | 175.52 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | `json_pure` v2.7.2-22-gf2b4586 | `parser` | 1985.88 | 1889.23 | 26.56 | + | | `parser_symbolic` | 1981.22 | 1886.84 | 25.17 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | [Rails 7.2.1](https://rubyonrails.org) | `parser` | 79.64 | 53.05 | 221.89 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | YAML (Ruby 3.2.5 builtin) | `parser` | 1884.27 | 1799.94 | 22.09 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | [YAJL 1.4.3](https://github.com/brianmario/yajl-ruby) | `parser` | 128.69 | 100.61 | 149.81 | + +### `parser2_benchmark.rb` + + | Package | Function | mean µs/call | median µs/call | stddev % | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | `json/ext` v2.7.2-22-gf2b4586 | `parser` | 257.77 | 234.84 | 85.98 | + | | `parser_symbolic` | 263.42 | 227.57 | 86.88 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | `json_pure` v2.7.2-22-gf2b4586 | `parser` | 3881.14 | 3539.20 | 25.45 | + | | `parser_symbolic` | 3895.00 | 3552.68 | 25.72 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | [Rails 7.2.1](https://rubyonrails.org) | `parser` | 78.52 | 54.84 | 219.40 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | YAML (Ruby 3.2.5 builtin) | `parser` | 2968.70 | 2879.86 | 15.72 | + |:------------------------------------------------------|:------------------|-------------:|---------------:|---------:| + | [YAJL 1.4.3](https://github.com/brianmario/yajl-ruby) | `parser` | 387.56 | 352.74 | 63.50 | + ## Development diff --git a/Rakefile b/Rakefile index 55354535..f0ddb752 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ class UndocumentedTestTask < Rake::TestTask end which = lambda { |c| - w = `which #{c}` + w = `which #{c} 2>/dev/null` break w.chomp unless w.empty? } diff --git a/benchmarks/.editorconfig b/benchmarks/.editorconfig new file mode 100644 index 00000000..08ae919e --- /dev/null +++ b/benchmarks/.editorconfig @@ -0,0 +1,5 @@ +[Makefile] +indent_style = tab + +[ohai.{json,ruby}] +indent_size = 2 diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore new file mode 100644 index 00000000..0e0dc000 --- /dev/null +++ b/benchmarks/.gitignore @@ -0,0 +1,3 @@ +!/Makefile +/vendor/ +/summary.md diff --git a/benchmarks/Makefile b/benchmarks/Makefile new file mode 100644 index 00000000..a4ceef7e --- /dev/null +++ b/benchmarks/Makefile @@ -0,0 +1,45 @@ +RUBYLIB := $(dir $(CURDIR))ext$(if $(RUBYLIB),:$(RUBYLIB)) +export RUBYLIB + +all: summary.md +.PHONY: all + +update-README.md: summary.md + { \ + sed -n '1,/BEGIN benchmarks\/summary\.md/p' <../README.md && \ + cat summary.md && \ + sed -n '/END benchmarks\/summary.md/,$$p' <../README.md ; \ + } > ../README.md.tmp + mv -f ../README.md.tmp ../README.md +.PHONY: update-README.md + +.NOTINTERMEDIATE: +.DELETE_ON_ERROR: +.NOTPARALLEL: + +summary.md: data/GeneratorBenchmarkComparison.log +summary.md: data/Generator2BenchmarkComparison.log +summary.md: data/ParserBenchmarkComparison.log +summary.md: data/Parser2BenchmarkComparison.log +summary.md: summarize.rb + bundle exec ruby ./summarize.rb >$@ + +data/GeneratorBenchmarkComparison.log: vendor + rm -f -- data/GeneratorBenchmark* + bundle exec ruby ./generator_benchmark.rb +data/Generator2BenchmarkComparison.log: vendor + rm -f -- data/Generator2Benchmark* + bundle exec ruby ./generator2_benchmark.rb +data/ParserBenchmarkComparison.log: vendor + rm -f -- data/ParserBenchmark* + bundle exec ruby ./parser_benchmark.rb +data/Parser2BenchmarkComparison.log: vendor + rm -f -- data/Parser2Benchmark* + bundle exec ruby ./parser2_benchmark.rb + +../.bundle/config: + bundle config set path $(CURDIR)/vendor + bundle config set with benchmark +vendor: ../Gemfile $(wildcard ../Gemfile.lock) ../.bundle/config + bundle install + touch --no-create $@ diff --git a/benchmarks/data/.gitignore b/benchmarks/data/.gitignore new file mode 100644 index 00000000..120f485d --- /dev/null +++ b/benchmarks/data/.gitignore @@ -0,0 +1,2 @@ +* +!/.gitignore diff --git a/benchmarks/generator2_benchmark.rb b/benchmarks/generator2_benchmark.rb new file mode 100755 index 00000000..714c03a1 --- /dev/null +++ b/benchmarks/generator2_benchmark.rb @@ -0,0 +1,222 @@ +#!/usr/bin/env ruby +# CODING: UTF-8 + +require 'rbconfig' +RUBY_PATH=File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) +RAKE_PATH=File.join(RbConfig::CONFIG['bindir'], 'rake') +require 'bullshit' +case ARGV.first +when 'ext' + require 'json/ext' +when 'pure' + require 'json/pure' +when 'rails' + require 'active_support' +when 'yajl' + require 'yajl' + require 'yajl/json_gem' + require 'stringio' +end + +module JSON + def self.[](*) end +end + +module Generator2BenchmarkCommon + include JSON + + def setup + @big = eval File.read(File.join(File.dirname(__FILE__), 'ohai.ruby')) + end + + def generic_reset_method + @result and @result.size >= 16 or raise @result.to_s + end +end + +module JSONGeneratorCommon + include Generator2BenchmarkCommon + + def benchmark_generator_fast + @result = JSON.fast_generate(@big) + end + + alias reset_benchmark_generator_fast generic_reset_method + + def benchmark_generator_safe + @result = JSON.generate(@big) + end + + alias reset_benchmark_generator_safe generic_reset_method + + def benchmark_generator_pretty + @result = JSON.pretty_generate(@big) + end + + alias reset_benchmark_generator_pretty generic_reset_method + + def benchmark_generator_ascii + @result = JSON.generate(@big, :ascii_only => true) + end + + alias reset_benchmark_generator_ascii generic_reset_method +end + +class Generator2BenchmarkExt < Bullshit::RepeatCase + include JSONGeneratorCommon + + warmup yes + iterations 2000 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes +end + +class Generator2BenchmarkPure < Bullshit::RepeatCase + include JSONGeneratorCommon + + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes +end + +class Generator2BenchmarkRails < Bullshit::RepeatCase + include Generator2BenchmarkCommon + + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_generator + @result = ActiveSupport::JSON.encode @big + end + + alias reset_benchmark_generator generic_reset_method +end + +class Generator2BenchmarkYajl < Bullshit::RepeatCase + include Generator2BenchmarkCommon + + warmup yes + iterations 2000 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_generator + output = StringIO.new + Yajl::Encoder.new.encode(@big, output) + @result = output.string + end + + def benchmark_generator_gem_api + @result = @big.to_json + end + + def reset_benchmark_generator + generic_reset_method + end +end + +if $0 == __FILE__ + Bullshit::Case.autorun false + + case ARGV.first + when 'ext' + Generator2BenchmarkExt.run + when 'pure' + Generator2BenchmarkPure.run + when 'rails' + Generator2BenchmarkRails.run + when 'yajl' + Generator2BenchmarkYajl.run + else + system "#{RAKE_PATH} clean" + system "#{RUBY_PATH} #$0 rails" + system "#{RUBY_PATH} #$0 pure" + system "#{RAKE_PATH} compile" + system "#{RUBY_PATH} #$0 ext" + system "#{RUBY_PATH} #$0 yajl" + Bullshit.compare do + output_filename File.join(File.dirname(__FILE__), 'data', 'Generator2BenchmarkComparison.log') + + benchmark Generator2BenchmarkExt, :generator_fast, :load => yes + benchmark Generator2BenchmarkExt, :generator_safe, :load => yes + benchmark Generator2BenchmarkExt, :generator_pretty, :load => yes + benchmark Generator2BenchmarkExt, :generator_ascii, :load => yes + benchmark Generator2BenchmarkPure, :generator_fast, :load => yes + benchmark Generator2BenchmarkPure, :generator_safe, :load => yes + benchmark Generator2BenchmarkPure, :generator_pretty, :load => yes + benchmark Generator2BenchmarkPure, :generator_ascii, :load => yes + benchmark Generator2BenchmarkRails, :generator, :load => yes + benchmark Generator2BenchmarkYajl, :generator, :load => yes + benchmark Generator2BenchmarkYajl, :generator_gem_api, :load => yes + end + end +end + diff --git a/benchmarks/generator_benchmark.rb b/benchmarks/generator_benchmark.rb new file mode 100755 index 00000000..04c78909 --- /dev/null +++ b/benchmarks/generator_benchmark.rb @@ -0,0 +1,223 @@ +#!/usr/bin/env ruby +# CODING: UTF-8 + +require 'rbconfig' +RUBY_PATH=File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) +RAKE_PATH=File.join(RbConfig::CONFIG['bindir'], 'rake') +require 'bullshit' +case ARGV.first +when 'ext' + require 'json/ext' +when 'pure' + require 'json/pure' +when 'rails' + require 'active_support' +when 'yajl' + require 'yajl' + require 'yajl/json_gem' + require 'stringio' +end + +module JSON + def self.[](*) end +end + +module GeneratorBenchmarkCommon + include JSON + + def setup + a = [ nil, false, true, "fÖßÄr", [ "n€st€d", true ], { "fooß" => "bär", "quux" => true } ] + puts a.to_json if a.respond_to?(:to_json) + @big = a * 100 + end + + def generic_reset_method + @result and @result.size > 2 + 6 * @big.size or raise @result.to_s + end +end + +module JSONGeneratorCommon + include GeneratorBenchmarkCommon + + def benchmark_generator_fast + @result = JSON.fast_generate(@big) + end + + alias reset_benchmark_generator_fast generic_reset_method + + def benchmark_generator_safe + @result = JSON.generate(@big) + end + + alias reset_benchmark_generator_safe generic_reset_method + + def benchmark_generator_pretty + @result = JSON.pretty_generate(@big) + end + + alias reset_benchmark_generator_pretty generic_reset_method + + def benchmark_generator_ascii + @result = JSON.generate(@big, :ascii_only => true) + end + + alias reset_benchmark_generator_ascii generic_reset_method +end + +class GeneratorBenchmarkExt < Bullshit::RepeatCase + include JSONGeneratorCommon + + warmup yes + iterations 2000 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes +end + +class GeneratorBenchmarkPure < Bullshit::RepeatCase + include JSONGeneratorCommon + + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes +end + +class GeneratorBenchmarkRails < Bullshit::RepeatCase + include GeneratorBenchmarkCommon + + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_generator + @result = ActiveSupport::JSON.encode @big + end + + alias reset_benchmark_generator generic_reset_method +end + +class GeneratorBenchmarkYajl < Bullshit::RepeatCase + include GeneratorBenchmarkCommon + + warmup yes + iterations 2000 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_generator + output = StringIO.new + Yajl::Encoder.new.encode(@big, output) + @result = output.string + end + + def benchmark_generator_gem_api + @result = @big.to_json + end + + def reset_benchmark_generator + generic_reset_method + end +end + +if $0 == __FILE__ + Bullshit::Case.autorun false + + case ARGV.first + when 'ext' + GeneratorBenchmarkExt.run + when 'pure' + GeneratorBenchmarkPure.run + when 'rails' + GeneratorBenchmarkRails.run + when 'yajl' + GeneratorBenchmarkYajl.run + else + system "#{RAKE_PATH} clean" + system "#{RUBY_PATH} #$0 rails" + system "#{RUBY_PATH} #$0 pure" + system "#{RAKE_PATH} compile" + system "#{RUBY_PATH} #$0 ext" + system "#{RUBY_PATH} #$0 yajl" + Bullshit.compare do + output_filename File.join(File.dirname(__FILE__), 'data', 'GeneratorBenchmarkComparison.log') + + benchmark GeneratorBenchmarkExt, :generator_fast, :load => yes + benchmark GeneratorBenchmarkExt, :generator_safe, :load => yes + benchmark GeneratorBenchmarkExt, :generator_pretty, :load => yes + benchmark GeneratorBenchmarkExt, :generator_ascii, :load => yes + benchmark GeneratorBenchmarkPure, :generator_fast, :load => yes + benchmark GeneratorBenchmarkPure, :generator_safe, :load => yes + benchmark GeneratorBenchmarkPure, :generator_pretty, :load => yes + benchmark GeneratorBenchmarkPure, :generator_ascii, :load => yes + benchmark GeneratorBenchmarkRails, :generator, :load => yes + benchmark GeneratorBenchmarkYajl, :generator, :load => yes + benchmark GeneratorBenchmarkYajl, :generator_gem_api, :load => yes + end + end +end diff --git a/benchmarks/ohai.ruby b/benchmarks/ohai.ruby new file mode 100644 index 00000000..36b4297e --- /dev/null +++ b/benchmarks/ohai.ruby @@ -0,0 +1 @@ +{"kernel"=>{"name"=>"Darwin", "machine"=>"i386", "modules"=>{"com.apple.driver.AppleAPIC"=>{"size"=>12288, "version"=>"1.4", "index"=>"26", "refcount"=>"0"}, "com.apple.driver.AirPort.Atheros"=>{"size"=>593920, "version"=>"318.8.3", "index"=>"88", "refcount"=>"0"}, "com.apple.driver.AppleIntelCPUPowerManagement"=>{"size"=>102400, "version"=>"59.0.1", "index"=>"22", "refcount"=>"0"}, "com.apple.iokit.IOStorageFamily"=>{"size"=>98304, "version"=>"1.5.5", "index"=>"44", "refcount"=>"9"}, "com.apple.iokit.IOATAPIProtocolTransport"=>{"size"=>16384, "version"=>"1.5.2", "index"=>"52", "refcount"=>"0"}, "com.apple.iokit.IOPCIFamily"=>{"size"=>65536, "version"=>"2.5", "index"=>"17", "refcount"=>"18"}, "org.virtualbox.kext.VBoxDrv"=>{"size"=>118784, "version"=>"2.2.0", "index"=>"114", "refcount"=>"3"}, "com.cisco.nke.ipsec"=>{"size"=>454656, "version"=>"2.0.1", "index"=>"111", "refcount"=>"0"}, "com.apple.driver.AppleHPET"=>{"size"=>12288, "version"=>"1.3", "index"=>"33", "refcount"=>"0"}, "com.apple.driver.AppleUSBHub"=>{"size"=>49152, "version"=>"3.2.7", "index"=>"47", "refcount"=>"0"}, "com.apple.iokit.IOFireWireFamily"=>{"size"=>258048, "version"=>"3.4.6", "index"=>"49", "refcount"=>"2"}, "com.apple.driver.AppleUSBComposite"=>{"size"=>16384, "version"=>"3.2.0", "index"=>"60", "refcount"=>"1"}, "com.apple.driver.AppleIntelPIIXATA"=>{"size"=>36864, "version"=>"2.0.0", "index"=>"41", "refcount"=>"0"}, "com.apple.driver.AppleSmartBatteryManager"=>{"size"=>28672, "version"=>"158.6.0", "index"=>"32", "refcount"=>"0"}, "com.apple.filesystems.udf"=>{"size"=>233472, "version"=>"2.0.2", "index"=>"119", "refcount"=>"0"}, "com.apple.iokit.IOSMBusFamily"=>{"size"=>12288, "version"=>"1.1", "index"=>"27", "refcount"=>"2"}, "com.apple.iokit.IOACPIFamily"=>{"size"=>16384, "version"=>"1.2.0", "index"=>"18", "refcount"=>"10"}, "foo.tap"=>{"size"=>24576, "version"=>"1.0", "index"=>"113", "refcount"=>"0"}, "com.vmware.kext.vmx86"=>{"size"=>864256, "version"=>"2.0.4", "index"=>"104", "refcount"=>"0"}, "com.apple.iokit.CHUDUtils"=>{"size"=>28672, "version"=>"200", "index"=>"98", "refcount"=>"0"}, "com.apple.driver.AppleACPIButtons"=>{"size"=>16384, "version"=>"1.2.4", "index"=>"30", "refcount"=>"0"}, "com.apple.driver.AppleFWOHCI"=>{"size"=>139264, "version"=>"3.7.2", "index"=>"50", "refcount"=>"0"}, "com.apple.iokit.IOSCSIArchitectureModelFamily"=>{"size"=>102400, "version"=>"2.0.5", "index"=>"51", "refcount"=>"4"}, "org.virtualbox.kext.VBoxNetAdp"=>{"size"=>8192, "version"=>"2.2.0", "index"=>"117", "refcount"=>"0"}, "com.apple.filesystems.autofs"=>{"size"=>45056, "version"=>"2.0.1", "index"=>"109", "refcount"=>"0"}, "com.vmware.kext.vmnet"=>{"size"=>36864, "version"=>"2.0.4", "index"=>"108", "refcount"=>"0"}, "com.apple.iokit.IOSCSIBlockCommandsDevice"=>{"size"=>90112, "version"=>"2.0.5", "index"=>"57", "refcount"=>"1"}, "com.apple.driver.AppleACPIPCI"=>{"size"=>12288, "version"=>"1.2.4", "index"=>"31", "refcount"=>"0"}, "com.apple.security.seatbelt"=>{"size"=>98304, "version"=>"107.10", "index"=>"25", "refcount"=>"0"}, "com.apple.driver.AppleUpstreamUserClient"=>{"size"=>16384, "version"=>"2.7.2", "index"=>"100", "refcount"=>"0"}, "com.apple.kext.OSvKernDSPLib"=>{"size"=>12288, "version"=>"1.1", "index"=>"79", "refcount"=>"1"}, "com.apple.iokit.IOBDStorageFamily"=>{"size"=>20480, "version"=>"1.5", "index"=>"58", "refcount"=>"1"}, "com.apple.iokit.IOGraphicsFamily"=>{"size"=>118784, "version"=>"1.7.1", "index"=>"70", "refcount"=>"5"}, "com.apple.iokit.IONetworkingFamily"=>{"size"=>90112, "version"=>"1.6.1", "index"=>"82", "refcount"=>"4"}, "com.apple.iokit.IOATAFamily"=>{"size"=>53248, "version"=>"2.0.0", "index"=>"40", "refcount"=>"2"}, "com.apple.iokit.IOUSBHIDDriver"=>{"size"=>20480, "version"=>"3.2.2", "index"=>"63", "refcount"=>"2"}, "org.virtualbox.kext.VBoxUSB"=>{"size"=>28672, "version"=>"2.2.0", "index"=>"115", "refcount"=>"0"}, "com.apple.security.TMSafetyNet"=>{"size"=>12288, "version"=>"3", "index"=>"23", "refcount"=>"0"}, "com.apple.iokit.IONDRVSupport"=>{"size"=>57344, "version"=>"1.7.1", "index"=>"71", "refcount"=>"3"}, "com.apple.BootCache"=>{"size"=>20480, "version"=>"30.3", "index"=>"20", "refcount"=>"0"}, "com.vmware.kext.vmioplug"=>{"size"=>24576, "version"=>"2.0.4", "index"=>"107", "refcount"=>"0"}, "com.apple.iokit.IOUSBUserClient"=>{"size"=>8192, "version"=>"3.2.4", "index"=>"46", "refcount"=>"1"}, "com.apple.iokit.IOSCSIMultimediaCommandsDevice"=>{"size"=>90112, "version"=>"2.0.5", "index"=>"59", "refcount"=>"0"}, "com.apple.driver.AppleIRController"=>{"size"=>20480, "version"=>"110", "index"=>"78", "refcount"=>"0"}, "com.apple.driver.AudioIPCDriver"=>{"size"=>16384, "version"=>"1.0.5", "index"=>"81", "refcount"=>"0"}, "com.apple.driver.AppleLPC"=>{"size"=>12288, "version"=>"1.2.11", "index"=>"73", "refcount"=>"0"}, "org.virtualbox.kext.VBoxNetFlt"=>{"size"=>16384, "version"=>"2.2.0", "index"=>"116", "refcount"=>"0"}, "com.apple.iokit.CHUDKernLib"=>{"size"=>20480, "version"=>"196", "index"=>"93", "refcount"=>"2"}, "com.apple.iokit.CHUDProf"=>{"size"=>49152, "version"=>"207", "index"=>"97", "refcount"=>"0"}, "com.apple.NVDAResman"=>{"size"=>2478080, "version"=>"5.3.6", "index"=>"90", "refcount"=>"2"}, "com.apple.driver.AppleACPIEC"=>{"size"=>20480, "version"=>"1.2.4", "index"=>"28", "refcount"=>"0"}, "foo.tun"=>{"size"=>24576, "version"=>"1.0", "index"=>"118", "refcount"=>"0"}, "com.apple.iokit.IOSerialFamily"=>{"size"=>36864, "version"=>"9.3", "index"=>"102", "refcount"=>"1"}, "com.apple.GeForce"=>{"size"=>622592, "version"=>"5.3.6", "index"=>"96", "refcount"=>"0"}, "com.apple.iokit.IOCDStorageFamily"=>{"size"=>32768, "version"=>"1.5", "index"=>"55", "refcount"=>"3"}, "com.apple.driver.AppleUSBEHCI"=>{"size"=>73728, "version"=>"3.2.5", "index"=>"39", "refcount"=>"0"}, "com.apple.nvidia.nv50hal"=>{"size"=>2445312, "version"=>"5.3.6", "index"=>"91", "refcount"=>"0"}, "com.apple.driver.AppleSMBIOS"=>{"size"=>16384, "version"=>"1.1.1", "index"=>"29", "refcount"=>"0"}, "com.apple.driver.AppleBacklight"=>{"size"=>16384, "version"=>"1.4.4", "index"=>"72", "refcount"=>"0"}, "com.apple.driver.AppleACPIPlatform"=>{"size"=>253952, "version"=>"1.2.4", "index"=>"19", "refcount"=>"3"}, "com.apple.iokit.SCSITaskUserClient"=>{"size"=>24576, "version"=>"2.0.5", "index"=>"54", "refcount"=>"0"}, "com.apple.iokit.IOHIDFamily"=>{"size"=>233472, "version"=>"1.5.3", "index"=>"21", "refcount"=>"7"}, "com.apple.driver.DiskImages"=>{"size"=>65536, "version"=>"195.2.2", "index"=>"101", "refcount"=>"0"}, "com.apple.iokit.IODVDStorageFamily"=>{"size"=>24576, "version"=>"1.5", "index"=>"56", "refcount"=>"2"}, "com.apple.iokit.IOFireWireIP"=>{"size"=>36864, "version"=>"1.7.6", "index"=>"83", "refcount"=>"0"}, "com.apple.driver.AppleRTC"=>{"size"=>20480, "version"=>"1.2.3", "index"=>"34", "refcount"=>"0"}, "com.apple.driver.XsanFilter"=>{"size"=>20480, "version"=>"2.7.91", "index"=>"53", "refcount"=>"0"}, "com.apple.driver.AppleEFIRuntime"=>{"size"=>12288, "version"=>"1.2.0", "index"=>"35", "refcount"=>"1"}, "com.apple.iokit.IOAHCIBlockStorage"=>{"size"=>69632, "version"=>"1.2.0", "index"=>"48", "refcount"=>"0"}, "com.apple.nke.applicationfirewall"=>{"size"=>32768, "version"=>"1.0.77", "index"=>"24", "refcount"=>"0"}, "com.apple.iokit.IO80211Family"=>{"size"=>126976, "version"=>"215.1", "index"=>"87", "refcount"=>"1"}, "com.vmware.kext.vmci"=>{"size"=>45056, "version"=>"2.0.4", "index"=>"106", "refcount"=>"0"}, "com.apple.iokit.IOAHCIFamily"=>{"size"=>24576, "version"=>"1.5.0", "index"=>"42", "refcount"=>"2"}, "com.apple.driver.AppleUSBUHCI"=>{"size"=>57344, "version"=>"3.2.5", "index"=>"38", "refcount"=>"0"}, "com.apple.driver.AppleUSBMergeNub"=>{"size"=>12288, "version"=>"3.2.4", "index"=>"61", "refcount"=>"0"}, "com.apple.iokit.IOUSBFamily"=>{"size"=>167936, "version"=>"3.2.7", "index"=>"37", "refcount"=>"13"}, "com.apple.driver.AppleEFINVRAM"=>{"size"=>24576, "version"=>"1.2.0", "index"=>"36", "refcount"=>"0"}, "com.apple.driver.AppleAHCIPort"=>{"size"=>53248, "version"=>"1.5.2", "index"=>"43", "refcount"=>"0"}}, "os"=>"Darwin", "version"=>"Darwin Kernel Version 9.6.0: Mon Nov 24 17:37:00 PST 2008; root:xnu-1228.9.59~1/RELEASE_I386", "release"=>"9.6.0"}, "command"=>{"ps"=>"ps -ef"}, "platform"=>"mac_os_x", "platform_version"=>"10.5.6", "keys"=>{"ssh"=>{"host_dsa_public"=>"private", "host_rsa_public"=>"private"}}, "ipaddress"=>"192.168.88.1", "fqdn"=>"local.local", "network"=>{"settings"=>{"net.inet6.ip6.forwarding"=>"0", "net.inet.ip.dummynet.debug"=>"0", "net.inet.ip.rtexpire"=>"10", "net.inet6.ipsec6.esp_trans_deflev"=>"1", "net.inet.tcp.tcbhashsize"=>"4096", "net.key.esp_auth"=>"0", "net.inet6.ip6.hlim"=>"64", "net.inet.ip.fw.dyn_fin_lifetime"=>"1", "net.inet.ip.fw.dyn_udp_lifetime"=>"10", "net.inet.icmp.bmcastecho"=>"1", "net.athbgscan"=>"1 1", "net.inet.tcp.reass.maxsegments"=>"2048", "net.athforceBias"=>"2 2", "net.inet6.ip6.auto_flowlabel"=>"1", "net.inet6.ip6.rtmaxcache"=>"128", "net.inet.tcp.sendspace"=>"131072", "net.inet.tcp.keepinit"=>"75000", "net.inet.ip.dummynet.max_chain_len"=>"16", "net.inet.tcp.rfc1644"=>"0", "net.inet.ip.fw.curr_dyn_buckets"=>"256", "net.inet.ip.dummynet.ready_heap"=>"0", "net.inet.ip.portrange.first"=>"49152", "net.inet.tcp.background_io_trigger"=>"5", "net.link.ether.inet.host_down_time"=>"20", "net.inet6.ipsec6.def_policy"=>"1", "net.inet6.ipsec6.ecn"=>"0", "net.inet.ip.fastforwarding"=>"0", "net.inet6.ip6.v6only"=>"0", "net.inet.tcp.sack"=>"1", "net.inet6.ip6.rtexpire"=>"3600", "net.link.ether.inet.proxyall"=>"0", "net.athaddbaignore"=>"0 0", "net.inet6.ip6.keepfaith"=>"0", "net.key.spi_trycnt"=>"1000", "net.link.ether.inet.prune_intvl"=>"300", "net.inet.tcp.ecn_initiate_out"=>"0", "net.inet.ip.fw.dyn_rst_lifetime"=>"1", "net.local.stream.sendspace"=>"8192", "net.inet.tcp.socket_unlocked_on_output"=>"1", "net.inet.ip.fw.verbose_limit"=>"0", "net.local.dgram.recvspace"=>"4096", "net.inet.ipsec.debug"=>"0", "net.link.ether.inet.log_arp_warnings"=>"0", "net.inet.tcp.ecn_negotiate_in"=>"0", "net.inet.tcp.rfc3465"=>"1", "net.inet.tcp.icmp_may_rst"=>"1", "net.link.ether.inet.sendllconflict"=>"0", "net.inet.ipsec.ah_offsetmask"=>"0", "net.key.blockacq_count"=>"10", "net.inet.tcp.delayed_ack"=>"3", "net.inet.ip.fw.verbose"=>"2", "net.inet.ip.fw.dyn_count"=>"0", "net.inet.tcp.slowlink_wsize"=>"8192", "net.inet6.ip6.fw.enable"=>"1", "net.inet.ip.portrange.hilast"=>"65535", "net.inet.icmp.maskrepl"=>"0", "net.link.ether.inet.apple_hwcksum_rx"=>"1", "net.inet.tcp.drop_synfin"=>"1", "net.key.spi_maxval"=>"268435455", "net.inet.ipsec.ecn"=>"0", "net.inet.ip.fw.dyn_keepalive"=>"1", "net.key.int_random"=>"60", "net.key.debug"=>"0", "net.inet.ip.dummynet.curr_time"=>"0", "net.inet.udp.blackhole"=>"0", "net.athaggrqmin"=>"1 1", "net.inet.ip.fw.dyn_syn_lifetime"=>"20", "net.inet.tcp.keepidle"=>"7200000", "net.inet6.ip6.tempvltime"=>"604800", "net.inet.tcp.recvspace"=>"358400", "net.inet.udp.maxdgram"=>"9216", "net.inet.tcp.keepintvl"=>"75000", "net.inet.ip.maxchainsent"=>"0", "net.athppmenable"=>"1 1", "net.inet.ipsec.esp_net_deflev"=>"1", "net.inet6.icmp6.nd6_useloopback"=>"1", "net.inet.tcp.slowstart_flightsize"=>"1", "net.inet.ip.fw.debug"=>"0", "net.inet.ip.linklocal.in.allowbadttl"=>"1", "net.key.spi_minval"=>"256", "net.inet.ip.forwarding"=>"0", "net.inet.tcp.v6mssdflt"=>"1024", "net.key.larval_lifetime"=>"30", "net.inet6.ip6.fw.verbose_limit"=>"0", "net.inet.ip.dummynet.red_lookup_depth"=>"256", "net.inet.tcp.pcbcount"=>"36", "net.inet.ip.fw.dyn_ack_lifetime"=>"300", "net.athCCAThreshold"=>"28 28", "net.inet.ip.portrange.lowlast"=>"600", "net.link.ether.inet.useloopback"=>"1", "net.athqdepth"=>"0 0", "net.inet.ip.ttl"=>"64", "net.inet.ip.rtmaxcache"=>"128", "net.inet.ipsec.bypass"=>"0", "net.inet6.icmp6.nd6_debug"=>"0", "net.inet.ip.use_route_genid"=>"1", "net.inet6.icmp6.rediraccept"=>"1", "net.inet.ip.fw.static_count"=>"1", "net.inet6.ip6.fw.debug"=>"0", "net.inet.udp.pcbcount"=>"104", "net.inet.ipsec.esp_randpad"=>"-1", "net.inet6.icmp6.nd6_maxnudhint"=>"0", "net.inet.tcp.always_keepalive"=>"0", "net.inet.udp.checksum"=>"1", "net.link.ether.inet.keep_announcements"=>"1", "net.athfixedDropThresh"=>"150 150", "net.inet6.ip6.kame_version"=>"20010528/apple-darwin", "net.inet.ip.fw.dyn_max"=>"4096", "net.inet.udp.log_in_vain"=>"0", "net.inet6.icmp6.nd6_mmaxtries"=>"3", "net.inet.ip.rtminexpire"=>"10", "net.inet.ip.fw.dyn_buckets"=>"256", "net.inet6.ip6.accept_rtadv"=>"0", "net.inet6.ip6.rr_prune"=>"5", "net.key.ah_keymin"=>"128", "net.inet.ip.redirect"=>"1", "net.inet.tcp.sack_globalmaxholes"=>"65536", "net.inet.ip.keepfaith"=>"0", "net.inet.ip.dummynet.expire"=>"1", "net.inet.ip.gifttl"=>"30", "net.inet.ip.portrange.last"=>"65535", "net.inet.ipsec.ah_net_deflev"=>"1", "net.inet6.icmp6.nd6_delay"=>"5", "net.inet.tcp.packetchain"=>"50", "net.inet6.ip6.hdrnestlimit"=>"50", "net.inet.tcp.newreno"=>"0", "net.inet6.ip6.dad_count"=>"1", "net.inet6.ip6.auto_linklocal"=>"1", "net.inet6.ip6.temppltime"=>"86400", "net.inet.tcp.strict_rfc1948"=>"0", "net.inet.ip.dummynet.red_max_pkt_size"=>"1500", "net.inet.ip.maxfrags"=>"2048", "net.inet.tcp.log_in_vain"=>"0", "net.athdupie"=>"1 1", "net.inet.tcp.rfc1323"=>"1", "net.inet.ip.subnets_are_local"=>"0", "net.inet.ip.dummynet.search_steps"=>"0", "net.inet.icmp.icmplim"=>"250", "net.link.ether.inet.apple_hwcksum_tx"=>"1", "net.inet6.icmp6.redirtimeout"=>"600", "net.inet.ipsec.ah_cleartos"=>"1", "net.inet6.ip6.log_interval"=>"5", "net.link.ether.inet.max_age"=>"1200", "net.inet.ip.fw.enable"=>"1", "net.inet6.ip6.redirect"=>"1", "net.athaggrfmax"=>"28 28", "net.inet.ip.maxfragsperpacket"=>"128", "net.inet6.ip6.use_deprecated"=>"1", "net.link.generic.system.dlil_input_sanity_check"=>"0", "net.inet.tcp.sack_globalholes"=>"0", "net.inet.tcp.reass.cursegments"=>"0", "net.inet6.icmp6.nodeinfo"=>"3", "net.local.inflight"=>"0", "net.inet.ip.dummynet.hash_size"=>"64", "net.inet.ip.dummynet.red_avg_pkt_size"=>"512", "net.inet.ipsec.dfbit"=>"0", "net.inet.tcp.reass.overflows"=>"0", "net.inet.tcp.rexmt_thresh"=>"2", "net.inet6.ip6.maxfrags"=>"8192", "net.inet6.ip6.rtminexpire"=>"10", "net.inet6.ipsec6.esp_net_deflev"=>"1", "net.inet.tcp.blackhole"=>"0", "net.key.esp_keymin"=>"256", "net.inet.ip.check_interface"=>"0", "net.inet.tcp.minmssoverload"=>"0", "net.link.ether.inet.maxtries"=>"5", "net.inet.tcp.do_tcpdrain"=>"0", "net.inet.ipsec.esp_port"=>"4500", "net.inet6.ipsec6.ah_net_deflev"=>"1", "net.inet.ip.dummynet.extract_heap"=>"0", "net.inet.tcp.path_mtu_discovery"=>"1", "net.inet.ip.intr_queue_maxlen"=>"50", "net.inet.ipsec.def_policy"=>"1", "net.inet.ip.fw.autoinc_step"=>"100", "net.inet.ip.accept_sourceroute"=>"0", "net.inet.raw.maxdgram"=>"8192", "net.inet.ip.maxfragpackets"=>"1024", "net.inet.ip.fw.one_pass"=>"0", "net.appletalk.routermix"=>"2000", "net.inet.tcp.tcp_lq_overflow"=>"1", "net.link.generic.system.ifcount"=>"9", "net.link.ether.inet.send_conflicting_probes"=>"1", "net.inet.tcp.background_io_enabled"=>"1", "net.inet6.ipsec6.debug"=>"0", "net.inet.tcp.win_scale_factor"=>"3", "net.key.natt_keepalive_interval"=>"20", "net.inet.tcp.msl"=>"15000", "net.inet.ip.portrange.hifirst"=>"49152", "net.inet.ipsec.ah_trans_deflev"=>"1", "net.inet.tcp.rtt_min"=>"1", "net.inet6.ip6.defmcasthlim"=>"1", "net.inet6.icmp6.nd6_prune"=>"1", "net.inet6.ip6.fw.verbose"=>"0", "net.inet.ip.portrange.lowfirst"=>"1023", "net.inet.tcp.maxseg_unacked"=>"8", "net.local.dgram.maxdgram"=>"2048", "net.key.blockacq_lifetime"=>"20", "net.inet.tcp.sack_maxholes"=>"128", "net.inet6.ip6.maxfragpackets"=>"1024", "net.inet6.ip6.use_tempaddr"=>"0", "net.athpowermode"=>"0 0", "net.inet.udp.recvspace"=>"73728", "net.inet.tcp.isn_reseed_interval"=>"0", "net.inet.tcp.local_slowstart_flightsize"=>"8", "net.inet.ip.dummynet.searches"=>"0", "net.inet.ip.intr_queue_drops"=>"0", "net.link.generic.system.multi_threaded_input"=>"1", "net.inet.raw.recvspace"=>"8192", "net.inet.ipsec.esp_trans_deflev"=>"1", "net.key.prefered_oldsa"=>"0", "net.local.stream.recvspace"=>"8192", "net.inet.tcp.sockthreshold"=>"64", "net.inet6.icmp6.nd6_umaxtries"=>"3", "net.pstimeout"=>"20 20", "net.inet.ip.sourceroute"=>"0", "net.inet.ip.fw.dyn_short_lifetime"=>"5", "net.inet.tcp.minmss"=>"216", "net.inet6.ip6.gifhlim"=>"0", "net.athvendorie"=>"1 1", "net.inet.ip.check_route_selfref"=>"1", "net.inet.icmp.log_redirect"=>"0", "net.inet6.icmp6.errppslimit"=>"100", "net.inet.tcp.mssdflt"=>"512", "net.inet.icmp.drop_redirect"=>"0", "net.inet6.ipsec6.esp_randpad"=>"-1", "net.inet6.ipsec6.ah_trans_deflev"=>"1", "net.inet.ip.random_id"=>"1", "net.inet.icmp.timestamp"=>"0"}, "interfaces"=>{"stf0"=>{"flags"=>[], "number"=>"0", "mtu"=>"1280", "type"=>"stf", "encapsulation"=>"6to4"}, "vmnet1"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"1", "addresses"=>[{"broadcast"=>"192.168.88.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.88.1"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vmnet", "encapsulation"=>"Ethernet"}, "vboxnet0"=>{"flags"=>["BROADCAST", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vboxnet", "encapsulation"=>"Ethernet"}, "lo0"=>{"flags"=>["UP", "LOOPBACK", "RUNNING", "MULTICAST"], "number"=>"0", "addresses"=>[{"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6", "address"=>"fe80::1"}, {"netmask"=>"255.0.0.0", "family"=>"inet", "address"=>"127.0.0.1"}, {"scope"=>"Node", "prefixlen"=>"128", "family"=>"inet6", "address"=>"::1"}, {"scope"=>"Node", "prefixlen"=>"128", "family"=>"inet6", "address"=>"private"}], "mtu"=>"16384", "type"=>"lo", "encapsulation"=>"Loopback"}, "vboxn"=>{"counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}}, "gif0"=>{"flags"=>["POINTOPOINT", "MULTICAST"], "number"=>"0", "mtu"=>"1280", "type"=>"gif", "encapsulation"=>"IPIP"}, "vmnet"=>{"counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}}, "en0"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"inactive", "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"en", "media"=>{"supported"=>[{"autoselect"=>{"options"=>[]}}, {"10baseT/UTP"=>{"options"=>["half-duplex"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"10baseT/UTP"=>{"options"=>["full-duplex", "flow-control"]}}, {"100baseTX"=>{"options"=>["half-duplex"]}}, {"100baseTX"=>{"options"=>["full-duplex"]}}, {"100baseTX"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"100baseTX"=>{"options"=>["full-duplex", "flow-control"]}}, {"1000baseT"=>{"options"=>["full-duplex"]}}, {"1000baseT"=>{"options"=>["full-duplex", "hw-loopback"]}}, {"1000baseT"=>{"options"=>["full-duplex", "flow-control"]}}, {"none"=>{"options"=>[]}}], "selected"=>[{"autoselect"=>{"options"=>[]}}]}, "counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"342", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"Ethernet"}, "vmnet8"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "number"=>"8", "addresses"=>[{"broadcast"=>"192.168.237.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.237.1"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"vmnet", "encapsulation"=>"Ethernet"}, "en1"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"active", "number"=>"1", "addresses"=>[{"scope"=>"Link", "prefixlen"=>"64", "family"=>"inet6", "address"=>"private"}, {"broadcast"=>"192.168.1.255", "netmask"=>"255.255.255.0", "family"=>"inet", "address"=>"192.168.1.4"}, {"family"=>"lladdr", "address"=>"private"}], "mtu"=>"1500", "type"=>"en", "media"=>{"supported"=>[{"autoselect"=>{"options"=>[]}}], "selected"=>[{"autoselect"=>{"options"=>[]}}]}, "counters"=>{"tx"=>{"packets"=>"7041789", "bytes"=>"449206298", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"95", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"19966002", "bytes"=>"13673879120", "compressed"=>0, "errors"=>"1655893", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"Ethernet", "arp"=>{"192.168.1.7"=>"private"}}, "fw0"=>{"flags"=>["UP", "BROADCAST", "SMART", "RUNNING", "SIMPLEX", "MULTICAST"], "status"=>"inactive", "number"=>"0", "addresses"=>[{"family"=>"lladdr", "address"=>"private"}], "mtu"=>"4078", "type"=>"fw", "media"=>{"supported"=>[{"autoselect"=>{"options"=>["full-duplex"]}}], "selected"=>[{"autoselect"=>{"options"=>["full-duplex"]}}]}, "counters"=>{"tx"=>{"packets"=>"0", "bytes"=>"346", "compressed"=>0, "collisions"=>"0", "carrier"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0}, "rx"=>{"packets"=>"0", "bytes"=>"0", "compressed"=>0, "errors"=>"0", "drop"=>0, "overrun"=>0, "multicast"=>0, "frame"=>0}}, "encapsulation"=>"1394"}}}, "os"=>"darwin", "domain"=>"local", "ohai_time"=>1240624355.08575, "platform_build"=>"9G55", "os_version"=>"9.6.0", "hostname"=>"local", "languages"=>{"ruby"=>{"target_os"=>"darwin9.0", "platform"=>"universal-darwin9.0", "host_vendor"=>"apple", "target_cpu"=>"i686", "target_vendor"=>"apple", "host_os"=>"darwin9.0", "version"=>"1.8.6", "host_cpu"=>"i686", "host"=>"i686-apple-darwin9.0", "release_date"=>"2008-03-03", "target"=>"i686-apple-darwin9.0"}}, "macaddress"=>"private"} diff --git a/benchmarks/parser2_benchmark.rb b/benchmarks/parser2_benchmark.rb new file mode 100755 index 00000000..a6095586 --- /dev/null +++ b/benchmarks/parser2_benchmark.rb @@ -0,0 +1,251 @@ +#!/usr/bin/env ruby +# CODING: UTF-8 + +require 'rbconfig' +RUBY_PATH=File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) +RAKE_PATH=File.join(RbConfig::CONFIG['bindir'], 'rake') +require 'bullshit' +case ARGV.first +when 'ext' + require 'json/ext' +when 'pure' + require 'json/pure' +when 'yaml' + require 'yaml' + require 'json/pure' +when 'rails' + require 'active_support' + require 'json/pure' +when 'yajl' + require 'yajl' + require 'json/pure' +else + require 'json/pure' +end + +module Parser2BenchmarkCommon + include JSON + + def setup + @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json')) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +class Parser2BenchmarkExt < Bullshit::RepeatCase + include Parser2BenchmarkCommon + + warmup yes + iterations 2000 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_parser + @result = JSON.parse(@json) + end + + alias reset_parser generic_reset_method + + def benchmark_parser_symbolic + @result = JSON.parse(@json, :symbolize_names => true) + end + + alias reset_parser_symbolc generic_reset_method +end + +class Parser2BenchmarkPure < Bullshit::RepeatCase + include Parser2BenchmarkCommon + + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_parser + @result = JSON.parse(@json) + end + + alias reset_parser generic_reset_method + + def benchmark_parser_symbolic + @result = JSON.parse(@json, :symbolize_names => true) + end + + alias reset_parser_symbolc generic_reset_method +end + +class Parser2BenchmarkYAML < Bullshit::RepeatCase + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def setup + @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json')) + end + + def benchmark_parser + @result = YAML.load(@json) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +class Parser2BenchmarkRails < Bullshit::RepeatCase + warmup yes + iterations 400 + + truncate_data do + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def setup + a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ] + @big = a * 100 + @json = JSON.generate(@big) + end + + def benchmark_parser + @result = ActiveSupport::JSON.decode(@json) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +class Parser2BenchmarkYajl < Bullshit::RepeatCase + warmup yes + iterations 2000 + + truncate_data do + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def setup + @big = @json = File.read(File.join(File.dirname(__FILE__), 'ohai.json')) + end + + def benchmark_parser + @result = Yajl::Parser.new.parse(@json) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +if $0 == __FILE__ + Bullshit::Case.autorun false + + case ARGV.first + when 'ext' + Parser2BenchmarkExt.run + when 'pure' + Parser2BenchmarkPure.run + when 'yaml' + Parser2BenchmarkYAML.run + when 'rails' + Parser2BenchmarkRails.run + when 'yajl' + Parser2BenchmarkYajl.run + else + system "#{RAKE_PATH} clean" + system "#{RUBY_PATH} #$0 yaml" + system "#{RUBY_PATH} #$0 rails" + system "#{RUBY_PATH} #$0 pure" + system "#{RAKE_PATH} compile" + system "#{RUBY_PATH} #$0 ext" + system "#{RUBY_PATH} #$0 yajl" + Bullshit.compare do + output_filename File.join(File.dirname(__FILE__), 'data', 'Parser2BenchmarkComparison.log') + + benchmark Parser2BenchmarkExt, :parser, :load => yes + benchmark Parser2BenchmarkExt, :parser_symbolic, :load => yes + benchmark Parser2BenchmarkPure, :parser, :load => yes + benchmark Parser2BenchmarkPure, :parser_symbolic, :load => yes + benchmark Parser2BenchmarkYAML, :parser, :load => yes + benchmark Parser2BenchmarkRails, :parser, :load => yes + benchmark Parser2BenchmarkYajl, :parser, :load => yes + end + end +end diff --git a/benchmarks/parser_benchmark.rb b/benchmarks/parser_benchmark.rb new file mode 100755 index 00000000..2e6d6985 --- /dev/null +++ b/benchmarks/parser_benchmark.rb @@ -0,0 +1,259 @@ +#!/usr/bin/env ruby +# CODING: UTF-8 + +require 'rbconfig' +RUBY_PATH=File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) +RAKE_PATH=File.join(RbConfig::CONFIG['bindir'], 'rake') +require 'bullshit' +case ARGV.first +when 'ext' + require 'json/ext' +when 'pure' + require 'json/pure' +when 'yaml' + require 'yaml' + require 'json/pure' +when 'rails' + require 'active_support' + require 'json/pure' +when 'yajl' + require 'yajl' + require 'json/pure' +else + require 'json/pure' +end + +module ParserBenchmarkCommon + include JSON + + def setup + a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ] + @big = a * 100 + @json = JSON.generate(@big) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +class ParserBenchmarkExt < Bullshit::RepeatCase + include ParserBenchmarkCommon + + warmup yes + iterations 2000 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_parser + @result = JSON.parse(@json) + end + + alias reset_parser generic_reset_method + + def benchmark_parser_symbolic + @result = JSON.parse(@json, :symbolize_names => true) + end + + alias reset_parser_symbolc generic_reset_method +end + +class ParserBenchmarkPure < Bullshit::RepeatCase + include ParserBenchmarkCommon + + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def benchmark_parser + @result = JSON.parse(@json) + end + + alias reset_parser generic_reset_method + + def benchmark_parser_symbolic + @result = JSON.parse(@json, :symbolize_names => true) + end + + alias reset_parser_symbolc generic_reset_method +end + +class ParserBenchmarkYAML < Bullshit::RepeatCase + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def setup + a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ] + @big = a * 100 + @json = JSON.pretty_generate(@big) + end + + def benchmark_parser + @result = YAML.load(@json) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +class ParserBenchmarkRails < Bullshit::RepeatCase + warmup yes + iterations 400 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def setup + a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ] + @big = a * 100 + @json = JSON.generate(@big) + end + + def benchmark_parser + @result = ActiveSupport::JSON.decode(@json) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +class ParserBenchmarkYajl < Bullshit::RepeatCase + warmup yes + iterations 2000 + + truncate_data do + enabled false + alpha_level 0.05 + window_size 50 + slope_angle 0.1 + end + + autocorrelation do + alpha_level 0.05 + max_lags 50 + file yes + end + + output_dir File.join(File.dirname(__FILE__), 'data') + output_filename benchmark_name + '.log' + data_file yes + histogram yes + + def setup + a = [ nil, false, true, "fÖß\nÄr", [ "n€st€d", true ], { "fooß" => "bär", "qu\r\nux" => true } ] + @big = a * 100 + @json = JSON.generate(@big) + end + + def benchmark_parser + @result = Yajl::Parser.new.parse(@json) + end + + def generic_reset_method + @result == @big or raise "not equal" + end +end + +if $0 == __FILE__ + Bullshit::Case.autorun false + + case ARGV.first + when 'ext' + ParserBenchmarkExt.run + when 'pure' + ParserBenchmarkPure.run + when 'yaml' + ParserBenchmarkYAML.run + when 'rails' + ParserBenchmarkRails.run + when 'yajl' + ParserBenchmarkYajl.run + else + system "#{RAKE_PATH} clean" + system "#{RUBY_PATH} #$0 yaml" + system "#{RUBY_PATH} #$0 rails" + system "#{RUBY_PATH} #$0 pure" + system "#{RAKE_PATH} compile" + system "#{RUBY_PATH} #$0 ext" + system "#{RUBY_PATH} #$0 yajl" + Bullshit.compare do + output_filename File.join(File.dirname(__FILE__), 'data', 'ParserBenchmarkComparison.log') + + benchmark ParserBenchmarkExt, :parser, :load => yes + benchmark ParserBenchmarkExt, :parser_symbolic, :load => yes + benchmark ParserBenchmarkPure, :parser, :load => yes + benchmark ParserBenchmarkPure, :parser_symbolic, :load => yes + benchmark ParserBenchmarkYAML, :parser, :load => yes + benchmark ParserBenchmarkRails, :parser, :load => yes + benchmark ParserBenchmarkYajl, :parser, :load => yes + end + end +end diff --git a/benchmarks/summarize.rb b/benchmarks/summarize.rb new file mode 100755 index 00000000..7d00e096 --- /dev/null +++ b/benchmarks/summarize.rb @@ -0,0 +1,101 @@ +#!/usr/bin/env ruby +# CODING: UTF-8 + +def summarize(data_dir) + pkg_data = { + :json => { + :ver => `git describe --tags`.strip, + }, + :yajl => { + :ver => Gem.loaded_specs['yajl-ruby'].version.to_s, + :url => Gem.loaded_specs['yajl-ruby'].homepage, + }, + :rails => { + :ver => Gem.loaded_specs['activesupport'].version.to_s, + :url => Gem.loaded_specs['activesupport'].homepage, + } + } + + puts " Ruby: " + `ruby --version`.strip + puts " CPU : " + File.readlines("/proc/cpuinfo", chomp: true).find{|line| line.start_with? "model name"}.split(":", 2)[1].strip + + cols = [ + {:hdr => "Package", :align => :left, :val => lambda { |key, val| + ugly = key.split('#', 2)[0] + case ugly + when "Ext" + "`json/ext` #{pkg_data[:json][:ver]}" + when "Pure" + "`json_pure` #{pkg_data[:json][:ver]}" + when "Rails" + "[Rails #{pkg_data[:rails][:ver]}](#{pkg_data[:rails][:url]})" + when "Yajl" + "[YAJL #{pkg_data[:yajl][:ver]}](#{pkg_data[:yajl][:url]})" + when "YAML" + "YAML (Ruby #{RUBY_VERSION} builtin)" + else + ugly + end + }}, + {:hdr => "Function", :align => :left, :val => lambda { |key, val| '`'+key.split('#', 2)[1]+'`' }}, + {:hdr => "mean µs/call", :align => :right, :val => lambda { |key, val| sprintf "%.2f", val['mean']['µs/call'] }}, + {:hdr => "median µs/call", :align => :right, :val => lambda { |key, val| sprintf "%.2f", val['median']['µs/call'] }}, + {:hdr => "stddev %", :align => :right, :val => lambda { |key, val| sprintf "%.2f", val['mean']['stddev%'] }}, + ] + + Dir.each_child(data_dir) do |filename| + next unless filename.end_with? "Comparison.log" + + benchmark_name = filename[0, filename.length-"Comparison.log".length] + benchmark_filename = benchmark_name.gsub(/(.)([A-Z])/){$1+"_"+$2}.downcase + ".rb" + + data = {} + lines = File.readlines(File.join(data_dir, filename), chomp: true) + while lines.length > 0 + line = lines.shift + if m = /^Comparing times \((.*)\):$/.match(line) + method = m[1] + method.slice! "call_time_" + elsif m = /^\s*[0-9]+ ([A-Z]\S+) *[0-9]+ repeats:$/.match(line) + name = m[1] + name.slice! benchmark_name + lines.shift + secs_per_call, stddev, _ = lines.shift.split + + data[name] = {} unless data.include? name + data[name][method] = { + 'µs/call' => secs_per_call.to_f * 1e6, + 'stddev%' => stddev.to_f, + } + end + end + + puts + puts "### `#{benchmark_filename}`" + puts + col_widths = cols.map{|col| col[:hdr].length } + data.each do |key, val| + col_widths = (col_widths.zip cols.map{|col|col[:val].call(key, val).length}).map(&:max) + end + fmt = " | " + cols.each_with_index.map{|col, i| col[:align] == :left ? "%-#{col_widths[i]}s" : "%#{col_widths[i]}s"}.join(" | ")+" |\n" + sep = " |" + cols.each_with_index.map{|col, i|sprintf (col[:align] == :left ? ":%s-" : "-%s:"), "-"*col_widths[i]}.join("|")+"|\n" + + printf fmt, *(cols.map{|col| col[:hdr]}) + prev = '' + for key in data.keys.sort + pkg = key.split('#', 2)[0] + if pkg != prev + printf sep + _key = key + else + _key = key[pkg.length,key.length] + end + printf fmt, *(cols.map{|col| col[:val].call(_key, data[key])}) + prev = pkg + end + end +end + +if $0 == __FILE__ + summarize(File.join(File.dirname(__FILE__), 'data')) +end