server_scripts 0.0.5 → 0.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 49c64405f9a8b3a97c13db36fad927e3e3f3353a
4
- data.tar.gz: 795ee015600d00c84752634fbdb0bc56c5325d4f
3
+ metadata.gz: 0d900565278f9df7242f33d23a8b8e22ee67429a
4
+ data.tar.gz: 6deaa6599e7bc742ebcabeee802ea699f9f9d44f
5
5
  SHA512:
6
- metadata.gz: 5f361a20a9bd64803525ec921662a99c03ed91ba290e9c499ecf6872a709da26bb602d742919f227537925787fbad5e39f44889626bb6b8cdf3652bbde230b05
7
- data.tar.gz: 7b4b5ffe44d28777b75b8a669eff5f3180f9bcd860ec6a652716a930f7e87b55fce64713e21048db5e0dc63739816795fb52b9533182b8a6ba523789a4c6a001
6
+ metadata.gz: 685af965add627451c130b29c557d13c3231e6b94730c2d2dfe7882027492d15933a353002978f3df5137222a19eccc7656d5c7307771f866f3ea820de22b411
7
+ data.tar.gz: 9f5c6cd9efd59294f7e8a430be9fa4abd99989a67c6591486dcdf735a9da33d549e5b1671371b2b7073eeacd34c4020da3d280a3485c784e3be96914ba3a835d
@@ -1,8 +1,12 @@
1
1
  # -*- coding: utf-8 -*-
2
+ require 'ptools'
3
+
2
4
  require 'server_scripts/node_type'
3
5
  require 'server_scripts/executor'
4
6
  require 'server_scripts/computer'
5
7
  require 'server_scripts/parser'
8
+ require 'server_scripts/experiment'
9
+ require 'server_scripts/batch_job'
6
10
  require 'server_scripts/version'
7
11
 
8
12
  module ServerScripts
@@ -25,108 +29,6 @@ module ServerScripts
25
29
  ENV["GROUP_NAME"]
26
30
  end
27
31
  end
28
-
29
- class BatchJob
30
- attr_accessor :job_name
31
- attr_accessor :out_file
32
- attr_accessor :err_file
33
- attr_accessor :wall_time
34
- attr_accessor :node_type
35
- attr_accessor :options
36
- attr_accessor :nodes
37
- attr_accessor :npernode
38
- attr_accessor :nprocs
39
- attr_accessor :run_cmd, :executor, :executable
40
- attr_accessor :additional_commands
41
- attr_accessor :enable_intel_itac
42
- attr_accessor :intel_vtune_fname
43
- attr_accessor :source_bashrc
44
- attr_accessor :modules
45
-
46
- attr_reader :env
47
- attr_reader :job_fname
48
- attr_reader :system
49
-
50
- def initialize job_fname
51
- @job_fname = job_fname
52
- @job_name = "sample"
53
- @out_file = "sample_out.log"
54
- @err_file = "sample_err.log"
55
- @wall_time = "1:00:00"
56
- @node_type = NodeType::FULL
57
- @nodes = 1
58
- @npernode = 1
59
- @nprocs = nil
60
- @run_cmd = nil
61
- @executor = :vanilla
62
- @env = {}
63
- @executable = "./a.out"
64
- @job_script = nil
65
- @enable_intel_itac = false
66
- @additional_commands = []
67
- @modules = []
68
- @source_bashrc = true
69
-
70
- yield self
71
- end
72
-
73
- def set_env var, value
74
- raise ArgumentError, "Env #{var} is already set to #{value}." if @env[var]
75
- @env[var] = value
76
- end
77
-
78
- def write_job_script!
79
- generate_job_script! unless @job_script
80
- File.write(@job_fname, @job_script)
81
- end
82
-
83
- def submit!
84
- write_job_script!
85
- Kernel.system(@system.job_submit_cmd(batch_script: @job_fname))
86
- end
87
-
88
- private
89
-
90
- def generate_job_script!
91
- @system = ServerScripts.system.new(@node_type, @nodes, @job_name,
92
- @wall_time, @out_file, @err_file, @env, @modules)
93
- configure_executor!
94
-
95
- @job_script = ""
96
- @job_script += @system.header
97
- @job_script += "\nsource ~/.bashrc\n" if @source_bashrc
98
- @job_script += @system.module_load_cmd
99
-
100
- @job_script += @system.env_setter
101
- @additional_commands.each do |c|
102
- @job_script += c + "\n"
103
- end
104
- @job_script += "#{@executor.run_cmd} #{@executable} #{@options}\n"
105
- end
106
-
107
- def configure_executor!
108
- check_process_counts
109
- @nprocs = @npernode * @nodes
110
-
111
- if @executor == :openmpi
112
- @executor = Executor::OpenMPI.new(npernode: @npernode, nprocs: @nprocs, env: @env)
113
- elsif @executor == :intel
114
- @executor = Executor::IntelMPI.new(npernode: @npernode, nprocs: @nprocs, env: @env)
115
- @executor.enable_itac = !!@enable_intel_itac
116
- @executor.vtune_fname = @intel_vtune_fname
117
- elsif @executor == :vanilla
118
- @executor = Executor::Vanilla.new
119
- else
120
- raise ArgumentError, "Cannot find MPI implementation #{@executor}."
121
- end
122
- end
123
-
124
- def check_process_counts
125
- if @nprocs && @nprocs != @npernode * @nodes
126
- raise ArgumentError, "Number of processes should be #{@npernode * @nodes} not #{@nprocs}"
127
- end
128
- end
129
- end
130
32
  end # module ServerScripts
