benchcc 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 661158a9d7af70f1063d55e5848506701c1b497e
4
- data.tar.gz: 1e2b3c71ecf1c3bc84b214d212ab8033e66428a1
3
+ metadata.gz: 1639266252ed6664cf61f275325fcb29e864341a
4
+ data.tar.gz: 3659e30ee50cfec284f7837ea705e778647c8493
5
5
  SHA512:
6
- metadata.gz: f0f1c6647a9a77860f83908b5bd102f9f468ee3e6fc968625cb33693c6a803c4ec304666f2b81031e535c6df15674e465c7c1a18646ce6d9575667e1b0fedc41
7
- data.tar.gz: ee7d8500e0fbbbecf20cb776ef75c7259c5c9d357a4aa35d44311e8799c16513ab40366294ee78f00d10e8e70fe7d475dfa2dc09df1cd95fa58bfe4cffbeeeaa
6
+ metadata.gz: 41b54dc111a54b67ceed9d434c8e3b3b39ef3074f0883666444a32ace49070a5c04510bac6e51656768836e48168be1f909f1651de3ffce0be22b436eac5310b
7
+ data.tar.gz: 9c1c1d2557408c9cccf48bfd730dc29fa074d22f1d9e0baeb07c42b15cf60b614a67abf8ec57958ac9b0ef08f7d479c17ee6a8d5193cbace8d0673153d1c40f9
@@ -1,9 +1,9 @@
1
1
  require_relative 'compiler'
2
+
2
3
  require 'csv'
3
4
  require 'pathname'
4
5
  require 'ruby-progressbar'
5
6
  require 'tilt'
6
- require 'timeout'
7
7
 
8
8
 
9
9
  module Benchcc
@@ -20,31 +20,45 @@ module Benchcc
20
20
  end
21
21
  end
22
22
 
23
- def benchmark(erb_file, environments, timeout: 10,
24
- evaluate_erb_relative_to: File.dirname(erb_file), &bench)
23
+ def benchmark(
24
+ erb_file:, # String or Pathname
25
+ environments:, # Array of Hash
26
+ compilation_timeout:, # Int
27
+ execution_timeout:, # Int
28
+ evaluate_erb_relative_to:, # String or Pathname
29
+ features:, # Array of Symbol
30
+ compiler_executable:, # String
31
+ compiler_id:, # String
32
+ compiler_options: # Array of String
33
+ )
25
34
  erb_file = Pathname.new(erb_file)
26
35
  progress = ProgressBar.create(format: '%p%% | %B |', total: environments.size)
36
+ compiler = Benchcc::which(compiler_id)
27
37
 
28
38
  data = CSV.generate({headers: :first_row}) do |csv|
29
- csv << [:input_size, :compilation_time, :memory_usage, :run_time]
39
+ csv << [:input_size] + features
30
40
 
31
41
  environments.each do |env|
32
42
  code = Renderer.new(evaluate_erb_relative_to).render(erb_file, **env)
33
43
  begin
44
+ row = {input_size: env[:input_size]}
34
45
  Tempfile.create([erb_file.basename, '.cpp']) do |tmp|
35
46
  tmp.write(code) && tmp.close
36
- bench_data = Timeout::timeout(timeout) {
37
- bench.call(tmp.path, env)
38
- }
39
- row = {
40
- input_size: env[:input_size],
41
- compilation_time: bench_data[:compilation_time],
42
- memory_usage: bench_data[:memory_usage],
43
- run_time: bench_data[:run_time] # TODO: implement this
44
- }
45
- csv << row
47
+
48
+ row.merge!(
49
+ compiler.call(
50
+ input_file: tmp.path,
51
+ features: features,
52
+ compiler_executable: compiler_executable,
53
+ compiler_options: compiler_options,
54
+ compilation_timeout: compilation_timeout,
55
+ execution_timeout: execution_timeout
56
+ )
57
+ )
46
58
  end
47
- rescue CompilationError, Timeout::Error => e
59
+
60
+ csv << row
61
+ rescue ExecutionError, CompilationError, Timeout::Error => e
48
62
  $stderr << e
49
63
  break
50
64
  end
@@ -3,121 +3,108 @@ require_relative 'ext/string'
3
3
  require 'open3'
4
4
  require 'pathname'
5
5
  require 'tempfile'
6
+ require 'timeout'
6
7
 
7
8
 
8
9
  module Benchcc
