The Gaudi Framework  v30r3 (a5ef0a68)
precedence.py
Go to the documentation of this file.
1 import os
2 import sys
3 import random
4 import string
5 import json
6 import networkx as nx
7 
8 from Gaudi.Configuration import INFO
9 from Configurables import GaudiSequencer, CPUCruncher
10 
11 
12 def _buildFilePath(filePath):
13 
14  if not os.path.exists(filePath):
15  __fullFilePath__ = os.path.realpath(os.path.join(
16  os.environ.get('GAUDIHIVEROOT', ''), "data", filePath))
17  if not os.path.exists(__fullFilePath__):
18  print "\nERROR: invalid file path '%s'. It must be either absolute, or relative to '$GAUDIHIVEROOT/data/'." % filePath
19  sys.exit(1)
20  else:
21  __fullFilePath__ = filePath
22 
23  return __fullFilePath__
24 
25 
26 class UniformTimeValue(object):
27  """A class to manage uniform algorithm timing"""
28 
29  def __init__(self, avgRuntime, varRuntime=0):
30 
31  self.avgRuntime = avgRuntime
32  self.varRuntime = varRuntime
33 
34  def get(self, algoName=''):
35  """Get time and its variance (in a tuple) for a given algorithm name"""
36 
37  return self.avgRuntime, self.varRuntime
38 
39 
40 class RealTimeValue(object):
41  """A class to manage real algorithm timing"""
42 
43  def __init__(self, path, defaultTime, factor=1):
44  """
45  defaultTime -- run time, assigned to an algorithm if no time is found in provided timing library
46  (and it will also be scaled by the 'factor' argument)
47  """
48 
49  self.path = os.path.realpath(_buildFilePath(path))
50  self.factor = factor
51  self.defaultTime = defaultTime # typically 0.05s
52  self.varRuntime = 0
53 
54  self.file = open(self.path)
55  self.timings = json.load(self.file)
56 
57  def get(self, algoName=''):
58  """Get time for a given algorithm name"""
59 
60  if algoName in self.timings:
61  time = float(self.timings[algoName])
62  else:
63  capAlgoName = algoName[0].upper() + algoName[1:len(algoName)]
64 
65  if capAlgoName in self.timings:
66  time = float(self.timings[capAlgoName])
67  else:
68  time = self.defaultTime
69  print "WARNING: TimiNg for %s (or %s) not found in the provided library, using default one: %s" % (
70  algoName, capAlgoName, time)
71 
72  time = time * self.factor
73 
74  return time, self.varRuntime
75 
76 
77 class UniformBooleanValue(object):
78 
79  def __init__(self, value):
80 
81  self.value = value
82 
83  def get(self):
84 
85  return self.value
86 
87 
89  """
90  Provides randomly distributed boolean value with True taking only 10%.
91  The distribution has only 276 values and is reproducible, if no pattern re-generation is requested.
92  """
93 
94  # 276 values, biased as 90% to 10%
95  builtinPattern = [True, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, True, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, True, False, True, False, False, False, False, False, False, False, False, True, False, True, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, True, False, False, False, False, True, True, False,
96  False, False, False, False, False, False, False, False, False, True, True, False, True, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, True, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, True, False, True]
97  pattern = []
98 
99  def __init__(self, useBuiltinPattern=True):
100 
101  if useBuiltinPattern:
103  else:
104  # 278 values, biased approximately as 90% to 10% (276 precedence graph algorithms, plus two algorithms added manually - DstWriter and Framework)
105  self.pattern = [False for i in range(
106  249)] + [True for i in range(29)]
107  random.shuffle(self.pattern)
108 
110 
111  def _create_generator(self, pattern):
112 
113  for b in pattern:
114  yield b
115 
116  def get(self):
117 
118  return next(self.generator)
119 
120  def get_pattern(self):
121 
122  return self.pattern
123 
124 
125 class CruncherSequence(object):
126  """Constructs the sequence tree of CPUCrunchers with provided control flow and data flow precedence rules."""
127 
128  unique_sequencers = []
129  dupl_seqs = {}
130  OR_sequencers = []
131  unique_algos = []
132  dupl_algos = {}
133 
134  unique_data_objects = []
135 
136  def __init__(self, timeValue, IOboolValue, sleepFraction, cfgPath, dfgPath, topSequencer,
137  showStat=False, timeline=False, outputLevel=INFO):
138  """
139  Keyword arguments:
140  timeValue -- timeValue object to set algorithm execution time
141  IOboolValue -- *BooleanValue object to set whether an algorithm has to experience IO-bound execution
142  cfgPath -- relative to $GAUDIHIVEROOT/data path to GRAPHML file with control flow dependencies
143  dfgPath -- relative to $GAUDIHIVEROOT/data path to GRAPHML file with data flow dependencies
144  showStat -- print out statistics on precedence graph
145  """
146 
147  self.timeValue = timeValue
148  self.IOboolValue = IOboolValue
149  self.sleepFraction = sleepFraction
150 
151  self.cfg = nx.read_graphml(_buildFilePath(cfgPath))
152  self.dfg = nx.read_graphml(_buildFilePath(dfgPath))
153 
154  self.enableTimeline = timeline
155 
156  self.outputLevel = outputLevel
157 
158  # Generate control flow part
159  self.sequencer = self._generate_sequence(topSequencer)
160 
161  if showStat:
162  import pprint
163 
164  print "\n===== Statistics on Algorithms ====="
165  print "Total number of algorithm nodes: ", len(
166  self.unique_algos) + sum([self.dupl_algos[i] - 1 for i in self.dupl_algos])
167  print "Number of unique algorithms: ", len(self.unique_algos)
168  print " -->", len(self.dupl_algos), "of them being re-used with the following distribution: ", [
169  self.dupl_algos[i] for i in self.dupl_algos]
170  # pprint.pprint(dupl_algos)
171 
172  print "\n===== Statistics on Sequencers ====="
173  print "Total number of sequencers: ", len(
174  self.unique_sequencers) + sum([self.dupl_seqs[i] - 1 for i in self.dupl_seqs])
175  print "Number of unique sequencers: ", len(self.unique_sequencers)
176  print " -->", len(self.dupl_seqs), "of them being re-used with the following distribution: ", [
177  self.dupl_seqs[i] for i in self.dupl_seqs]
178  # pprint.pprint(dupl_seqs)
179  print "Number of OR-sequencers: ", len(self.OR_sequencers)
180 
181  print "\n===== Statistics on DataObjects ====="
182  print "Number of unique DataObjects: ", len(
183  self.unique_data_objects)
184  # pprint.pprint(self.unique_data_objects)
185  print
186 
187  def get(self):
188 
189  return self.sequencer
190 
191  def _declare_data_deps(self, algo_name, algo):
192  """ Declare data inputs and outputs for a given algorithm. """
193 
194  # Declare data inputs
195  for inNode, outNode in self.dfg.in_edges(algo_name):
196  dataName = inNode
197  if dataName not in self.unique_data_objects:
198  self.unique_data_objects.append(dataName)
199 
200  if dataName not in algo.inpKeys:
201  algo.inpKeys.append(dataName)
202 
203  # Declare data outputs
204  for inNode, outNode in self.dfg.out_edges(algo_name):
205  dataName = outNode
206  if dataName not in self.unique_data_objects:
207  self.unique_data_objects.append(dataName)
208 
209  if dataName not in algo.outKeys:
210  algo.outKeys.append(dataName)
211 
212  def _generate_sequence(self, name, seq=None):
213  """ Assemble the tree of sequencers. """
214 
215  if not seq:
216  seq = GaudiSequencer(name, ShortCircuit=False)
217 
218  for n in self.cfg[name]:
219  if '/' in n:
220  algo_type, algo_name = n.split('/')
221  else:
222  algo_type = 'GaudiAlgorithm'
223  algo_name = n
224 
225  if algo_type in ['GaudiSequencer', 'AthSequencer', 'ProcessPhase']:
226  if algo_name in ['RecoITSeq', 'RecoOTSeq', 'RecoTTSeq']:
227  continue
228 
229  if n not in self.unique_sequencers:
230  self.unique_sequencers.append(n)
231  else:
232  if n not in self.dupl_seqs:
233  self.dupl_seqs[n] = 2
234  else:
235  self.dupl_seqs[n] += 1
236 
237  seq_daughter = GaudiSequencer(algo_name, OutputLevel=INFO)
238  if self.cfg.node[n].get('ModeOR') == 'True':
239  self.OR_sequencers.append(n)
240  seq_daughter.ModeOR = True
241  # if self.cfg.node[n].get('Lazy') == 'False':
242  # print "Non-Lazy - ", n
243  seq_daughter.ShortCircuit = False
244  if seq_daughter not in seq.Members:
245  seq.Members += [seq_daughter]
246  # iterate deeper
247  self._generate_sequence(n, seq_daughter)
248  else:
249  #rndname = ''.join(random.choice(string.lowercase) for i in range(5))
250  #if algo_name in unique_algos: algo_name = algo_name + "-" + rndname
251  if n not in self.unique_algos:
252  self.unique_algos.append(n)
253  else:
254  if n not in self.dupl_algos:
255  self.dupl_algos[n] = 2
256  else:
257  self.dupl_algos[n] += 1
258 
259  avgRuntime, varRuntime = self.timeValue.get(algo_name)
260  algo_daughter = CPUCruncher(algo_name,
261  OutputLevel=self.outputLevel,
262  shortCalib=True,
263  varRuntime=varRuntime,
264  avgRuntime=avgRuntime,
265  SleepFraction=self.sleepFraction if self.IOboolValue.get() else 0.,
266  Timeline=self.enableTimeline)
267 
268  self._declare_data_deps(algo_name, algo_daughter)
269 
270  if algo_daughter not in seq.Members:
271  seq.Members += [algo_daughter]
272 
273  return seq
def __init__(self, path, defaultTime, factor=1)
Definition: precedence.py:43
def _generate_sequence(self, name, seq=None)
Definition: precedence.py:212
def __init__(self, avgRuntime, varRuntime=0)
Definition: precedence.py:29
double sum(double x, double y, double z)
def get(self, algoName='')
Definition: precedence.py:57
def get(self, algoName='')
Definition: precedence.py:34
def __init__(self, timeValue, IOboolValue, sleepFraction, cfgPath, dfgPath, topSequencer, showStat=False, timeline=False, outputLevel=INFO)
Definition: precedence.py:137
def __init__(self, useBuiltinPattern=True)
Definition: precedence.py:99
decltype(auto) range(Args &&...args)
Zips multiple containers together to form a single range.
def _declare_data_deps(self, algo_name, algo)
Definition: precedence.py:191
def _buildFilePath(filePath)
Definition: precedence.py:12