131
33
 
132
34
 
@@ -0,0 +1,107 @@
1
+ module ServerScripts
2
+ class BatchJob
3
+ attr_accessor :job_name
4
+ attr_accessor :out_file
5
+ attr_accessor :err_file
6
+ attr_accessor :wall_time
7
+ attr_accessor :node_type
8
+ attr_accessor :options
9
+ attr_accessor :nodes
10
+ attr_accessor :npernode
11
+ attr_accessor :nprocs
12
+ attr_accessor :run_cmd, :executor, :executable
13
+ attr_accessor :additional_commands
14
+ attr_accessor :enable_intel_itac
15
+ attr_accessor :intel_vtune_fname
16
+ attr_accessor :source_bashrc
17
+ attr_accessor :modules
18
+ attr_accessor :reservation_id
19
+
20
+ attr_reader :env
21
+ attr_reader :job_fname
22
+ attr_reader :system
23
+
24
+ def initialize job_fname="sample_job.sh"
25
+ @job_fname = job_fname
26
+ @job_name = "sample"
27
+ @out_file = "sample_out.log"
28
+ @err_file = "sample_err.log"
29
+ @wall_time = "1:00:00"
30
+ @node_type = NodeType::FULL
31
+ @nodes = 1
32
+ @npernode = 1
33
+ @nprocs = nil
34
+ @run_cmd = nil
35
+ @executor = :vanilla
36
+ @env = {}
37
+ @executable = "./a.out"
38
+ @job_script = nil
39
+ @enable_intel_itac = false
40
+ @additional_commands = []
41
+ @modules = []
42
+ @source_bashrc = true
43
+ @reservation_id = nil
44
+
45
+ yield self
46
+ end
47
+
48
+ def set_env var, value
49
+ raise ArgumentError, "Env #{var} is already set to #{value}." if @env[var]
50
+ @env[var] = value
51
+ end
52
+
53
+ def write_job_script!
54
+ generate_job_script! unless @job_script
55
+ File.write(@job_fname, @job_script)
56
+ end
57
+
58
+ def submit!
59
+ write_job_script!
60
+ Kernel.system(@system.job_submit_cmd(
61
+ batch_script: @job_fname,
62
+ res_id: @reservation_id))
63
+ end
64
+
65
+ private
66
+
67
+ def generate_job_script!
68
+ @system = ServerScripts.system.new(@node_type, @nodes, @job_name,
69
+ @wall_time, @out_file, @err_file, @env, @modules)
70
+ configure_executor!
71
+
72
+ @job_script = ""
73
+ @job_script += @system.header
74
+ @job_script += "\nsource ~/.bashrc\n" if @source_bashrc
75
+ @job_script += @system.module_load_cmd
76
+
77
+ @job_script += @system.env_setter
78
+ @additional_commands.each do |c|
79
+ @job_script += c + "\n"
80
+ end
81
+ @job_script += "#{@executor.run_cmd} #{@executable} #{@options}\n"
82
+ end
83
+
84
+ def configure_executor!
85
+ check_process_counts
86
+ @nprocs = @npernode * @nodes
87
+
88
+ if @executor == :openmpi
89
+ @executor = Executor::OpenMPI.new(npernode: @npernode, nprocs: @nprocs, env: @env)
90
+ elsif @executor == :intel
91
+ @executor = Executor::IntelMPI.new(npernode: @npernode, nprocs: @nprocs, env: @env)
92
+ @executor.enable_itac = !!@enable_intel_itac
93
+ @executor.vtune_fname = @intel_vtune_fname
94
+ elsif @executor == :vanilla
95
+ @executor = Executor::Vanilla.new
96
+ else
97
+ raise ArgumentError, "Cannot find MPI implementation #{@executor}."
98
+ end
99
+ end
100
+
101
+ def check_process_counts
102
+ if @nprocs && @nprocs != @npernode * @nodes
103
+ raise ArgumentError, "Number of processes should be #{@npernode * @nodes} not #{@nprocs}"
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,17 @@
1
+ module ServerScripts
2
+ # Class that represents an experiment that needs to be conducted. Accepts
3
+ # various parameters that need to be tested and a BatchJob object which it
4
+ # customize depending on the test parameters.
5
+ class Experiment
6
+ attr_accessor :batch_job
7
+ attr_accessor :exp_params
8
+ attr_accessor :job_params
9
+ attr_accessor :executable
10
+
11
+ def initialize exp_name
12
+ @exp_name = exp_name
13
+ end
14
+
15
+
16
+ end # class Experiment
17
+ end # module ServerScripts
@@ -1,16 +1,136 @@
1
1
  module ServerScripts
