benchcc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.gitmodules +6 -0
  4. data/Gemfile +13 -0
  5. data/LICENSE.md +25 -0
  6. data/README.md +3 -0
  7. data/Rakefile +11 -0
  8. data/benchcc.gemspec +24 -0
  9. data/benchmarks/Rakefile +78 -0
  10. data/benchmarks/fmap/_env.rb +3 -0
  11. data/benchmarks/fmap/_main.erb +7 -0
  12. data/benchmarks/fmap/hana_list.erb.cpp +18 -0
  13. data/benchmarks/fmap/mpl11_list.erb.cpp +15 -0
  14. data/benchmarks/fmap/mpl_list.erb.cpp +15 -0
  15. data/benchmarks/fmap/mpl_vector.erb.cpp +15 -0
  16. data/benchmarks/include/_env.rb +1 -0
  17. data/benchmarks/include/hana.erb.cpp +1 -0
  18. data/benchmarks/include/mpl.erb.cpp +185 -0
  19. data/benchmarks/include/mpl11.erb.cpp +1 -0
  20. data/benchmarks/include/mpl11.min.erb.cpp +1 -0
  21. data/benchmarks/sum/_env.rb +3 -0
  22. data/benchmarks/sum/_main.erb +4 -0
  23. data/benchmarks/sum/constexpr.erb.cpp +24 -0
  24. data/benchmarks/sum/mpl11_variadic_foldl.erb.cpp +16 -0
  25. data/benchmarks/tuple_create/_env.rb +3 -0
  26. data/benchmarks/tuple_create/boost_tuple.erb.cpp +6 -0
  27. data/benchmarks/tuple_create/fusion_tuple.erb.cpp +6 -0
  28. data/benchmarks/tuple_create/hana_list.erb.cpp +6 -0
  29. data/benchmarks/tuple_create/std_tuple.erb.cpp +6 -0
  30. data/benchmarks/tuple_last/_env.rb +3 -0
  31. data/benchmarks/tuple_last/boost_tuple.erb.cpp +7 -0
  32. data/benchmarks/tuple_last/fusion_tuple.erb.cpp +7 -0
  33. data/benchmarks/tuple_last/hana_list.erb.cpp +7 -0
  34. data/benchmarks/tuple_last/std_tuple.erb.cpp +7 -0
  35. data/benchmarks/type_foldl/_env.rb +3 -0
  36. data/benchmarks/type_foldl/_main.erb +18 -0
  37. data/benchmarks/type_foldl/hana_list.erb.cpp +15 -0
  38. data/benchmarks/type_foldl/mpl11_cons.erb.cpp +10 -0
  39. data/benchmarks/type_foldl/mpl11_list.erb.cpp +8 -0
  40. data/benchmarks/type_foldl/mpl_list.erb.cpp +7 -0
  41. data/benchmarks/type_foldl/mpl_vector.erb.cpp +7 -0
  42. data/benchmarks/value_foldl/_env.rb +3 -0
  43. data/benchmarks/value_foldl/_main.erb +17 -0
  44. data/benchmarks/value_foldl/fusion_cons.erb.cpp +8 -0
  45. data/benchmarks/value_foldl/fusion_list.erb.cpp +9 -0
  46. data/benchmarks/value_foldl/fusion_vector.erb.cpp +9 -0
  47. data/benchmarks/value_foldl/hana_list.erb.cpp +6 -0
  48. data/lib/benchcc/benchmark.rb +53 -0
  49. data/lib/benchcc/compiler.rb +134 -0
  50. data/lib/benchcc/ext/enumerable.rb +7 -0
  51. data/lib/benchcc/ext/file.rb +16 -0
  52. data/lib/benchcc/ext/gnuplot.rb +8 -0
  53. data/lib/benchcc/ext/numeric.rb +15 -0
  54. data/lib/benchcc/ext/string.rb +5 -0
  55. data/lib/benchcc/fusion.rb +54 -0
  56. data/lib/benchcc/mpl.rb +48 -0
  57. data/lib/benchcc/plot.rb +44 -0
  58. data/lib/benchcc/version.rb +3 -0
  59. data/lib/benchcc.rb +6 -0
  60. data/spec/benchmark_spec.rb +23 -0
  61. data/spec/compiler_spec.rb +69 -0
  62. data/spec/numeric_spec.rb +23 -0
  63. data/spec/plot_spec.rb +29 -0
  64. data/spec/src/invalid.cpp +1 -0
  65. data/spec/src/plot.csv +3 -0
  66. data/spec/src/valid.cpp +1 -0
  67. metadata +166 -0