9
10
  class CompilationError < RuntimeError
10
11
  end
11
12
 
12
- # Basic interface to compiler frontends.
13
- class Compiler
14
- # Show the name and the version of the compiler.
15
- def to_s
16
- raise NotImplementedError
17
- end
18
-
19
- # compile: Path -> Hash
20
- #
21
- # Compile the given file and return compilation statistics.
22
- #
23
- # Additional compiler-specific arguments may be specified. A
24
- # `CompilationError` is raised if the compilation fails for
25
- # whatever reason.
26
- def compile(file, *args)
27
- raise NotImplementedError
28
- end
29
-
30
- def self.guess_from_binary(binary)
31
- stdout, stderr, status = Open3.capture3("#{binary} --version")
32
- case stdout
33
- when /\(GCC\)/
34
- return GCC.new(binary)
35
- when /clang/
36
- return Clang.new(binary)
37
- else
38
- raise ArgumentError("unknown compiler #{binary}")
39
- end
40
- end
41
- end # class Compiler
42
-
43
- class Clang < Compiler
44
- def initialize(binary)
45
- @exe = `which #{binary}`.strip
46
- raise "#{binary} not found" unless $?.success?
47
- end
13
+ class ExecutionError < RuntimeError
14
+ end
48
15
 
49
- def compile(file, *args)
50
- file = Pathname.new(file).expand_path
51
- command = "/usr/bin/time -l #{@exe} #{args.join(' ')} -ftime-report #{file}"
52
- stdout, stderr, status = Open3.capture3(command)
53
-
54
- if status.success?
55
- memusg = stderr.match(/(\d+)\s+maximum/)[1].to_i
56
-
57
- section = -> (title) {
58
- title.gsub!(' ', '\s')
59
- /(
60
- ===-+===\n
61
- .*#{title}.*\n
62
- ===-+===\n
63
- (.|\n)+?(?====)
64
- )|(
65
- ===-+===\n
66
- .*#{title}.*\n
67
- ===-+===\n
68
- (.|\n)+
69
- )/x
70
- }
71
- time = stderr.match(section["Miscellaneous Ungrouped Timers"]).to_s
72
- .match(/(\d+\.?\d+).+?Total/)[1]
73
- return {
74
- compilation_time: time,
75
- memory_usage: memusg
16
+ class Clang
17
+ def call(input_file:, features:, compiler_executable:, compiler_options:,
18
+ compilation_timeout:, execution_timeout:)
19
+ stats = {}
20
+ Dir.mktmpdir do |tmp_dir|
21
+ if features.include?(:compilation_time)
22
+ compiler_options << '-ftime-report'
23
+ end
24
+
25
+ if features.include?(:execution_time)
26
+ compiler_options << "-o#{tmp_dir}/a.out"
27
+ end
28
+
29
+ if features.include?(:memory_usage)
30
+ compiler_executable = "/usr/bin/time -l #{compiler_executable}"
31
+ end
32
+
33
+ command = "#{compiler_executable} #{compiler_options.join(' ')} #{input_file}"
34
+ stdout, stderr, status = Timeout::timeout(compilation_timeout) {
35
+ Open3.capture3(command)
76
36
  }
77
37
 
78
- else
79
- err_string = <<-EOS.strip_heredoc
80
- > #{command}
81
- #{stderr}
82
-
83
- [compiling:
84
- #{file.read}
85
- ]
86
- EOS
87
- raise CompilationError.new(err_string)
38
+ if not status.success?
39
+ raise CompilationError.new(<<-EOS.strip_heredoc
40
+ > #{command}
41
+ #{stderr}
42
+
43
+ [compiling:
44
+ #{input_file.read}
45
+ ]
46
+ EOS
47
+ )
48
+ end
49
+
50
+ if features.include?(:compilation_time)
51
+ section = -> (title) {
52
+ title.gsub!(' ', '\s')
53
+ /(
54
+ ===-+===\n
55
+ .*#{title}.*\n
56
+ ===-+===\n
57
+ (.|\n)+?(?====)
58
+ )|(
59
+ ===-+===\n
60
+ .*#{title}.*\n
61
+ ===-+===\n
62
+ (.|\n)+
63
+ )/x
64
+ }
65
+ time = stderr.match(section["Miscellaneous Ungrouped Timers"]).to_s
66
+ .match(/(\d+\.?\d+).+?Total/)[1]
67
+ stats.merge!({compilation_time: time})
68
+ end
69
+
70
+ if features.include?(:memory_usage)
71
+ memusg = stderr.match(/(\d+)\s+maximum/)[1]
72
+ stats.merge!({memory_usage: memusg})
73
+ end
74
+
75
+ if features.include?(:execution_time)
76
+ command = "/usr/bin/time #{tmp_dir}/a.out"
77
+ stdout, stderr, status = Timeout::timeout(execution_timeout) {
78
+ Open3.capture3(command)
79
+ }
80
+ if status.success?
81
+ time = stderr.match(/(\d+\.?\d*)\s+real/)[1]
82
+ stats.merge!({execution_time: time})
83
+ else
84
+ raise ExecutionError.new(<<-EOS.strip_heredoc
85
+ > #{command}
86
+ #{stderr}
87
+
88
+ [running:
89
+ #{input_file.read}
90
+ ]
91
+ EOS
92
+ )
93
+ end
94
+ end
95
+
96
+ return stats
88
97
  end
89
98
  end
90
99
  end
91
100
 
92
- class GCC < Compiler
93
- def initialize(binary)
94
- @exe = `which #{binary}`.strip
95
- raise "#{binary} not found" unless $?.success?
96
- end
97
-
98
- def compile(file, *args)
99
- file = Pathname.new(file).expand_path
100
- command = "/usr/bin/time -l #{@exe} #{args.join(' ')} -ftime-report #{file}"
101
- stdout, stderr, status = Open3.capture3(command)
102
-
103
- if status.success?
104
- time = stderr.match(/TOTAL.+/).to_s.split[-3].to_f
105
- memusg = stderr.match(/(\d+)\s+maximum/)[1].to_i
106
- return {
107
- compilation_time: time,
108
- memory_usage: memusg
109
- }
110
- else
111
- err_string = <<-EOS.strip_heredoc
112
- > #{command}
113
- #{stderr}
114
-
115
- [compiling:
116
- #{file.read}
117
- ]
118
- EOS
119
- raise CompilationError.new(err_string)
120
- end
101
+ def which(compiler_id)
102
+ case compiler_id
103
+ when 'Clang'
104
+ return Clang.new
105
+ else
106
+ raise ArgumentError.new("Unsupported compiler id: #{compiler_id}")
121
107
  end
122
108
  end
109
+ module_function :which
123
110
  end
@@ -1,3 +1,3 @@
1
1
  module Benchcc
2
- VERSION = "0.0.11"
2
+ VERSION = "0.0.12"
3
3
  end
@@ -10,35 +10,17 @@ describe Benchcc.method(:benchmark) do
10
10
  envs = [{input_size: 1}, {input_size: 2}]
11
11
  Tempfile.create('') do |file|
12
12
  expect {
13
- data = Benchcc.benchmark(file.path, envs) do |file, env|
14
- {
15
- compilation_time: 'foo',
16
- memory_usage: 'bar',
17
- run_time: 'baz'
18
- }
19
- end
20
- }.not_to raise_error
21
- end
22
- }
23
-
24
- it('should stop on timeout') {
25
- envs = [{input_size: 1}, {input_size: 2}]
26
- Tempfile.create('') do |file|
27
- expect {
28
- data = Benchcc.benchmark(file.path, envs, timeout: 0.1) do |file, env|
29
- sleep(0.2)
30
- end
31
- }.not_to raise_error
32
- end
33
- }
34
-
35
- it('should stop on CompilationError') {
36
- envs = [{input_size: 1}, {input_size: 2}]
37
- Tempfile.create('') do |file|
38
- expect {
39
- data = Benchcc.benchmark(file.path, envs, timeout: 0.1) do |file, env|
40
- raise Benchcc::CompilationError.new
41
- end
13
+ data = Benchcc.benchmark(
14
+ erb_file: file.path,
15
+ environments: [],
16
+ compilation_timeout: 10,
17
+ execution_timeout: 10,
18
+ evaluate_erb_relative_to: '',
19
+ features: [],
20
+ compiler_executable: `which clang++`,
21
+ compiler_id: 'Clang',
22
+ compiler_options: []
23
+ )
42
24
  }.not_to raise_error
43
25
  end
44
26
  }