2
2
  module Parser
3
3
  class ITAC
4
- attr_reader :ideal_trace_file, :real_trace_file
4
+ attr_accessor :real_itac_fname
5
+ attr_reader :ideal_itac_fname
5
6
 
6
7
  def initialize itac_file
7
- @itac_file = itac_file
8
- @ideal_trace_file = nil
9
- @real_trace_file = nil
8
+ check_for_traceanalyzer
9
+
10
+ @real_itac_fname = itac_file
11
+ @ideal_itac_fname = "#{@real_itac_fname}.ideal.single.stf"
12
+ @real_function_profile = nil
13
+ @ideal_function_profile = nil
14
+
15
+ yield self if block_given?
16
+ end
17
+
18
+ def trace!
19
+ generate_ideal_trace!
20
+ analyze_function_profiles!
10
21
  end
11
22
 
12
23
  def generate_ideal_trace!
13
-
24
+ unless File.file?(@ideal_itac_fname)
25
+ Kernel.system(
26
+ "traceanalyzer --cli --ideal -u -o #{@ideal_itac_fname} #{@real_itac_fname}")
27
+ end
28
+ end
29
+
30
+ def analyze_function_profiles!
31
+ unless @real_function_profile
32
+ @real_function_profile = `traceanalyzer --cli --functionprofile #{@real_itac_fname}`
33
+ @real_function_profile = @real_function_profile.split("\n")
34
+ end
35
+ unless @ideal_function_profile
36
+ @ideal_function_profile = `traceanalyzer --cli --functionprofile #{@ideal_itac_fname}`
37
+ @ideal_function_profile = @ideal_function_profile.split("\n")
38
+ end
39
+ end
40
+
41
+ # Total time of real execution including intialization etc.
42
+ def real_app_time
43
+ @total_app_time ||= event_time("Application", kind: :real, how: :all_procs)
44
+ @total_app_time
45
+ end
46
+
47
+ # Ideal MPI time. Only wait time.
48
+ def ideal_mpi_time
49
+ @ideal_mpi_time ||= event_time("MPI", kind: :ideal)
50
+ @ideal_mpi_time
51
+ end
52
+
53
+ # Actual MPI time. Includes wait time and communication time.
54
+ def real_mpi_time
55
+ @real_mpi_time ||= event_time("MPI")
56
+ @real_mpi_time
57
+ end
58
+
59
+ # Time that MPI spent in communication.
60
+ def mpi_comm_time
61
+ real_mpi_time - ideal_mpi_time
62
+ end
63
+
64
+ # Get event time for a particular event. Specify whether from ideal trace or real trace.
65
+ def event_time(event, kind: :real, how: :all_procs)
66
+ if kind == :real
67
+ parse_real_event_time(event, how: how)
68
+ elsif kind == :ideal
69
+ parse_ideal_event_time(event, how: how)
70
+ else
71
+ raise ArgumentError, "kind argument should be either :real or :ideal, not #{kind}."
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def get_allprocs_event_time(func_profile, event)
78
+ regex_event = Regexp.quote(event)
79
+ event_time = nil
80
+ func_profile.each do |l|
81
+ if l.match(/"All_Processes";"#{regex_event}"/)
82
+ match_data = l.match /"All_Processes";"#{regex_event}";(\d+);(\d+);(\d+);(\d+)/
83
+ event_time = match_data[2].to_f / 1e9
84
+ break
85
+ end
86
+ end
87
+
88
+ unless event_time
89
+ raise RuntimeError, "no event #{event} could be found in the function profile"
90
+ end
91
+
92
+ event_time
93
+ end
94
+
95
+ def get_perproc_event_time(func_profile, event)
96
+ regex_event = Regexp.quote(event)
97
+ per_proc_event_time = {}
98
+
99
+ func_profile.each do |l|
100
+ if l.match(/"Process\s(\d+)";"#{regex_event}"/)
101
+ match_data = l.match(/"Process\s(\d+)";"#{regex_event}";(\d+);(\d+);(\d+);(\d+)/)
102
+ per_proc_event_time[match_data[1].to_i] = match_data[3].to_f / 1e9
103
+ end
104
+ end
105
+
106
+ values = per_proc_event_time.values
107
+ [values.inject(:+).to_f / values.size, values.min, values.max]
108
+ end
109
+
110
+ def parse_real_event_time(event, how:)
111
+ if how == :all_procs
112
+ get_allprocs_event_time(@real_function_profile, event)
113
+ elsif how == :per_proc
114
+ get_perproc_event_time(@real_function_profile, event)
115
+ else
116
+ raise ArgumentError, "how argument should be either :all_procs or :per_proc, not #{how}."
117
+ end
118
+ end
119
+
120
+ def parse_ideal_event_time(event, how:)
121
+ if how == :all_procs
122
+ get_allprocs_event_time(@ideal_function_profile, event)
123
+ elsif how == :per_proc
124
+ get_perproc_event_time(@ideal_function_profile, event)
125
+ else
126
+ raise ArgumentError, "how argument should be either :all_procs or :per_proc, not #{how}."
127
+ end
128
+ end
129
+
130
+ def check_for_traceanalyzer
131
+ unless File.which("traceanalyzer")
132
+ raise RuntimeError, "Must have intel traceanalyzer executable installed."
133
+ end
14
134
  end