@@ -0,0 +1,8 @@
1
+ #define BOOST_MPL11_NO_ASSERTIONS
2
+ #include <boost/mpl11/list.hpp>
3
+
4
+
5
+ <%= render('_main.erb') do |f, state, xs|
6
+ list = "boost::mpl11::list<#{xs.join(', ')}>"
7
+ "boost::mpl11::foldl<#{f}, #{state}, #{list}>::type"
8
+ end %>
@@ -0,0 +1,7 @@
1
+ #include <boost/mpl/fold.hpp>
2
+ <%= Benchcc::MPL::List.new(0...depth).includes %>
3
+
4
+
5
+ <%= render('_main.erb') do |f, state, xs|
6
+ "boost::mpl::fold<#{Benchcc::MPL::List.new(xs)}, #{state}, #{f}>::type"
7
+ end %>
@@ -0,0 +1,7 @@
1
+ #include <boost/mpl/fold.hpp>
2
+ <%= Benchcc::MPL::Vector.new(0...depth).includes %>
3
+
4
+
5
+ <%= render('_main.erb') do |f, state, xs|
6
+ "boost::mpl::fold<#{Benchcc::MPL::Vector.new(xs)}, #{state}, #{f}>::type"
7
+ end %>
@@ -0,0 +1,3 @@
1
+ (0..1000).step(5).map { |depth|
2
+ { breadth: 1, depth: depth }
3
+ }
@@ -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,8 @@
1
+ #include <boost/fusion/algorithm/iteration/fold.hpp>
2
+ <%= Benchcc::Fusion::Cons.new(0...depth).includes %>
3
+
4
+
5
+ <%= render('_main.erb') do |f, state, xs|
6
+ cons = Benchcc::Fusion::Cons.new(xs)
7
+ "boost::fusion::fold(#{cons}{}, #{state}, #{f})"
8
+ end %>
@@ -0,0 +1,9 @@
1
+ <%= Benchcc::Fusion::List.new(0...depth).includes %>
2
+
3
+ #include <boost/fusion/algorithm/iteration/fold.hpp>
4
+
5
+
6
+ <%= render('_main.erb') do |f, state, xs|
7
+ list = Benchcc::Fusion::List.new(xs)
8
+ "boost::fusion::fold(#{list}{}, #{state}, #{f})"
9
+ end %>
@@ -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,6 @@
1
+ #include <boost/hana/list.hpp>
2
+
3
+ <%= render('_main.erb') do |f, state, xs|
4
+ xs = xs.map { |x| "#{x}{}" }.join(', ')
5
+ "boost::hana::foldl(#{f}, #{state}, boost::hana::list(#{xs}))"
6
+ 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,7 @@
1
+ module Enumerable
2
+ def foldr(state, &block)
3
+ reverse.inject(state) { |state, x| block.call(x, state) }
4
+ end
5
+
6
+ alias_method(:foldl, :inject)
7
+ 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,8 @@
1
+ require 'gnuplot'
2
+
3
+
4
+ class Gnuplot::DataSet
5
+ def self.from_file(file, using: [1, 2], &block)
6
+ self.new("\"#{file}\" using #{using.join(':')}", &block)
7
+ end
8
+ 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,5 @@
1
+ class String
2
+ def quote(char = '"')
3
+ char + self + char
4
+ end
5
+ 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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Benchcc
2
+ VERSION = "0.0.1"
3
+ end
data/lib/benchcc.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'benchcc/benchmark'
2
+ require 'benchcc/compiler'
3
+ require 'benchcc/fusion'
4
+ require 'benchcc/mpl'
5
+ require 'benchcc/plot'
6
+ require 'benchcc/version'
@@ -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
@@ -0,0 +1,3 @@
1
+ x, peak_memusg, wall_time
2
+ 0, 1, , 2
3
+ 1, 2, , 3
@@ -0,0 +1 @@
1
+ int main() {}