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 +4 -4
- data/lib/benchcc/benchmark.rb +29 -15
- data/lib/benchcc/compiler.rb +89 -102
- data/lib/benchcc/version.rb +1 -1
- data/spec/benchmark_spec.rb +11 -29
- data/spec/compiler_spec.rb +40 -38
- data/spec/src/invalid_runtime.cpp +5 -0
- data/spec/src/valid.cpp +7 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1639266252ed6664cf61f275325fcb29e864341a
|
4
|
+
data.tar.gz: 3659e30ee50cfec284f7837ea705e778647c8493
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41b54dc111a54b67ceed9d434c8e3b3b39ef3074f0883666444a32ace49070a5c04510bac6e51656768836e48168be1f909f1651de3ffce0be22b436eac5310b
|
7
|
+
data.tar.gz: 9c1c1d2557408c9cccf48bfd730dc29fa074d22f1d9e0baeb07c42b15cf60b614a67abf8ec57958ac9b0ef08f7d479c17ee6a8d5193cbace8d0673153d1c40f9
|
data/lib/benchcc/benchmark.rb
CHANGED
@@ -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(
|
24
|
-
|
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
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
59
|
+
|
60
|
+
csv << row
|
61
|
+
rescue ExecutionError, CompilationError, Timeout::Error => e
|
48
62
|
$stderr << e
|
49
63
|
break
|
50
64
|
end
|
data/lib/benchcc/compiler.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
/
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
data/lib/benchcc/version.rb
CHANGED
data/spec/benchmark_spec.rb
CHANGED
@@ -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(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
}
|
data/spec/compiler_spec.rb
CHANGED
@@ -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
|
-
|
9
|
-
describe
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
).
|
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
|
-
|
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
|
-
|
26
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
@
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
data/spec/src/valid.cpp
CHANGED
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.
|
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-
|
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
|