benchcc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.gitmodules +6 -0
- data/Gemfile +13 -0
- data/LICENSE.md +25 -0
- data/README.md +3 -0
- data/Rakefile +11 -0
- data/benchcc.gemspec +24 -0
- data/benchmarks/Rakefile +78 -0
- data/benchmarks/fmap/_env.rb +3 -0
- data/benchmarks/fmap/_main.erb +7 -0
- data/benchmarks/fmap/hana_list.erb.cpp +18 -0
- data/benchmarks/fmap/mpl11_list.erb.cpp +15 -0
- data/benchmarks/fmap/mpl_list.erb.cpp +15 -0
- data/benchmarks/fmap/mpl_vector.erb.cpp +15 -0
- data/benchmarks/include/_env.rb +1 -0
- data/benchmarks/include/hana.erb.cpp +1 -0
- data/benchmarks/include/mpl.erb.cpp +185 -0
- data/benchmarks/include/mpl11.erb.cpp +1 -0
- data/benchmarks/include/mpl11.min.erb.cpp +1 -0
- data/benchmarks/sum/_env.rb +3 -0
- data/benchmarks/sum/_main.erb +4 -0
- data/benchmarks/sum/constexpr.erb.cpp +24 -0
- data/benchmarks/sum/mpl11_variadic_foldl.erb.cpp +16 -0
- data/benchmarks/tuple_create/_env.rb +3 -0
- data/benchmarks/tuple_create/boost_tuple.erb.cpp +6 -0
- data/benchmarks/tuple_create/fusion_tuple.erb.cpp +6 -0
- data/benchmarks/tuple_create/hana_list.erb.cpp +6 -0
- data/benchmarks/tuple_create/std_tuple.erb.cpp +6 -0
- data/benchmarks/tuple_last/_env.rb +3 -0
- data/benchmarks/tuple_last/boost_tuple.erb.cpp +7 -0
- data/benchmarks/tuple_last/fusion_tuple.erb.cpp +7 -0
- data/benchmarks/tuple_last/hana_list.erb.cpp +7 -0
- data/benchmarks/tuple_last/std_tuple.erb.cpp +7 -0
- data/benchmarks/type_foldl/_env.rb +3 -0
- data/benchmarks/type_foldl/_main.erb +18 -0
- data/benchmarks/type_foldl/hana_list.erb.cpp +15 -0
- data/benchmarks/type_foldl/mpl11_cons.erb.cpp +10 -0
- data/benchmarks/type_foldl/mpl11_list.erb.cpp +8 -0
- data/benchmarks/type_foldl/mpl_list.erb.cpp +7 -0
- data/benchmarks/type_foldl/mpl_vector.erb.cpp +7 -0
- data/benchmarks/value_foldl/_env.rb +3 -0
- data/benchmarks/value_foldl/_main.erb +17 -0
- data/benchmarks/value_foldl/fusion_cons.erb.cpp +8 -0
- data/benchmarks/value_foldl/fusion_list.erb.cpp +9 -0
- data/benchmarks/value_foldl/fusion_vector.erb.cpp +9 -0
- data/benchmarks/value_foldl/hana_list.erb.cpp +6 -0
- data/lib/benchcc/benchmark.rb +53 -0
- data/lib/benchcc/compiler.rb +134 -0
- data/lib/benchcc/ext/enumerable.rb +7 -0
- data/lib/benchcc/ext/file.rb +16 -0
- data/lib/benchcc/ext/gnuplot.rb +8 -0
- data/lib/benchcc/ext/numeric.rb +15 -0
- data/lib/benchcc/ext/string.rb +5 -0
- data/lib/benchcc/fusion.rb +54 -0
- data/lib/benchcc/mpl.rb +48 -0
- data/lib/benchcc/plot.rb +44 -0
- data/lib/benchcc/version.rb +3 -0
- data/lib/benchcc.rb +6 -0
- data/spec/benchmark_spec.rb +23 -0
- data/spec/compiler_spec.rb +69 -0
- data/spec/numeric_spec.rb +23 -0
- data/spec/plot_spec.rb +29 -0
- data/spec/src/invalid.cpp +1 -0
- data/spec/src/plot.csv +3 -0
- data/spec/src/valid.cpp +1 -0
- metadata +166 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
template <typename ...>
|
2
|
+
struct result { };
|
3
|
+
|
4
|
+
constexpr struct {
|
5
|
+
template <typename State, typename X>
|
6
|
+
constexpr result<State, X> operator()(State, X) const
|
7
|
+
{ return {}; }
|
8
|
+
} f{};
|
9
|
+
|
10
|
+
<%= (0...breadth).map { |breadth|
|
11
|
+
xs = (0...depth).map { |depth| "x#{breadth}<#{depth}>" }
|
12
|
+
<<-EOS
|
13
|
+
constexpr struct { } state#{breadth}{};
|
14
|
+
template <int> struct x#{breadth} { };
|
15
|
+
static const auto go#{breadth} = #{yield 'f', "state#{breadth}", xs};
|
16
|
+
EOS
|
17
|
+
}.join("\n") %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= Benchcc::Fusion::Vector.new(0...depth).includes %>
|
2
|
+
|
3
|
+
#include <boost/fusion/algorithm/iteration/fold.hpp>
|
4
|
+
|
5
|
+
|
6
|
+
<%= render('_main.erb') do |f, state, xs|
|
7
|
+
vector = Benchcc::Fusion::Vector.new(xs)
|
8
|
+
"boost::fusion::fold(#{vector}{}, #{state}, #{f})"
|
9
|
+
end %>
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'compiler'
|
2
|
+
require 'csv'
|
3
|
+
require 'pathname'
|
4
|
+
require 'ruby-progressbar'
|
5
|
+
require 'tilt'
|
6
|
+
require 'timeout'
|
7
|
+
|
8
|
+
|
9
|
+
module Benchcc
|
10
|
+
class Renderer
|
11
|
+
def initialize(relative_to)
|
12
|
+
@relative_to = Pathname.new(relative_to)
|
13
|
+
@locals = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(file, **locals, &block)
|
17
|
+
@locals.merge!(locals)
|
18
|
+
file = Pathname.new(file).expand_path(@relative_to)
|
19
|
+
Tilt::ERBTemplate.new(file).render(self, **@locals, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def benchmark(file, envs, timeout: 10, relative_to: File.dirname(file), &block)
|
24
|
+
progress = ProgressBar.create(format: "#{file} %p%% | %B |",
|
25
|
+
total: envs.size)
|
26
|
+
consecutive_errors, data = 0, []
|
27
|
+
envs.each do |env|
|
28
|
+
break if consecutive_errors >= 2
|
29
|
+
code = Renderer.new(relative_to).render(file, **env)
|
30
|
+
begin
|
31
|
+
Timeout::timeout(timeout) { data << env.merge(block.call(code)) }
|
32
|
+
consecutive_errors = 0
|
33
|
+
rescue CompilationError, Timeout::Error => e
|
34
|
+
$stderr << e
|
35
|
+
consecutive_errors += 1
|
36
|
+
end
|
37
|
+
progress.increment
|
38
|
+
end
|
39
|
+
return data
|
40
|
+
ensure
|
41
|
+
progress.finish
|
42
|
+
end
|
43
|
+
module_function :benchmark
|
44
|
+
|
45
|
+
def benchmark_to_csv(file, envs, out, timeout: 10, relative_to: File.dirname(file), &block)
|
46
|
+
data = benchmark(file, envs, timeout: timeout, relative_to: relative_to, &block)
|
47
|
+
CSV.open(out, 'wb') do |csv|
|
48
|
+
csv << data.first.keys unless data.empty?
|
49
|
+
data.each { |line| csv << line.values }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
module_function :benchmark_to_csv
|
53
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'pathname'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
|
6
|
+
module Benchcc
|
7
|
+
class CompilationError < RuntimeError
|
8
|
+
def initialize(command_line, code, compiler_error_message)
|
9
|
+
@cli = command_line
|
10
|
+
@code = code
|
11
|
+
@compiler_stderr = compiler_error_message
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
<<-EOS
|
16
|
+
compilation failed when invoking "#{@cli}"
|
17
|
+
compiler error message was:
|
18
|
+
#{'-' * 80}
|
19
|
+
#{@compiler_stderr}
|
20
|
+
|
21
|
+
full compiled file was:
|
22
|
+
#{'-' * 80}
|
23
|
+
#{@code}
|
24
|
+
EOS
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Basic interface to compiler frontends.
|
29
|
+
class Compiler
|
30
|
+
# Maximum template recursion depth supported by the compiler.
|
31
|
+
def template_depth
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
# Maximum constexpr recursion depth supported by the compiler.
|
36
|
+
def constexpr_depth
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# Show the name and the version of the compiler.
|
41
|
+
def to_s
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
44
|
+
|
45
|
+
# compile_file: Path -> Hash
|
46
|
+
#
|
47
|
+
# Compile the given file and return compilation statistics.
|
48
|
+
#
|
49
|
+
# Additional compiler-specific arguments may be specified.
|
50
|
+
#
|
51
|
+
# A `CompilationError` is be raised if the compilation fails for
|
52
|
+
# whatever reason. Either this method or `compile_code` must be
|
53
|
+
# implemented in subclasses.
|
54
|
+
def compile_file(file, *args)
|
55
|
+
raise ArgumentError, "invalid filename #{file}" unless File.file? file
|
56
|
+
code = File.read(file)
|
57
|
+
compile_code(code, *args)
|
58
|
+
end
|
59
|
+
|
60
|
+
# compile_code: String -> Hash
|
61
|
+
#
|
62
|
+
# Compile the given string and return compilation statistics.
|
63
|
+
#
|
64
|
+
# This method has the same behavior as `compile_file`, except the code
|
65
|
+
# is given as-is instead of being in a file. Either this method or
|
66
|
+
# `compile_file` must be implemented in subclasses.
|
67
|
+
def compile_code(code, *args)
|
68
|
+
tmp = Tempfile.new(["", '.cpp'])
|
69
|
+
tmp.write(code)
|
70
|
+
tmp.close
|
71
|
+
compile_file(tmp.path, *args)
|
72
|
+
ensure
|
73
|
+
tmp.unlink
|
74
|
+
end
|
75
|
+
end # class Compiler
|
76
|
+
|
77
|
+
class Clang < Compiler
|
78
|
+
def initialize(binary)
|
79
|
+
@exe = `which #{binary}`.strip
|
80
|
+
raise "#{binary} not found" unless $?.success?
|
81
|
+
end
|
82
|
+
|
83
|
+
def compile_file(file, *args)
|
84
|
+
file = Pathname.new(file).expand_path
|
85
|
+
command = "time -l #{@exe} #{args.join(' ')} -ftime-report #{file}"
|
86
|
+
stdout, stderr, status = Open3.capture3(command)
|
87
|
+
raise CompilationError.new(command, file.read, stderr) unless status.success?
|
88
|
+
|
89
|
+
return {
|
90
|
+
peak_memusg: stderr.match(/(\d+)\s+maximum/)[1].to_i,
|
91
|
+
wall_time: stderr.match(/.+Total/).to_s.split[-3].to_f
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def template_depth; 256; end
|
96
|
+
def constexpr_depth; 512; end
|
97
|
+
end
|
98
|
+
|
99
|
+
class GCC < Compiler
|
100
|
+
def initialize(binary)
|
101
|
+
@exe = `which #{binary}`.strip
|
102
|
+
raise "#{binary} not found" unless $?.success?
|
103
|
+
end
|
104
|
+
|
105
|
+
def compile_file(file, *args)
|
106
|
+
file = Pathname.new(file).expand_path
|
107
|
+
command = "time -l #{@exe} #{args.join(' ')} -ftime-report #{file}"
|
108
|
+
stdout, stderr, status = Open3.capture3(command)
|
109
|
+
raise CompilationError.new(command, file.read, stderr) unless status.success?
|
110
|
+
|
111
|
+
return {
|
112
|
+
peak_memusg: stderr.match(/(\d+)\s+maximum/)[1].to_i,
|
113
|
+
wall_time: stderr.match(/TOTAL.+/).to_s.split[-3].to_f
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
def template_depth; 900; end
|
118
|
+
def constexpr_depth; 512; end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Compiler
|
122
|
+
def self.guess_from_binary(binary)
|
123
|
+
stdout, stderr, status = Open3.capture3("#{binary} --version")
|
124
|
+
case stdout
|
125
|
+
when /\(GCC\)/
|
126
|
+
return GCC.new(binary)
|
127
|
+
when /clang/
|
128
|
+
return Clang.new(binary)
|
129
|
+
else
|
130
|
+
raise ArgumentError("unknown compiler #{binary}")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
|
4
|
+
class File
|
5
|
+
def self.remove_ext(path)
|
6
|
+
path.chomp(extname(path))
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.sub_ext(path, ext)
|
10
|
+
Pathname.new(path).sub_ext(ext).to_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.basename_we(path)
|
14
|
+
File.basename(File.remove_ext(path))
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Numeric
|
2
|
+
# round_up: Numeric -> Numeric
|
3
|
+
#
|
4
|
+
# Round up the integer to a given precision in decimal digits (default 0
|
5
|
+
# digits). This is similar to `round`, except that rounding is always done
|
6
|
+
# upwards.
|
7
|
+
def round_up(ndigits = 0)
|
8
|
+
k = 10 ** ndigits
|
9
|
+
if self % k == 0
|
10
|
+
self
|
11
|
+
else
|
12
|
+
(1 + self/k) * k
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'ext/enumerable'
|
2
|
+
require_relative 'ext/numeric'
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
|
6
|
+
module Benchcc
|
7
|
+
module Fusion
|
8
|
+
class List < SimpleDelegator
|
9
|
+
def includes
|
10
|
+
inc = ["#define FUSION_MAX_LIST_SIZE 50"]
|
11
|
+
inc << headers.map { |hdr| "#include <#{hdr}>" }
|
12
|
+
inc.join("\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
def headers
|
16
|
+
["boost/fusion/container/list/list.hpp"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"boost::fusion::list<#{join(', ')}>"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Cons < SimpleDelegator
|
25
|
+
def includes
|
26
|
+
headers.map { |hdr| "#include <#{hdr}>" }.join("\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
def headers
|
30
|
+
["boost/fusion/container/list/cons.hpp"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
foldr('boost::fusion::nil_') do |head, tail|
|
35
|
+
"boost::fusion::cons<#{head}, #{tail}>"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Vector < SimpleDelegator
|
41
|
+
def includes
|
42
|
+
headers.map { |hdr| "#include <#{hdr}>" }.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
def headers
|
46
|
+
["boost/fusion/container/vector/vector#{[10, size.round_up(1)].max}.hpp"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"boost::fusion::vector#{size}<#{join(', ')}>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/benchcc/mpl.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative 'ext/numeric'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
|
5
|
+
module Benchcc
|
6
|
+
module MPL
|
7
|
+
class Sequence < SimpleDelegator
|
8
|
+
def includes
|
9
|
+
headers.map { |hdr| "#include <#{hdr}>" }.join("\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
def headers(name)
|
13
|
+
[
|
14
|
+
"boost/mpl/#{name}/#{name}#{[size.round_up(1), 50].min}.hpp",
|
15
|
+
size > 50 ? "boost/mpl/#{name}/aux_/item.hpp" : nil
|
16
|
+
].compact
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Vector < Sequence
|
21
|
+
def to_s
|
22
|
+
initial, rest = map(&:to_s).take(50), map(&:to_s).drop(50)
|
23
|
+
vectorN = "boost::mpl::vector#{initial.size}<#{initial.join(', ')}>"
|
24
|
+
rest.reduce(vectorN) do |tail, x|
|
25
|
+
"boost::mpl::v_item<#{x}, #{tail}, 0>" # we emulate mpl::push_back
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def headers; super 'vector'; end
|
30
|
+
end
|
31
|
+
|
32
|
+
class List < Sequence
|
33
|
+
def to_s
|
34
|
+
tail = map(&:to_s).last(50)
|
35
|
+
init = map(&:to_s).first([size - tail.size, 0].max)
|
36
|
+
listN = "boost::mpl::list#{tail.size}<#{tail.join(', ')}>"
|
37
|
+
|
38
|
+
init.reverse.zip(51..Float::INFINITY).reduce(listN) do |tail, x_size|
|
39
|
+
x, size = x_size
|
40
|
+
# we emulate mpl::push_front
|
41
|
+
"boost::mpl::l_item<boost::mpl::long_<#{size}>, #{x}, #{tail}>"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def headers; super 'list'; end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/benchcc/plot.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'gnuplot'
|
3
|
+
|
4
|
+
|
5
|
+
module Benchcc
|
6
|
+
def plot_memusg(output, *inputs)
|
7
|
+
Gnuplot.open do |io|
|
8
|
+
Gnuplot::Plot.new(io) do |plot|
|
9
|
+
plot.ylabel 'Memory usage'
|
10
|
+
plot.decimal "locale 'en_US.UTF-8'"
|
11
|
+
plot.format 'y "%.2e kb"'
|
12
|
+
plot.term 'png'
|
13
|
+
plot.output output
|
14
|
+
plot.data = inputs.map { |file|
|
15
|
+
csv = CSV.table(file)
|
16
|
+
Gnuplot::DataSet.new([csv[:x], csv[:peak_memusg]]) { |ds|
|
17
|
+
ds.title = file
|
18
|
+
ds.with = 'lines'
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
module_function :plot_memusg
|
25
|
+
|
26
|
+
def plot_time(output, *inputs)
|
27
|
+
Gnuplot.open do |io|
|
28
|
+
Gnuplot::Plot.new(io) do |plot|
|
29
|
+
plot.ylabel 'Compilation time'
|
30
|
+
plot.format 'y "%.2g s"'
|
31
|
+
plot.term 'png'
|
32
|
+
plot.output output
|
33
|
+
plot.data = inputs.map { |file|
|
34
|
+
csv = CSV.table(file)
|
35
|
+
Gnuplot::DataSet.new([csv[:x], csv[:wall_time]]) { |ds|
|
36
|
+
ds.title = file
|
37
|
+
ds.with = 'lines'
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
module_function :plot_time
|
44
|
+
end
|
data/lib/benchcc.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'benchcc/benchmark'
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
|
8
|
+
describe Benchcc.method(:benchmark) do
|
9
|
+
it('time outs are ignored') {
|
10
|
+
timeout = 0.3
|
11
|
+
envs = [{time: timeout + 0.1}, {time: timeout - 0.1},
|
12
|
+
{time: timeout + 0.2}, {time: timeout - 0.2}]
|
13
|
+
Tempfile.create('') do |file|
|
14
|
+
file.write('<%= time %>')
|
15
|
+
file.flush
|
16
|
+
data = Benchcc.benchmark(file.path, envs, timeout: timeout) do |code|
|
17
|
+
sleep(code.to_f)
|
18
|
+
{time: code.to_f}
|
19
|
+
end
|
20
|
+
expect(data).to eq(envs.select { |env| env[:time] < timeout })
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'benchcc/compiler'
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'rspec'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
|
8
|
+
describe Benchcc::Compiler do
|
9
|
+
describe :compile_file do
|
10
|
+
it {
|
11
|
+
class Mock < Benchcc::Compiler
|
12
|
+
def compile_code(code); code; end
|
13
|
+
end
|
14
|
+
|
15
|
+
file = Tempfile.new('')
|
16
|
+
file.write("whatever")
|
17
|
+
expect(Mock.new.compile_file(file.path)).to eq(file.read)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
describe :compile_code do
|
22
|
+
it {
|
23
|
+
class Mock < Benchcc::Compiler
|
24
|
+
def compile_file(file); File.read(file); end
|
25
|
+
end
|
26
|
+
|
27
|
+
expect(Mock.new.compile_code("whatever")).to eq("whatever")
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
describe :guess_from_binary do
|
32
|
+
it('can guess clang') {
|
33
|
+
expect(Benchcc::Compiler.guess_from_binary(`which clang`.strip)).to be_instance_of(Benchcc::Clang)
|
34
|
+
}
|
35
|
+
|
36
|
+
# GCC is aliased to clang on OSX, so this test fails.
|
37
|
+
# it('can guess gcc') {
|
38
|
+
# expect(Benchcc::Compiler.guess_from_binary(`which gcc`.strip)).to be_instance_of(Benchcc::GCC)
|
39
|
+
# }
|
40
|
+
|
41
|
+
it('fails otherwise') {
|
42
|
+
expect { Benchcc::Compiler.guess_from_binary('not_a_compiler') }.to raise_error
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
[[Benchcc::Clang, 'clang++'], [Benchcc::GCC, 'g++']].each do |compiler, which|
|
48
|
+
describe compiler do
|
49
|
+
before(:each) {
|
50
|
+
@cc = compiler.new(which)
|
51
|
+
@invalid = Pathname.new('src/invalid.cpp').expand_path(File.dirname(__FILE__))
|
52
|
+
@valid = Pathname.new('src/valid.cpp').expand_path(File.dirname(__FILE__))
|
53
|
+
}
|
54
|
+
|
55
|
+
describe :compile_file do
|
56
|
+
it('fails cleanly on invalid input') {
|
57
|
+
expect {
|
58
|
+
@cc.compile_file(@invalid)
|
59
|
+
}.to raise_error(Benchcc::CompilationError)
|
60
|
+
}
|
61
|
+
|
62
|
+
it('returns statistics on valid input') {
|
63
|
+
result = @cc.compile_file(@valid, '-o /dev/null')
|
64
|
+
expect(result).to have_key(:peak_memusg)
|
65
|
+
expect(result).to have_key(:wall_time)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "benchcc/ext/numeric"
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
|
5
|
+
|
6
|
+
describe Numeric do
|
7
|
+
describe :round_up do
|
8
|
+
it {
|
9
|
+
-150.upto(150) do |n|
|
10
|
+
expect(n.round_up).to eq(n)
|
11
|
+
end
|
12
|
+
}
|
13
|
+
|
14
|
+
it {
|
15
|
+
(10..150).step(10) do |tenths|
|
16
|
+
tenths.downto(tenths-9) do |n|
|
17
|
+
expect(n.round_up(1)).to eq(tenths)
|
18
|
+
end
|
19
|
+
expect((tenths-10).round_up(1)).to eq(tenths-10)
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
data/spec/plot_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'benchcc/plot'
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'rspec'
|
5
|
+
|
6
|
+
|
7
|
+
describe 'plots' do
|
8
|
+
before :each do
|
9
|
+
@tmpdir = Dir.mktmpdir
|
10
|
+
@out = File.join(@tmpdir, 'plot.png')
|
11
|
+
@csv = Pathname.new('src/plot.csv').expand_path(File.dirname(__FILE__))
|
12
|
+
end
|
13
|
+
|
14
|
+
after :each do
|
15
|
+
FileUtils.remove_entry(@tmpdir)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Benchcc.method(:plot_memusg) do
|
19
|
+
it {
|
20
|
+
expect { Benchcc.plot_memusg(@out, @csv) }.not_to raise_error
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Benchcc.method(:plot_time) do
|
25
|
+
it {
|
26
|
+
expect { Benchcc.plot_time(@out, @csv) }.not_to raise_error
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
int main() {
|
data/spec/src/plot.csv
ADDED
data/spec/src/valid.cpp
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
int main() {}
|