15
135
  end # class ITAC
16
136
  end
@@ -1,48 +1,89 @@
1
1
  module ServerScripts
2
2
  module Parser
3
3
  class StarpuProfile
4
- attr_reader :total_time
5
- attr_reader :total_exec_time
6
- attr_reader :total_sleep_time
7
- attr_reader :total_overhead_time
8
-
4
+
5
+ attr_reader :time_hash
6
+ # Specify the regex that will allow finding the profile files for the given starpu
7
+ # processes. Each process will output one file.
8
+ #
9
+ # Usage:
10
+ # parser = Parser::StarpuProfile.new("4_proc_profile_1_*.starpu_profile")
11
+ # parser.total_time
9
12
  def initialize regex
10
13
  @regex = regex
11
- @total_time = 0.0
12
- @total_exec_time = 0.0
13
- @total_sleep_time = 0.0
14
- @total_overhead_time = 0.0
15
-
14
+ @time_hash = {}
16
15
  extract_data_from_profiles
17
16
  end
18
17
 
18
+ def total_time
19
+ extract_from_time_hash :total_time
20
+ end
21
+
22
+ def total_exec_time
23
+ extract_from_time_hash :exec_time
24
+ end
25
+
26
+ def total_sleep_time
27
+ extract_from_time_hash :sleep_time
28
+ end
29
+
30
+ def total_overhead_time
31
+ extract_from_time_hash :overhead_time
32
+ end
33
+
34
+ def proc_time event:, proc_id:
35
+ time = 0.0
36
+ @time_hash[proc_id].each_value do |thread_info|
37
+ time += thread_info[event]
38
+ end
39
+
40
+ time
41
+ end
42
+
43
+ def time event:, proc_id:, worker_id:
44
+ @time_hash[proc_id][worker_id][event]
45
+ end
46
+
19
47
  private
