diff --git a/Gemfile b/Gemfile index 94120da85..394919fd6 100644 --- a/Gemfile +++ b/Gemfile @@ -1,80 +1,86 @@ source ENV['GEM_SOURCE'] || "https://rubygems.org" def location_for(place, fake_version = nil) if place =~ /^(git[:@][^#]*)#(.*)/ [fake_version, { :git => $1, :branch => $2, :require => false }].compact elsif place =~ /^file:\/\/(.*)/ ['>= 0', { :path => File.expand_path($1), :require => false }] else [place, { :require => false }] end end # C Ruby (MRI) or Rubinius, but NOT Windows platforms :ruby do gem 'pry', :group => :development gem 'yard', :group => :development gem 'redcarpet', '~> 2.0', :group => :development gem "racc", "1.4.9", :group => :development end gem "puppet", :path => File.dirname(__FILE__), :require => false gem "facter", *location_for(ENV['FACTER_LOCATION'] || '~> 1.6') gem "hiera", *location_for(ENV['HIERA_LOCATION'] || '~> 1.0') gem "rake", :require => false gem "rgen", "0.6.5", :require => false group(:development, :test) do # Jenkins workers may be using RSpec 2.9, so RSpec 2.11 syntax # (like `expect(value).to eq matcher`) should be avoided. gem "rspec", "~> 2.11.0", :require => false # Mocha is not compatible across minor version changes; because of this only # versions matching ~> 0.10.5 are supported. All other versions are unsupported # and can be expected to fail. gem "mocha", "~> 0.10.5", :require => false gem "yarjuf", "~> 1.0" # json-schema does not support windows, so use the 'ruby' platform to exclude it on windows platforms :ruby do # json-schema uses multi_json, but chokes with multi_json 1.7.9, so prefer 1.7.7 gem "multi_json", "1.7.7", :require => false gem "json-schema", "2.1.1", :require => false end + case RUBY_VERSION + when /^1.8/ + gem 'ruby-prof', "~> 0.13.1" + else + gem 'ruby-prof' + end end group(:extra) do gem "rack", "~> 1.4", :require => false gem "activerecord", '~> 3.2', :require => false gem "couchrest", '~> 1.0', :require => false gem "net-ssh", '~> 2.1', :require => false gem "puppetlabs_spec_helper", :require => false gem "sqlite3", :require => false gem "stomp", :require => false gem "tzinfo", :require => false gem "msgpack", :require => false end require 'yaml' data = YAML.load_file(File.join(File.dirname(__FILE__), 'ext', 'project_data.yaml')) bundle_platforms = data['bundle_platforms'] data['gem_platform_dependencies'].each_pair do |gem_platform, info| if bundle_deps = info['gem_runtime_dependencies'] bundle_platform = bundle_platforms[gem_platform] or raise "Missing bundle_platform" platform(bundle_platform.intern) do bundle_deps.each_pair do |name, version| gem(name, version, :require => false) end end end end if File.exists? "#{__FILE__}.local" eval(File.read("#{__FILE__}.local"), binding) end # vim:filetype=ruby diff --git a/benchmarks/many_modules/benchmarker.rb b/benchmarks/many_modules/benchmarker.rb new file mode 100644 index 000000000..8540f85b1 --- /dev/null +++ b/benchmarks/many_modules/benchmarker.rb @@ -0,0 +1,77 @@ +require 'erb' +require 'ostruct' +require 'fileutils' +require 'json' + +class Benchmarker + include FileUtils + + def initialize(target, size) + @target = target + @size = size + end + + def setup + require 'puppet' + config = File.join(@target, 'puppet.conf') + Puppet.initialize_settings(['--config', config]) + end + + def run + env = Puppet.lookup(:environments).get('benchmarking') + node = Puppet::Node.new("testing", :environment => env) + Puppet::Resource::Catalog.indirection.find("testing", :use_node => node) + end + + def generate + environment = File.join(@target, 'environments', 'benchmarking') + templates = File.join('benchmarks', 'many_modules') + + mkdir_p(File.join(environment, 'modules')) + mkdir_p(File.join(environment, 'manifests')) + + render(File.join(templates, 'site.pp.erb'), + File.join(environment, 'manifests', 'site.pp'), + :size => @size) + + @size.times do |i| + module_name = "module#{i}" + module_base = File.join(environment, 'modules', module_name) + manifests = File.join(module_base, 'manifests') + + mkdir_p(manifests) + + File.open(File.join(module_base, 'metadata.json'), 'w') do |f| + JSON.dump({ + "types" => [], + "source" => "", + "author" => "ManyModules Benchmark", + "license" => "Apache 2.0", + "version" => "1.0.0", + "description" => "Many Modules benchmark module #{i}", + "summary" => "Just this benchmark module, you know?", + "dependencies" => [], + }, f) + end + + render(File.join(templates, 'module', 'init.pp.erb'), + File.join(manifests, 'init.pp'), + :name => module_name) + + render(File.join(templates, 'module', 'internal.pp.erb'), + File.join(manifests, 'internal.pp'), + :name => module_name) + end + + render(File.join(templates, 'puppet.conf.erb'), + File.join(@target, 'puppet.conf'), + :location => @target) + end + + def render(erb_file, output_file, bindings) + site = ERB.new(File.read(erb_file)) + File.open(output_file, 'w') do |fh| + fh.write(site.result(OpenStruct.new(bindings).instance_eval { binding })) + end + end +end diff --git a/benchmarks/many_modules/description b/benchmarks/many_modules/description new file mode 100644 index 000000000..a2b58ebe7 --- /dev/null +++ b/benchmarks/many_modules/description @@ -0,0 +1,3 @@ +Benchmark scenario: many manifests spread across many modules. +Benchmark target: catalog compilation. + diff --git a/benchmarks/many_modules/module/init.pp.erb b/benchmarks/many_modules/module/init.pp.erb new file mode 100644 index 000000000..49fca07ba --- /dev/null +++ b/benchmarks/many_modules/module/init.pp.erb @@ -0,0 +1,3 @@ +class <%= name %> { + class { "<%= name %>::internal": } +} diff --git a/benchmarks/many_modules/module/internal.pp.erb b/benchmarks/many_modules/module/internal.pp.erb new file mode 100644 index 000000000..83a36947d --- /dev/null +++ b/benchmarks/many_modules/module/internal.pp.erb @@ -0,0 +1,3 @@ +class <%= name %>::internal { + notify { "<%= name %>::internal": } +} diff --git a/benchmarks/many_modules/puppet.conf.erb b/benchmarks/many_modules/puppet.conf.erb new file mode 100644 index 000000000..e0c5d8588 --- /dev/null +++ b/benchmarks/many_modules/puppet.conf.erb @@ -0,0 +1,3 @@ +confdir = <%= location %> +vardir = <%= location %> +environmentpath = <%= File.join(location, 'environments') %> diff --git a/benchmarks/many_modules/site.pp.erb b/benchmarks/many_modules/site.pp.erb new file mode 100644 index 000000000..9f6c9668a --- /dev/null +++ b/benchmarks/many_modules/site.pp.erb @@ -0,0 +1,3 @@ +<% size.times do |i| %> + include module<%= i %> +<% end %> diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake new file mode 100644 index 000000000..c59a3213a --- /dev/null +++ b/tasks/benchmark.rake @@ -0,0 +1,69 @@ +require 'benchmark' +require 'tmpdir' + +namespace :benchmark do + def generate_scenario_tasks(location, name) + desc File.read(File.join(location, 'description')) + task name => "#{name}:run" + + namespace name do + task :setup do + ENV['ITERATIONS'] ||= '10' + ENV['SIZE'] ||= '100' + ENV['TARGET'] ||= Dir.mktmpdir(name) + ENV['TARGET'] = File.expand_path(ENV['TARGET']) + + mkdir_p(ENV['TARGET']) + + require File.expand_path(File.join(location, 'benchmarker.rb')) + + @benchmark = Benchmarker.new(ENV['TARGET'], ENV['SIZE'].to_i) + end + + desc "Generate the #{name} scenario." + task :generate => :setup do + @benchmark.generate + @benchmark.setup + end + + desc "Run the #{name} scenario." + task :run => :generate do + format = if RUBY_VERSION =~ /^1\.8/ + Benchmark::FMTSTR + else + Benchmark::FORMAT + end + Benchmark.benchmark(Benchmark::CAPTION, 10, format, "> total:", "> avg:") do |b| + times = [] + ENV['ITERATIONS'].to_i.times do |i| + times << b.report("Run #{i + 1}") do + @benchmark.run + end + end + + sum = times.inject(Benchmark::Tms.new, &:+) + + [sum, sum / times.length] + end + end + + desc "Profile a single run of the #{name} scenario." + task :profile => :generate do + require 'ruby-prof' + + result = RubyProf.profile do + @benchmark.run + end + + printer = RubyProf::CallTreePrinter.new(result) + File.open(File.join("callgrind.#{name}.#{Time.now.to_i}.trace"), "w") do |f| + printer.print(f) + end + end + end + end + + Dir.glob('benchmarks/*') do |location| + generate_scenario_tasks(location, File.basename(location)) + end +end