]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/hana/benchmark/measure.in.rb
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / hana / benchmark / measure.in.rb
1 #!/usr/bin/env ruby
2 #
3 # Copyright Louis Dionne 2013-2017
4 # Distributed under the Boost Software License, Version 1.0.
5 # (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
6 #
7 #
8 # When called as a program, this script runs the command line given in
9 # arguments and returns the total time. This is similar to the `time`
10 # command from Bash.
11 #
12 # This file can also be required as a Ruby module to gain access to the
13 # methods defined below.
14 #
15 # NOTE:
16 # This file must not be used as-is. It must be processed by CMake first.
17
18 require 'benchmark'
19 require 'open3'
20 require 'pathname'
21 require 'ruby-progressbar'
22 require 'tilt'
23
24
25 def split_at(n, list)
26 before = list[0...n] || []
27 after = list[n..-1] || []
28 return [before, after]
29 end
30
31 # types : A sequence of strings to put in the mpl::vector.
32 # Using this method requires including
33 # - <boost/mpl/vector.hpp>
34 # - <boost/mpl/push_back.hpp>
35 def mpl_vector(types)
36 fast, rest = split_at(20, types)
37 rest.inject("boost::mpl::vector#{fast.length}<#{fast.join(', ')}>") { |v, t|
38 "boost::mpl::push_back<#{v}, #{t}>::type"
39 }
40 end
41
42 # types : A sequence of strings to put in the mpl::list.
43 # Using this method requires including
44 # - <boost/mpl/list.hpp>
45 # - <boost/mpl/push_front.hpp>
46 def mpl_list(types)
47 prefix, fast = split_at([types.length - 20, 0].max, types)
48 prefix.reverse.inject("boost::mpl::list#{fast.length}<#{fast.join(', ')}>") { |l, t|
49 "boost::mpl::push_front<#{l}, #{t}>::type"
50 }
51 end
52
53 # values : A sequence of strings representing values to put in the fusion::vector.
54 # Using this method requires including
55 # - <boost/fusion/include/make_vector.hpp>
56 # - <boost/fusion/include/push_back.hpp>
57 def fusion_vector(values)
58 fast, rest = split_at(10, values)
59 rest.inject("boost::fusion::make_vector(#{fast.join(', ')})") { |xs, v|
60 "boost::fusion::push_back(#{xs}, #{v})"
61 }
62 end
63
64 # values : A sequence of strings representing values to put in the fusion::list.
65 # Using this method requires including
66 # - <boost/fusion/include/make_list.hpp>
67 # - <boost/fusion/include/push_back.hpp>
68 def fusion_list(values)
69 fast, rest = split_at(10, values)
70 rest.inject("boost::fusion::make_list(#{fast.join(', ')})") { |xs, v|
71 "boost::fusion::push_back(#{xs}, #{v})"
72 }
73 end
74
75 # Turns a CMake-style boolean into a Ruby boolean.
76 def cmake_bool(b)
77 return true if b.is_a? String and ["true", "yes", "1"].include?(b.downcase)
78 return true if b.is_a? Integer and b > 0
79 return false # otherwise
80 end
81
82 # aspect must be one of :compilation_time, :bloat, :execution_time
83 def measure(aspect, template_relative, range, env = {})
84 measure_file = Pathname.new("#{MEASURE_FILE}")
85 template = Pathname.new(template_relative).expand_path
86 range = range.to_a
87
88 if ENV["BOOST_HANA_JUST_CHECK_BENCHMARKS"] && range.length >= 2
89 range = [range[0], range[-1]]
90 end
91
92 make = -> (target) {
93 command = "@CMAKE_COMMAND@ --build @CMAKE_BINARY_DIR@ --target #{target}"
94 stdout, stderr, status = Open3.capture3(command)
95 }
96
97 progress = ProgressBar.create(format: '%p%% %t | %B |',
98 title: template_relative,
99 total: range.size,
100 output: STDERR)
101 range.map do |n|
102 # Evaluate the ERB template with the given environment, and save
103 # the result in the `measure.cpp` file.
104 code = Tilt::ERBTemplate.new(template).render(nil, input_size: n, env: env)
105 measure_file.write(code)
106
107 # Compile the file and get timing statistics. The timing statistics
108 # are output to stdout when we compile the file because of the way
109 # the `compile.benchmark.measure` CMake target is setup.
110 stdout, stderr, status = make["#{MEASURE_TARGET}"]
111 raise "compilation error: #{stdout}\n\n#{stderr}\n\n#{code}" if not status.success?
112 ctime = stdout.match(/\[compilation time: (.+)\]/i)
113 # Size of the generated executable in KB
114 size = File.size("@CMAKE_CURRENT_BINARY_DIR@/#{MEASURE_TARGET}").to_f / 1000
115
116 # If we didn't match anything, that's because we went too fast, CMake
117 # did not have the time to see the changes to the measure file and
118 # the target was not rebuilt. So we sleep for a bit and then retry
119 # this iteration.
120 (sleep 0.2; redo) if ctime.nil?
121 stat = ctime.captures[0].to_f if aspect == :compilation_time
122 stat = size if aspect == :bloat
123
124 # Run the resulting program and get timing statistics. The statistics
125 # should be written to stdout by the `measure` function of the
126 # `measure.hpp` header.
127 if aspect == :execution_time
128 stdout, stderr, status = make["#{MEASURE_TARGET}.run"]
129 raise "runtime error: #{stderr}\n\n#{code}" if not status.success?
130 match = stdout.match(/\[execution time: (.+)\]/i)
131 if match.nil?
132 raise ("Could not find [execution time: ...] bit in the output. " +
133 "Did you use the `measure` function in the `measure.hpp` header? " +
134 "stdout follows:\n#{stdout}")
135 end
136 stat = match.captures[0].to_f
137 end
138
139 progress.increment
140 [n, stat]
141 end
142 ensure
143 measure_file.write("")
144 progress.finish if progress
145 end
146
147 def time_execution(erb_file, range, env = {})
148 measure(:execution_time, erb_file, range, env)
149 end
150
151 def time_compilation(erb_file, range, env = {})
152 measure(:compilation_time, erb_file, range, env)
153 end
154
155 if __FILE__ == $0
156 command = ARGV.join(' ')
157 time = Benchmark.realtime { `#{command}` }
158
159 puts "[command line: #{command}]"
160 puts "[compilation time: #{time}]"
161 end