20
48
 
49
+ def extract_from_time_hash key
50
+ time = 0.0
51
+
52
+ @time_hash.each_value do |proc_time|
53
+ proc_time.each_value do |thread_time|
54
+ time += thread_time[key]
55
+ end
56
+ end
57
+
58
+ time
59
+ end
60
+
21
61
  def extract_data_from_profiles
22
62
  Dir.glob(@regex) do |fname|
63
+ proc_id = fname.match(@regex.gsub("*", "(\\d+)"))[1].to_i
64
+ @time_hash[proc_id] = {}
23
65
  output = File.read(fname).split("\n")
24
-
66
+ recent_cpu = nil
67
+
25
68
  output.each do |line|
69
+ cpu_match = line.match(/CPU\s(\d+)/)
70
+ recent_cpu = cpu_match[1].to_i if cpu_match
26
71
  match_data = line.match(/total\:\s+(\d+)\.\d+\sms\sexecuting\:\s(\d+).\d+\sms\ssleeping\:\s(\d+).\d+\sms\soverhead\s(\d+).\d+\sms/)
27
72
 
28
73
  if match_data
29
74
  exec_time = match_data[2].to_i
30
-
31
75
  if exec_time != 0
32
- @total_time += match_data[1].to_f
33
- @total_exec_time += exec_time.to_f
34
- @total_sleep_time += match_data[3].to_f
35
- @total_overhead_time += match_data[4].to_f
76
+ @time_hash[proc_id][recent_cpu] = {}
77
+ @time_hash[proc_id][recent_cpu][:total_time] = match_data[1].to_f / 1e3
78
+ @time_hash[proc_id][recent_cpu][:exec_time] = match_data[2].to_f / 1e3
79
+ @time_hash[proc_id][recent_cpu][:sleep_time] = match_data[3].to_f / 1e3
80
+ @time_hash[proc_id][recent_cpu][:overhead_time] = match_data[4].to_f / 1e3
36
81
  end
82
+ recent_cpu = nil
37
83
  end
38
84
  end
39
85
  end
40
-
41
- @total_time /= 1e3
42
- @total_exec_time /= 1e3
43
- @total_sleep_time /= 1e3
44
- @total_overhead_time /= 1e3
45
86
  end
46
- end
47
- end
87
+ end # class
88
+ end # module Parser
48
89
  end # module StarpuProfile
@@ -1,3 +1,3 @@
1
1
  module ServerScripts
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.8"
3
3
  end
@@ -35,6 +35,8 @@ Gem::Specification.new do |spec|
35
35
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
36
36
  spec.require_paths = ["lib"]
37
37
 
38
+ spec.add_runtime_dependency 'ptools'
39
+
38
40
  spec.add_development_dependency 'minitest', '~> 5.11'
39
41
  spec.add_development_dependency 'minitest-hooks'
40
42
  spec.add_development_dependency 'minitest-fail-fast'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: server_scripts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sameer Deshmukh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-22 00:00:00.000000000 Z
11
+ date: 2020-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ptools
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: minitest
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -65,6 +79,7 @@ files:
65
79
  - README.md
66
80
  - Rakefile
67
81
  - lib/server_scripts.rb
82
+ - lib/server_scripts/batch_job.rb
68
83
  - lib/server_scripts/computer.rb
69
84
  - lib/server_scripts/computer/abci.rb
70
85
  - lib/server_scripts/computer/base.rb
@@ -75,6 +90,7 @@ files:
75
90
  - lib/server_scripts/executor/mpi_program.rb
76
91
  - lib/server_scripts/executor/valgrind.rb
77
92
  - lib/server_scripts/executor/vanilla.rb
93
+ - lib/server_scripts/experiment.rb
78
94
  - lib/server_scripts/node_type.rb
79
95
  - lib/server_scripts/parser.rb
80
96
  - lib/server_scripts/parser/itac.rb