@@ -2,52 +2,54 @@ require 'benchcc/compiler'
2
2
 
3
3
  require 'pathname'
4
4
  require 'rspec'
5
- require 'tempfile'
6
5
 
7
6
 
8
- describe Benchcc::Compiler do
9
- describe :guess_from_binary do
10
- it('can guess clang') {
11
- expect(
12
- Benchcc::Compiler.guess_from_binary(`which clang`.strip)
13
- ).to be_instance_of(Benchcc::Clang)
7
+ [['Clang', 'clang++']].each do |compiler_id, executable|
8
+ describe compiler_id do
9
+ before(:each) {
10
+ @cc = Benchcc::which(compiler_id)
11
+ @invalid = Pathname.new('src/invalid.cpp').expand_path(File.dirname(__FILE__))
12
+ @invalid_runtime = Pathname.new('src/invalid_runtime.cpp').expand_path(File.dirname(__FILE__))
13
+ @valid = Pathname.new('src/valid.cpp').expand_path(File.dirname(__FILE__))
14
14
  }
15
15
 
16
- # GCC is aliased to clang on OSX, so this test fails.
17
- # it('can guess gcc') {
18
- # expect(
19
- # Benchcc::Compiler.guess_from_binary(`which gcc`.strip)
20
- # ).to be_instance_of(Benchcc::GCC)
21
- # }
22
-
23
- it('fails otherwise') {
16
+ it('fails cleanly on invalid input') {
24
17
  expect {
25
- Benchcc::Compiler.guess_from_binary('not_a_compiler')
26
- }.to raise_error
18
+ @cc.call(
19
+ input_file: @invalid,
20
+ features: [],
21
+ compiler_executable: executable,
22
+ compiler_options: [],
23
+ compilation_timeout: 10,
24
+ execution_timeout: 10
25
+ )
26
+ }.to raise_error(Benchcc::CompilationError)
27
27
  }
28
- end
29
- end
30
28
 
31
- [[Benchcc::Clang, 'clang++'], [Benchcc::GCC, 'g++']].each do |compiler, which|
32
- describe compiler do
33
- before(:each) {
34
- @cc = compiler.new(which)
35
- @invalid = Pathname.new('src/invalid.cpp').expand_path(File.dirname(__FILE__))
36
- @valid = Pathname.new('src/valid.cpp').expand_path(File.dirname(__FILE__))
29
+ it('fails cleanly on runtime error') {
30
+ expect {
31
+ @cc.call(
32
+ input_file: @invalid_runtime,
33
+ features: [:execution_time],
34
+ compiler_executable: executable,
35
+ compiler_options: [],
36
+ compilation_timeout: 10,
37
+ execution_timeout: 10
38
+ )
39
+ }.to raise_error(Benchcc::ExecutionError)
37
40
  }
38
41
 
39
- describe :compile do
40
- it('fails cleanly on invalid input') {
41
- expect {
42
- @cc.compile(@invalid)
43
- }.to raise_error(Benchcc::CompilationError)
44
- }
45
-
46
- it('returns statistics on valid input') {
47
- expect(
48
- @cc.compile(@valid, '-o /dev/null')
49
- ).to be_instance_of(Hash)
50
- }
51
- end
42
+ it('returns statistics on valid input') {
43
+ expect(
44
+ @cc.call(
45
+ input_file: @valid,
46
+ features: [:execution_time, :compilation_time, :memory_usage],
47
+ compiler_executable: executable,
48
+ compiler_options: ['-o/dev/null'],
49
+ compilation_timeout: 10,
50
+ execution_timeout: 10
51
+ )
52
+ ).to be_instance_of(Hash)
53
+ }
52
54
  end
53
55
  end
@@ -0,0 +1,5 @@
1
+ #include <cassert>
2
+
3
+ int main() {
4
+ assert(false);
5
+ }
data/spec/src/valid.cpp CHANGED
@@ -1 +1,7 @@
1
- int main() {}
1
+ #include <chrono>
2
+ #include <thread>
3
+
4
+
5
+ int main() {
6
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
7
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: benchcc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Louis Dionne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-20 00:00:00.000000000 Z
11
+ date: 2014-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gnuplot
@@ -115,6 +115,7 @@ files:
115
115
  - spec/numeric_spec.rb
116
116
  - spec/plot_spec.rb
117
117
  - spec/src/invalid.cpp
118
+ - spec/src/invalid_runtime.cpp
118
119
  - spec/src/plot.csv
119
120
  - spec/src/valid.cpp
120
121
  homepage: https://github.com/ldionne/benchcc