The Gaudi Framework  v36r9p1 (5c15b2bb)
precedence.py
Go to the documentation of this file.
1 
11 from __future__ import print_function
12 
13 import json
14 import os
15 import random
16 import string
17 import sys
18 
19 # FIXME: workaround for the old version of networkx in LCG 100
20 import warnings
21 
22 warnings.filterwarnings("ignore", message='"is" with a literal', category=SyntaxWarning)
23 
24 import networkx as nx
25 from Configurables import CPUCruncher, GaudiSequencer
26 from Gaudi.Configuration import INFO
27 
28 
29 def _buildFilePath(filePath):
30 
31  if not os.path.exists(filePath):
32  __fullFilePath__ = os.path.realpath(
33  os.path.join(
34  os.environ.get("ENV_PROJECT_SOURCE_DIR", ""),
35  "GaudiHive",
36  "data",
37  filePath,
38  )
39  )
40  if not os.path.exists(__fullFilePath__):
41  __fullFilePath__ = os.path.realpath(
42  os.path.join(
43  os.environ.get("ENV_PROJECT_SOURCE_DIR", ""),
44  "Gaudi",
45  "GaudiHive",
46  "data",
47  filePath,
48  )
49  )
50  if not os.path.exists(__fullFilePath__):
51  print(
52  "\nERROR: invalid file path '%s'. "
53  "It must be either absolute, or relative to "
54  "'$ENV_PROJECT_SOURCE_DIR/GaudiHive/data/' or to "
55  "'$ENV_PROJECT_SOURCE_DIR/Gaudi/GaudiHive/data/'." % filePath
56  )
57  sys.exit(1)
58  else:
59  __fullFilePath__ = filePath
60 
61  return __fullFilePath__
62 
63 
64 class UniformTimeValue(object):
65  """A class to manage uniform algorithm timing"""
66 
67  def __init__(self, avgRuntime, varRuntime=0):
68 
69  self.avgRuntime = avgRuntime
70  self.varRuntime = varRuntime
71 
72  def get(self, algoName=""):
73  """Get time and its variance (in a tuple) for a given algorithm name"""
74 
75  return self.avgRuntime, self.varRuntime
76 
77 
78 class RealTimeValue(object):
79  """A class to manage real algorithm timing"""
80 
81  def __init__(self, path, defaultTime, factor=1):
82  """
83  defaultTime -- run time, assigned to an algorithm if no time is found in provided timing library
84  (and it will also be scaled by the 'factor' argument)
85  """
86 
87  self.path = os.path.realpath(_buildFilePath(path))
88  self.factor = factor
89  self.defaultTime = defaultTime # typically 0.05s
90  self.varRuntime = 0
91 
92  self.file = open(self.path)
93  self.timings = json.load(self.file)
94 
95  def get(self, algoName=""):
96  """Get time for a given algorithm name"""
97 
98  if algoName in self.timings:
99  time = float(self.timings[algoName])
100  else:
101  capAlgoName = algoName[0].upper() + algoName[1 : len(algoName)]
102 
103  if capAlgoName in self.timings:
104  time = float(self.timings[capAlgoName])
105  else:
106  time = self.defaultTime
107  print(
108  "WARNING: Timing for %s (or %s) not found in the provided library, using default one: %s"
109  % (algoName, capAlgoName, time)
110  )
111 
112  time = time * self.factor
113 
114  return time, self.varRuntime
115 
116 
117 class UniformBooleanValue(object):
118  def __init__(self, value):
119 
120  self.value = value
121 
122  def get(self):
123 
124  return self.value
125 
126 
127 class RndBiasedBooleanValue(object):
128  """Provides randomly ordered set of boolean values with requested proportion of True and False."""
129 
130  def __init__(self, pattern, seed=None):
131  """
132  Keyword arguments:
133  pattern -- either a dictionary describing proportion of True and False (e.g., {True:5,False:15}), or
134  a list/tuple containing a pattern to be used as-is (e.g., [False,True,True,False])
135  seed -- an int, long or other hashable object to initialize random number generator (passed to random.shuffle as-is)
136  """
137 
138  if isinstance(pattern, dict):
139  proportion = pattern
140 
141  length = proportion[True] + proportion[False]
142  if length <= 0:
143  raise "ERROR: Wrong set length requested: %i " % length
144 
145  self.pattern = [False for i in range(proportion[False])] + [
146  True for i in range(proportion[True])
147  ]
148 
149  if seed is not None:
150  random.seed(seed)
151 
152  random.shuffle(self.pattern)
153 
154  elif isinstance(pattern, (list, tuple)):
155  self.pattern = pattern
156  else:
157  raise "ERROR: unknown pattern type"
158 
160 
161  def _create_generator(self, pattern):
162 
163  for b in pattern:
164  yield b
165 
166  def get(self):
167 
168  return next(self.generator)
169 
170  def get_pattern(self):
171 
172  return self.pattern
173 
174 
175 class CruncherSequence(object):
176  """Constructs the sequence tree of CPUCrunchers with provided control flow and data flow precedence rules."""
177 
178  unique_sequencers = []
179  dupl_seqs = {}
180  OR_sequencers = []
181  unique_algos = []
182  dupl_algos = {}
183 
184  unique_data_objects = []
185 
186  def __init__(
187  self,
188  timeValue,
189  BlockingBoolValue,
190  sleepFraction,
191  cfgPath,
192  dfgPath,
193  topSequencer,
194  showStat=False,
195  timeline=False,
196  outputLevel=INFO,
197  cardinality=1,
198  ):
199  """
200  Keyword arguments:
201  timeValue -- timeValue object to set algorithm execution time
202  BlockingBoolValue -- *BooleanValue object to set whether an algorithm has to experience CPU-blocking execution
203  cfgPath -- relative to $ENV_PROJECT_SOURCE_DIR/GaudiHive/data path to GRAPHML file with control flow dependencies
204  dfgPath -- relative to $ENV_PROJECT_SOURCE_DIR/GaudiHive/data path to GRAPHML file with data flow dependencies
205  showStat -- print out statistics on precedence graph
206  """
207 
208  self.cardinality = cardinality
209  self.timeValue = timeValue
210  self.BlockingBoolValue = BlockingBoolValue
211  self.sleepFraction = sleepFraction
212 
213  self.cfg = nx.read_graphml(_buildFilePath(cfgPath))
214  self.dfg = nx.read_graphml(_buildFilePath(dfgPath))
215 
216  self.enableTimeline = timeline
217 
218  self.outputLevel = outputLevel
219 
220  # Generate control flow part
221  self.sequencer = self._generate_sequence(topSequencer)
222 
223  if showStat:
224  import pprint
225 
226  print("\n===== Statistics on Algorithms =====")
227  print(
228  "Total number of algorithm nodes: ",
229  len(self.unique_algos)
230  + sum([self.dupl_algos[i] - 1 for i in self.dupl_algos]),
231  )
232  print("Number of unique algorithms: ", len(self.unique_algos))
233  print(
234  " -->",
235  len(self.dupl_algos),
236  "of them being re-used with the following distribution: ",
237  [self.dupl_algos[i] for i in self.dupl_algos],
238  )
239  # pprint.pprint(dupl_algos)
240 
241  print("\n===== Statistics on Sequencers =====")
242  print(
243  "Total number of sequencers: ",
244  len(self.unique_sequencers)
245  + sum([self.dupl_seqs[i] - 1 for i in self.dupl_seqs]),
246  )
247  print("Number of unique sequencers: ", len(self.unique_sequencers))
248  print(
249  " -->",
250  len(self.dupl_seqs),
251  "of them being re-used with the following distribution: ",
252  [self.dupl_seqs[i] for i in self.dupl_seqs],
253  )
254  # pprint.pprint(dupl_seqs)
255  print("Number of OR-sequencers: ", len(self.OR_sequencers))
256 
257  print("\n===== Statistics on DataObjects =====")
258  print("Number of unique DataObjects: ", len(self.unique_data_objects))
259  # pprint.pprint(self.unique_data_objects)
260  print()
261 
262  def get(self):
263 
264  return self.sequencer
265 
266  def _declare_data_deps(self, algo_name, algo):
267  """Declare data inputs and outputs for a given algorithm."""
268 
269  # Declare data inputs
270  for inNode, outNode in self.dfg.in_edges(algo_name):
271  dataName = inNode
272  if dataName not in self.unique_data_objects:
273  self.unique_data_objects.append(dataName)
274 
275  if dataName not in algo.inpKeys:
276  algo.inpKeys.append(dataName)
277 
278  # Declare data outputs
279  for inNode, outNode in self.dfg.out_edges(algo_name):
280  dataName = outNode
281  if dataName not in self.unique_data_objects:
282  self.unique_data_objects.append(dataName)
283 
284  if dataName not in algo.outKeys:
285  algo.outKeys.append(dataName)
286 
287  def _generate_sequence(self, name, seq=None):
288  """Assemble the tree of sequencers."""
289 
290  if not seq:
291  seq = GaudiSequencer(name, ShortCircuit=False)
292 
293  for n in self.cfg[name]:
294  # extract entity name and type
295  algo_name = n.split("/")[1] if "/" in n else n
296 
297  if "type" in self.cfg.nodes[n]:
298  # first rely on explicit type, if given
299  algo_type = self.cfg.nodes[n].get("type")
300  else:
301  # if the type is not given explicitly, try to extract it from entity name,
302  # and, if unsuccessful, assume it is an algorithm
303  algo_type = n.split("/")[0] if "/" in n else "Algorithm"
304 
305  if algo_type in ["GaudiSequencer", "AthSequencer", "ProcessPhase"]:
306  if algo_name in ["RecoITSeq", "RecoOTSeq", "RecoTTSeq"]:
307  continue
308 
309  if n not in self.unique_sequencers:
310  self.unique_sequencers.append(n)
311  else:
312  if n not in self.dupl_seqs:
313  self.dupl_seqs[n] = 2
314  else:
315  self.dupl_seqs[n] += 1
316 
317  seq_daughter = GaudiSequencer(algo_name, OutputLevel=INFO)
318  if self.cfg.nodes[n].get("ModeOR") == "True":
319  self.OR_sequencers.append(n)
320  seq_daughter.ModeOR = True
321  # if self.cfg.nodes[n].get('Lazy') == 'False':
322  # print "Non-Lazy - ", n
323  seq_daughter.ShortCircuit = False
324  if seq_daughter not in seq.Members:
325  seq.Members += [seq_daughter]
326  # iterate deeper
327  self._generate_sequence(n, seq_daughter)
328  else:
329  # rndname = ''.join(random.choice(string.lowercase) for i in range(5))
330  # if algo_name in unique_algos: algo_name = algo_name + "-" + rndname
331  if n not in self.unique_algos:
332  self.unique_algos.append(n)
333  else:
334  if n not in self.dupl_algos:
335  self.dupl_algos[n] = 2
336  else:
337  self.dupl_algos[n] += 1
338 
339  avgRuntime, varRuntime = self.timeValue.get(algo_name)
340 
341  algo_daughter = CPUCruncher(
342  algo_name,
343  Cardinality=self.cardinality,
344  OutputLevel=self.outputLevel,
345  varRuntime=varRuntime,
346  avgRuntime=avgRuntime,
347  SleepFraction=self.sleepFraction
348  if self.BlockingBoolValue.get()
349  else 0.0,
350  Timeline=self.enableTimeline,
351  )
352 
353  self._declare_data_deps(algo_name, algo_daughter)
354 
355  if algo_daughter not in seq.Members:
356  seq.Members += [algo_daughter]
357 
358  return seq
GaudiHive.precedence._buildFilePath
def _buildFilePath(filePath)
Definition: precedence.py:29
GaudiHive.precedence.CruncherSequence.__init__
def __init__(self, timeValue, BlockingBoolValue, sleepFraction, cfgPath, dfgPath, topSequencer, showStat=False, timeline=False, outputLevel=INFO, cardinality=1)
Definition: precedence.py:186
GaudiHive.precedence.RndBiasedBooleanValue.get_pattern
def get_pattern(self)
Definition: precedence.py:170
GaudiHive.precedence.CruncherSequence.enableTimeline
enableTimeline
Definition: precedence.py:204
GaudiHive.precedence.RealTimeValue.defaultTime
defaultTime
Definition: precedence.py:89
GaudiHive.precedence.RealTimeValue.timings
timings
Definition: precedence.py:93
GaudiHive.precedence.UniformBooleanValue.__init__
def __init__(self, value)
Definition: precedence.py:118
GaudiHive.precedence.RealTimeValue.get
def get(self, algoName="")
Definition: precedence.py:95
GaudiHive.precedence.CruncherSequence.timeValue
timeValue
Definition: precedence.py:197
GaudiHive.precedence.CruncherSequence.sleepFraction
sleepFraction
Definition: precedence.py:199
GaudiHive.precedence.RealTimeValue.factor
factor
Definition: precedence.py:88
GaudiHive.precedence.UniformBooleanValue
Definition: precedence.py:117
GaudiHive.precedence.CruncherSequence.unique_sequencers
list unique_sequencers
Definition: precedence.py:178
GaudiHive.precedence.RealTimeValue.file
file
Definition: precedence.py:92
GaudiHive.precedence.UniformTimeValue.avgRuntime
avgRuntime
Definition: precedence.py:69
GaudiHive.precedence.RealTimeValue.__init__
def __init__(self, path, defaultTime, factor=1)
Definition: precedence.py:81
GaudiHive.precedence.RndBiasedBooleanValue
Definition: precedence.py:127
GaudiHive.precedence.CruncherSequence.get
def get(self)
Definition: precedence.py:262
GaudiHive.precedence.CruncherSequence.BlockingBoolValue
BlockingBoolValue
Definition: precedence.py:198
GaudiHive.precedence.CruncherSequence.sequencer
sequencer
Definition: precedence.py:209
GaudiHive.precedence.CruncherSequence.dupl_algos
dictionary dupl_algos
Definition: precedence.py:182
GaudiHive.precedence.CruncherSequence.unique_algos
list unique_algos
Definition: precedence.py:181
GaudiHive.precedence.CruncherSequence.unique_data_objects
list unique_data_objects
Definition: precedence.py:184
Gaudi.Configuration
Definition: Configuration.py:1
GaudiHive.precedence.CruncherSequence.cfg
cfg
Definition: precedence.py:201
GaudiHive.precedence.CruncherSequence.dfg
dfg
Definition: precedence.py:202
GaudiHive.precedence.UniformTimeValue
Definition: precedence.py:64
GaudiHive.precedence.RndBiasedBooleanValue.get
def get(self)
Definition: precedence.py:166
GaudiHive.precedence.RndBiasedBooleanValue.generator
generator
Definition: precedence.py:159
GaudiHive.precedence.UniformBooleanValue.value
value
Definition: precedence.py:120
GaudiHive.precedence.RealTimeValue.path
path
Definition: precedence.py:87
GaudiHive.precedence.UniformTimeValue.varRuntime
varRuntime
Definition: precedence.py:70
GaudiHive.precedence.UniformBooleanValue.get
def get(self)
Definition: precedence.py:122
GaudiHive.precedence.RndBiasedBooleanValue.pattern
pattern
Definition: precedence.py:145
GaudiHive.precedence.CruncherSequence.dupl_seqs
dictionary dupl_seqs
Definition: precedence.py:179
GaudiHive.precedence.RealTimeValue
Definition: precedence.py:78
GaudiHive.precedence.CruncherSequence._generate_sequence
def _generate_sequence(self, name, seq=None)
Definition: precedence.py:287
GaudiHive.precedence.CruncherSequence.OR_sequencers
list OR_sequencers
Definition: precedence.py:180
GaudiHive.precedence.RndBiasedBooleanValue._create_generator
def _create_generator(self, pattern)
Definition: precedence.py:161
GaudiHive.precedence.CruncherSequence.outputLevel
outputLevel
Definition: precedence.py:206
GaudiHive.precedence.CruncherSequence
Definition: precedence.py:175
GaudiHive.precedence.RealTimeValue.varRuntime
varRuntime
Definition: precedence.py:90
GaudiHive.precedence.CruncherSequence._declare_data_deps
def _declare_data_deps(self, algo_name, algo)
Definition: precedence.py:266
GaudiHive.precedence.RndBiasedBooleanValue.__init__
def __init__(self, pattern, seed=None)
Definition: precedence.py:130
GaudiHive.precedence.UniformTimeValue.__init__
def __init__(self, avgRuntime, varRuntime=0)
Definition: precedence.py:67
Gaudi::Functional::details::zip::range
decltype(auto) range(Args &&... args)
Zips multiple containers together to form a single range.
Definition: FunctionalDetails.h:102
GaudiHive.precedence.CruncherSequence.cardinality
cardinality
Definition: precedence.py:196
GaudiHive.precedence.UniformTimeValue.get
def get(self, algoName="")
Definition: precedence.py:72