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