benchcc 0.0.11 → 0.0.12
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.
- 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
|