diff options
Diffstat (limited to '')
-rwxr-xr-x | src/boost/libs/hana/benchmark/measure.in.rb | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/boost/libs/hana/benchmark/measure.in.rb b/src/boost/libs/hana/benchmark/measure.in.rb new file mode 100755 index 000000000..65e2a98b4 --- /dev/null +++ b/src/boost/libs/hana/benchmark/measure.in.rb @@ -0,0 +1,161 @@ +#!/usr/bin/env ruby +# +# Copyright Louis Dionne 2013-2017 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +# +# +# When called as a program, this script runs the command line given in +# arguments and returns the total time. This is similar to the `time` +# command from Bash. +# +# This file can also be required as a Ruby module to gain access to the +# methods defined below. +# +# NOTE: +# This file must not be used as-is. It must be processed by CMake first. + +require 'benchmark' +require 'open3' +require 'pathname' +require 'ruby-progressbar' +require 'tilt' + + +def split_at(n, list) + before = list[0...n] || [] + after = list[n..-1] || [] + return [before, after] +end + +# types : A sequence of strings to put in the mpl::vector. +# Using this method requires including +# - <boost/mpl/vector.hpp> +# - <boost/mpl/push_back.hpp> +def mpl_vector(types) + fast, rest = split_at(20, types) + rest.inject("boost::mpl::vector#{fast.length}<#{fast.join(', ')}>") { |v, t| + "boost::mpl::push_back<#{v}, #{t}>::type" + } +end + +# types : A sequence of strings to put in the mpl::list. +# Using this method requires including +# - <boost/mpl/list.hpp> +# - <boost/mpl/push_front.hpp> +def mpl_list(types) + prefix, fast = split_at([types.length - 20, 0].max, types) + prefix.reverse.inject("boost::mpl::list#{fast.length}<#{fast.join(', ')}>") { |l, t| + "boost::mpl::push_front<#{l}, #{t}>::type" + } +end + +# values : A sequence of strings representing values to put in the fusion::vector. +# Using this method requires including +# - <boost/fusion/include/make_vector.hpp> +# - <boost/fusion/include/push_back.hpp> +def fusion_vector(values) + fast, rest = split_at(10, values) + rest.inject("boost::fusion::make_vector(#{fast.join(', ')})") { |xs, v| + "boost::fusion::push_back(#{xs}, #{v})" + } +end + +# values : A sequence of strings representing values to put in the fusion::list. +# Using this method requires including +# - <boost/fusion/include/make_list.hpp> +# - <boost/fusion/include/push_back.hpp> +def fusion_list(values) + fast, rest = split_at(10, values) + rest.inject("boost::fusion::make_list(#{fast.join(', ')})") { |xs, v| + "boost::fusion::push_back(#{xs}, #{v})" + } +end + +# Turns a CMake-style boolean into a Ruby boolean. +def cmake_bool(b) + return true if b.is_a? String and ["true", "yes", "1"].include?(b.downcase) + return true if b.is_a? Integer and b > 0 + return false # otherwise +end + +# aspect must be one of :compilation_time, :bloat, :execution_time +def measure(aspect, template_relative, range, env = {}) + measure_file = Pathname.new("#{MEASURE_FILE}") + template = Pathname.new(template_relative).expand_path + range = range.to_a + + if ENV["BOOST_HANA_JUST_CHECK_BENCHMARKS"] && range.length >= 2 + range = [range[0], range[-1]] + end + + make = -> (target) { + command = "@CMAKE_COMMAND@ --build @CMAKE_BINARY_DIR@ --target #{target}" + stdout, stderr, status = Open3.capture3(command) + } + + progress = ProgressBar.create(format: '%p%% %t | %B |', + title: template_relative, + total: range.size, + output: STDERR) + range.map do |n| + # Evaluate the ERB template with the given environment, and save + # the result in the `measure.cpp` file. + code = Tilt::ERBTemplate.new(template).render(nil, input_size: n, env: env) + measure_file.write(code) + + # Compile the file and get timing statistics. The timing statistics + # are output to stdout when we compile the file because of the way + # the `compile.benchmark.measure` CMake target is setup. + stdout, stderr, status = make["#{MEASURE_TARGET}"] + raise "compilation error: #{stdout}\n\n#{stderr}\n\n#{code}" if not status.success? + ctime = stdout.match(/\[compilation time: (.+)\]/i) + # Size of the generated executable in KB + size = File.size("@CMAKE_CURRENT_BINARY_DIR@/#{MEASURE_TARGET}").to_f / 1000 + + # If we didn't match anything, that's because we went too fast, CMake + # did not have the time to see the changes to the measure file and + # the target was not rebuilt. So we sleep for a bit and then retry + # this iteration. + (sleep 0.2; redo) if ctime.nil? + stat = ctime.captures[0].to_f if aspect == :compilation_time + stat = size if aspect == :bloat + + # Run the resulting program and get timing statistics. The statistics + # should be written to stdout by the `measure` function of the + # `measure.hpp` header. + if aspect == :execution_time + stdout, stderr, status = make["#{MEASURE_TARGET}.run"] + raise "runtime error: #{stderr}\n\n#{code}" if not status.success? + match = stdout.match(/\[execution time: (.+)\]/i) + if match.nil? + raise ("Could not find [execution time: ...] bit in the output. " + + "Did you use the `measure` function in the `measure.hpp` header? " + + "stdout follows:\n#{stdout}") + end + stat = match.captures[0].to_f + end + + progress.increment + [n, stat] + end +ensure + measure_file.write("") + progress.finish if progress +end + +def time_execution(erb_file, range, env = {}) + measure(:execution_time, erb_file, range, env) +end + +def time_compilation(erb_file, range, env = {}) + measure(:compilation_time, erb_file, range, env) +end + +if __FILE__ == $0 + command = ARGV.join(' ') + time = Benchmark.realtime { `#{command}` } + + puts "[command line: #{command}]" + puts "[compilation time: #{time}]" +end |