CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
edmStreamStallGrapher.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 from itertools import groupby
3 from operator import attrgetter,itemgetter
4 import sys
5 
6 #----------------------------------------------
7 def printHelp():
8  s = '''
9 To Use: Add the Tracer Service to the cmsRun job you want to check for
10  stream stalls. Make sure to use the 'printTimstamps' option
11  cms.Service("Tracer", printTimestamps = cms.untracked.bool(True))
12  After running the job, execute this script and pass the name of the
13  log file to the script as the only command line argument.
14 
15 To Read: The script will then print an 'ASCII art' stall graph which
16  consists of the name of the module which either started or stopped
17  running on a stream, and the number of modules running on each
18  stream at that the moment in time. If the module just started, you
19  will also see the amount of time the module spent between finishing
20  its prefetching and starting. The state of a module is represented
21  by a symbol:
22 
23  plus ("+") the stream has just finished waiting and is starting a module
24  minus ("-") the stream just finished running a module
25 
26  If a module had to wait more than 0.1 seconds, the end of the line
27  will have "STALLED". Once the first 4 events have finished
28  processing, the program prints "FINISH INIT". This is useful if one
29  wants to ignore stalled caused by startup actions, e.g. reading
30  conditions.
31 
32  Once the graph is completed, the program outputs the list of modules
33  which had the greatest total stall times. The list is sorted by
34  total stall time and written in descending order. In addition, the
35  list of all stall times for the module is given.'''
36  return s
37 
38 
39 kStallThreshold=100 #in milliseconds
40 kTracerInput=False
41 
42 #Stream states
43 kStarted=0
44 kFinished=1
45 kPrefetchEnd=2
46 
47 #Special names
48 kSourceFindEvent = "sourceFindEvent"
49 kSourceDelayedRead ="sourceDelayedRead"
50 
51 #----------------------------------------------
53  processingSteps = []
54  numStreams = 0
55  maxNameSize = 0
56  foundEventToStartFrom = False
57  moduleNames = {}
58  for rawl in f:
59  l = rawl.strip()
60  if not l or l[0] == '#':
61  if len(l) > 5 and l[0:2] == "#M":
62  (id,name)=tuple(l[2:].split())
63  moduleNames[id] = name
64  continue
65  (step,payload) = tuple(l.split(None,1))
66  payload=payload.split()
67 
68  # Payload format is:
69  # <stream id> <..other fields..> <time since begin job>
70  stream = int(payload[0])
71  time = int(payload[-1])
72  trans = None
73 
74  numStreams = max(numStreams, stream+1)
75 
76  # 'S' = begin of event creation in source
77  # 's' = end of event creation in source
78  if step == 'S' or step == 's':
79  name = kSourceFindEvent
80  trans = kStarted
81  # The start of an event is the end of the framework part
82  if step == 's':
83  trans = kFinished
84  else:
85  # moduleID is the second payload argument for all steps below
86  moduleID = payload[1]
87 
88  # 'p' = end of module prefetching
89  # 'M' = begin of module processing
90  # 'm' = end of module processing
91  if step == 'p' or step == 'M' or step == 'm':
92  trans = kStarted
93  if step == 'p':
94  trans = kPrefetchEnd
95  elif step == 'm':
96  trans = kFinished
97  name = moduleNames[moduleID]
98 
99  # Delayed read from source
100  # 'R' = begin of delayed read from source
101  # 'r' = end of delayed read from source
102  if step == 'R' or step == 'r':
103  trans = kStarted
104  if step == 'r':
105  trans = kFinished
106  name = kSourceDelayedRead
107 
108  maxNameSize = max(maxNameSize, len(name))
109  processingSteps.append((name,trans,stream,time))
110 
111  f.close()
112  return (processingSteps,numStreams,maxNameSize)
113 
114 #----------------------------------------------
115 def getTime(line):
116  time = line.split(" ")[1]
117  time = time.split(":")
118  time = int(time[0])*60*60+int(time[1])*60+float(time[2])
119  time = int(1000*time) # convert to milliseconds
120  return time
121 
122 #----------------------------------------------
124  processingSteps = []
125  numStreams = 0
126  maxNameSize = 0
127  startTime = 0
128  for l in f:
129  if l.find("processing event :") != -1:
130  time = getTime(l)
131  if startTime == 0:
132  startTime = time
133  time = time - startTime
134  streamIndex = l.find("stream = ")
135  stream = int(l[streamIndex+9:l.find(" ",streamIndex+10)])
136  name = kSourceFindEvent
137  trans = kFinished
138  #the start of an event is the end of the framework part
139  if l.find("starting:") != -1:
140  trans = kStarted
141  processingSteps.append((name,trans,stream,time))
142  numStreams = max(numStreams, stream+1)
143  if l.find("processing event for module") != -1:
144  time = getTime(l)
145  if startTime == 0:
146  startTime = time
147  time = time - startTime
148  trans = kStarted
149  stream = 0
150  delayed = False
151  if l.find("finished:") != -1:
152  if l.find("prefetching") != -1:
153  trans = kPrefetchEnd
154  else:
155  trans = kFinished
156  else:
157  if l.find("prefetching") != -1:
158  #skip this since we don't care about prefetch starts
159  continue
160  streamIndex = l.find("stream = ")
161  stream = int( l[streamIndex+9:l.find(" ",streamIndex+10)])
162  name = l.split("'")[1]
163  maxNameSize = max(maxNameSize, len(name))
164  processingSteps.append((name,trans,stream,time))
165  numStreams = max(numStreams, stream+1)
166  if l.find("event delayed read from source") != -1:
167  time = getTime(l)
168  if startTime == 0:
169  startTime = time
170  time = time - startTime
171  trans = kStarted
172  stream = 0
173  delayed = False
174  if l.find("finished:") != -1:
175  trans = kFinished
176  streamIndex = l.find("stream = ")
177  stream = int(l[streamIndex+9:l.find(" ",streamIndex+10)])
178  name = kSourceDelayedRead
179  maxNameSize = max(maxNameSize, len(name))
180  processingSteps.append((name,trans,stream,time))
181  numStreams = max(numStreams, stream+1)
182  f.close()
183  return (processingSteps,numStreams,maxNameSize)
184 
185 
186 #----------------------------------------------
187 def chooseParser(inputFile):
188  firstLine = inputFile.readline().rstrip()
189  inputFile.seek(0) # Rewind back to beginning
190 
191  if firstLine.find("# Step") != -1:
192  print "> ... Parsing StallMonitor output."
193  return parseStallMonitorOutput
194  elif firstLine.find("++") != -1:
195  global kTracerInput
196  kTracerInput = True
197  print "> ... Parsing Tracer output."
198  return parseTracerOutput
199  else:
200  inputFile.close()
201  print "Unknown input format."
202  exit(1)
203 
204 #----------------------------------------------
205 def readLogFile(inputFile):
206  parseInput = chooseParser(inputFile)
207  return parseInput(inputFile)
208 
209 #----------------------------------------------
210 # Patterns:
211 #
212 # source: The source just records how long it was spent doing work,
213 # not how long it was stalled. We can get a lower bound on the stall
214 # time by measuring the time the stream was doing no work up till
215 # the source was run.
216 # modules: The time between prefetch finished and 'start processing' is
217 # the time it took to acquire any resources
218 #
219 def findStalledModules(processingSteps, numStreams):
220  streamTime = [0]*numStreams
221  stalledModules = {}
222  modulesActiveOnStream = [{} for x in xrange(numStreams)]
223  for n,trans,s,time in processingSteps:
224  waitTime = None
225  modulesOnStream = modulesActiveOnStream[s]
226  if trans == kPrefetchEnd:
227  modulesOnStream[n] = time
228  if trans == kStarted:
229  if n in modulesOnStream:
230  waitTime = time - modulesOnStream[n]
231  if n == kSourceDelayedRead:
232  if 0 == len(modulesOnStream):
233  waitTime = time - streamTime[s]
234  if trans == kFinished:
235  if n != kSourceDelayedRead and n!=kSourceFindEvent:
236  del modulesOnStream[n]
237  streamTime[s] = time
238  if waitTime is not None:
239  if waitTime > kStallThreshold:
240  t = stalledModules.setdefault(n,[])
241  t.append(waitTime)
242  return stalledModules
243 
244 
245 #----------------------------------------------
246 def createAsciiImage(processingSteps, numStreams, maxNameSize):
247  streamTime = [0]*numStreams
248  streamState = [0]*numStreams
249  modulesActiveOnStreams = [{} for x in xrange(numStreams)]
250  for n,trans,s,time in processingSteps:
251  modulesActiveOnStream = modulesActiveOnStreams[s]
252  waitTime = None
253  if trans == kPrefetchEnd:
254  modulesActiveOnStream[n] = time
255  continue
256  if trans == kStarted:
257  if n != kSourceFindEvent:
258  streamState[s] +=1
259  if n in modulesActiveOnStream:
260  waitTime = time - modulesActiveOnStream[n]
261  if n == kSourceDelayedRead:
262  if streamState[s] == 0:
263  waitTime = time-streamTime[s]
264  if trans == kFinished:
265  if n != kSourceDelayedRead and n!=kSourceFindEvent:
266  del modulesActiveOnStream[n]
267  if n != kSourceFindEvent:
268  streamState[s] -=1
269  streamTime[s] = time
270  states = "%-*s: " % (maxNameSize,n)
271  if trans == kStarted:
272  states +="+ "
273  if trans == kFinished:
274  states +="- "
275  for index, state in enumerate(streamState):
276  if n==kSourceFindEvent and index == s:
277  states +="* "
278  else:
279  states +=str(state)+" "
280  if waitTime is not None:
281  states += " %.2f"% (waitTime/1000.)
282  if waitTime > kStallThreshold:
283  states += " STALLED "+str(time/1000.)+" "+str(s)
284 
285  print states
286  return stalledModules
287 
288 #----------------------------------------------
289 def printStalledModulesInOrder(stalledModules):
290  priorities = []
291  maxNameSize = 0
292  for name,t in stalledModules.iteritems():
293  maxNameSize = max(maxNameSize, len(name))
294  t.sort(reverse=True)
295  priorities.append((name,sum(t),t))
296 
297  def sumSort(i,j):
298  return cmp(i[1],j[1])
299  priorities.sort(cmp=sumSort, reverse=True)
300 
301  nameColumn = "Stalled Module"
302  maxNameSize = max(maxNameSize, len(nameColumn))
303 
304  stallColumn = "Tot Stall Time"
305  stallColumnLength = len(stallColumn)
306 
307  print "%-*s" % (maxNameSize, nameColumn), "%-*s"%(stallColumnLength,stallColumn), " Stall Times"
308  for n,s,t in priorities:
309  paddedName = "%-*s:" % (maxNameSize,n)
310  print paddedName, "%-*.2f"%(stallColumnLength,s/1000.), ", ".join([ "%.2f"%(x/1000.) for x in t])
311 
312 #--------------------------------------------------------
313 class Point:
314  def __init__(self, x_, y_):
315  self.x = x_
316  self.y = y_
317 
318  def __str__(self):
319  return "(x: {}, y: {})".format(self.x,self.y)
320 
321  def __repr__(self):
322  return self.__str__()
323 
324 #--------------------------------------------------------
326  if len(ps) < 2:
327  return ps
328  reducedPoints = []
329  tmp = ps[0]
330  for p in ps[1:]:
331  if tmp.x == p.x:
332  tmp.y += p.y
333  else:
334  reducedPoints.append(tmp)
335  tmp = p
336  reducedPoints.append(tmp)
337  reducedPoints = [p for p in reducedPoints if p.y != 0]
338  return reducedPoints
339 
340 # -------------------------------------------
341 def adjacentDiff(*pairLists):
342  points = []
343  for pairList in pairLists:
344  points += [Point(x[0], 1) for x in pairList if x[1] != 0]
345  points += [Point(sum(x),-1) for x in pairList if x[1] != 0]
346  points.sort(key=attrgetter('x'))
347  return points
348 
349 stackType = 'stack'
350 
351 # --------------------------------------------
352 class Stack:
353  def __init__(self):
354  self.data = []
355 
356  def update(self, graphType, points):
357  tmp = points
358  if len(self.data) != 0:
359  tmp += self.data[-1][1]
360 
361  tmp.sort(key=attrgetter('x'))
362  tmp = reduceSortedPoints(tmp)
363  self.data.append((graphType, tmp))
364 
365 #---------------------------------------------
366 # StreamInfoElement
368  def __init__(self, begin_, delta_, color_):
369  self.begin=begin_
370  self.delta=delta_
371  self.color=color_
372 
373  def unpack(self):
374  return self.begin, self.delta, self.color
375 
376 #----------------------------------------------
377 # Consolidating contiguous blocks with the same color
378 # drastically reduces the size of the pdf file.
379 def consolidateContiguousBlocks(numStreams, streamInfo):
380  oldStreamInfo = streamInfo
381  streamInfo = [[] for x in xrange(numStreams)]
382 
383  for s in xrange(numStreams):
384  lastStartTime,lastTimeLength,lastColor = oldStreamInfo[s][0].unpack()
385  for info in oldStreamInfo[s][1:]:
386  start,length,color = info.unpack()
387  if color == lastColor and lastStartTime+lastTimeLength == start:
388  lastTimeLength += length
389  else:
390  streamInfo[s].append(StreamInfoElement(lastStartTime,lastTimeLength,lastColor))
391  lastStartTime = start
392  lastTimeLength = length
393  lastColor = color
394  streamInfo[s].append(StreamInfoElement(lastStartTime,lastTimeLength,lastColor))
395 
396  return streamInfo
397 
398 #----------------------------------------------
399 # Consolidating contiguous blocks with the same color drastically
400 # reduces the size of the pdf file. Same functionality as the
401 # previous function, but with slightly different implementation.
403  oldBlocks = blocks
404 
405  blocks = []
406  lastStartTime,lastTimeLength,lastHeight = oldBlocks[0]
407  for start,length,height in oldBlocks[1:]:
408  if height == lastHeight and lastStartTime+lastTimeLength == start:
409  lastTimeLength += length
410  else:
411  blocks.append((lastStartTime,lastTimeLength,lastHeight))
412  lastStartTime = start
413  lastTimeLength = length
414  lastHeight = height
415  blocks.append((lastStartTime,lastTimeLength,lastHeight))
416 
417  return blocks
418 
419 #----------------------------------------------
420 def createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModuleInfo):
421 
422  stalledModuleNames = set([x for x in stalledModuleInfo.iterkeys()])
423  streamInfo = [[] for x in xrange(numStreams)]
424  modulesActiveOnStreams = [{} for x in xrange(numStreams)]
425  streamLastEventEndTimes = [None]*numStreams
426  streamRunningTimes = [[] for x in xrange(numStreams)]
427  maxNumberOfConcurrentModulesOnAStream = 1
428  streamInvertedMessageFromModule = [set() for x in xrange(numStreams)]
429 
430  for n,trans,s,time in processingSteps:
431  startTime = None
432  if streamLastEventEndTimes[s] is None:
433  streamLastEventEndTimes[s]=time
434  if trans == kStarted:
435  if n == kSourceFindEvent:
436  # We assume the time from the end of the last event
437  # for a stream until the start of a new event for that
438  # stream is taken up by the source.
439  startTime = streamLastEventEndTimes[s]
440  moduleNames = set(n)
441  else:
442  activeModules = modulesActiveOnStreams[s]
443  moduleNames = set(activeModules.iterkeys())
444  if n in streamInvertedMessageFromModule[s] and kTracerInput:
445  # This is the rare case where a finished message
446  # is issued before the corresponding started.
447  streamInvertedMessageFromModule[s].remove(n)
448  continue
449  activeModules[n] = time
450  nModulesRunning = len(activeModules)
451  streamRunningTimes[s].append(Point(time,1))
452  maxNumberOfConcurrentModulesOnAStream = max(maxNumberOfConcurrentModulesOnAStream, nModulesRunning)
453  if nModulesRunning > 1:
454  # Need to create a new time span to avoid overlaps in graph.
455  startTime = min(activeModules.itervalues())
456  for k in activeModules.iterkeys():
457  activeModules[k]=time
458 
459  if trans == kFinished:
460  if n == kSourceFindEvent:
461  streamLastEventEndTimes[s]=time
462  else:
463  activeModules = modulesActiveOnStreams[s]
464  if n not in activeModules and kTracerInput:
465  # This is the rare case where a finished message
466  # is issued before the corresponding started.
467  streamInvertedMessageFromModule[s].add(n)
468  continue
469  streamRunningTimes[s].append(Point(time,-1))
470  startTime = activeModules[n]
471  moduleNames = set(activeModules.iterkeys())
472  del activeModules[n]
473  nModulesRunning = len(activeModules)
474  if nModulesRunning > 0:
475  # Reset start time for remaining modules to this time
476  # to avoid overlapping time ranges when making the plot.
477  for k in activeModules.iterkeys():
478  activeModules[k] = time
479  if startTime is not None:
480  c="green"
481  if (kSourceDelayedRead in moduleNames) or (kSourceFindEvent in moduleNames):
482  c = "orange"
483  for n in moduleNames:
484  if n in stalledModuleNames:
485  c="red"
486  break
487  streamInfo[s].append(StreamInfoElement(startTime, time-startTime, c))
488 
489  streamInfo = consolidateContiguousBlocks(numStreams, streamInfo)
490 
491  nr = 1
492  if shownStacks:
493  nr += 1
494  fig, ax = plt.subplots(nrows=nr, squeeze=True)
495  axStack = None
496  if shownStacks:
497  [xH,yH] = fig.get_size_inches()
498  fig.set_size_inches(xH,yH*4/3)
499  ax = plt.subplot2grid((4,1),(0,0), rowspan=3)
500  axStack = plt.subplot2grid((4,1),(3,0))
501 
502  ax.set_xlabel("Time (sec)")
503  ax.set_ylabel("Stream ID")
504  ax.set_ylim(-0.5,numStreams-0.5)
505  ax.yaxis.set_ticks(xrange(numStreams))
506 
507  height = 0.8/maxNumberOfConcurrentModulesOnAStream
508  allStackTimes={'green': [], 'red': [], 'blue': [], 'orange': []}
509  for i,perStreamInfo in enumerate(streamInfo):
510  times=[(x.begin/1000., x.delta/1000.) for x in perStreamInfo] # Scale from msec to sec.
511  colors=[x.color for x in perStreamInfo]
512  ax.broken_barh(times,(i-0.4,height),facecolors=colors,edgecolors=colors,linewidth=0)
513  for info in perStreamInfo:
514  allStackTimes[info.color].append((info.begin, info.delta))
515 
516  # Now superimpose the number of concurrently running modules on to the graph.
517  if maxNumberOfConcurrentModulesOnAStream > 1:
518 
519  for i,perStreamRunningTimes in enumerate(streamRunningTimes):
520  perStreamTimes = sorted(perStreamRunningTimes, key=attrgetter('x'))
521  perStreamTimes = reduceSortedPoints(perStreamTimes)
522  streamHeight = 0
523  preparedTimes = []
524  for t1,t2 in zip(perStreamTimes, perStreamTimes[1:]):
525  streamHeight += t1.y
526  if streamHeight < 2:
527  continue
528  preparedTimes.append((t1.x,t2.x-t1.x, streamHeight))
529  preparedTimes.sort(key=itemgetter(2))
530  preparedTimes = mergeContiguousBlocks(preparedTimes)
531  for nthreads, ts in groupby(preparedTimes, itemgetter(2)):
532  theTS = [(t[0],t[1]) for t in ts]
533  theTimes = [(t[0]/1000.,t[1]/1000.) for t in theTS]
534  yspan = (i-0.4+height,height*(nthreads-1))
535  ax.broken_barh(theTimes, yspan, facecolors='blue', edgecolors='blue', linewidth=0)
536  allStackTimes['blue'].extend(theTS*(nthreads-1))
537 
538  if shownStacks:
539  print "> ... Generating stack"
540  stack = Stack()
541  for color in ['green','blue','red','orange']:
542  tmp = allStackTimes[color]
543  tmp = reduceSortedPoints(adjacentDiff(tmp))
544  stack.update(color, tmp)
545 
546  for stk in reversed(stack.data):
547  color = stk[0]
548 
549  # Now arrange list in a manner that it can be grouped by the height of the block
550  height = 0
551  xs = []
552  for p1,p2 in zip(stk[1], stk[1][1:]):
553  height += p1.y
554  xs.append((p1.x, p2.x-p1.x, height))
555  xs.sort(key = itemgetter(2))
556  xs = mergeContiguousBlocks(xs)
557 
558  for height, xpairs in groupby(xs, itemgetter(2)):
559  finalxs = [(e[0]/1000.,e[1]/1000.) for e in xpairs]
560  axStack.broken_barh(finalxs, (0, height), facecolors=color, edgecolors=color, linewidth=0)
561 
562  axStack.set_xlabel("Time (sec)");
563  axStack.set_ylabel("# threads");
564  axStack.set_xlim(ax.get_xlim())
565  axStack.tick_params(top='off')
566 
567  fig.text(0.1, 0.95, "modules running", color = "green", horizontalalignment = 'left')
568  fig.text(0.5, 0.95, "stalled module running", color = "red", horizontalalignment = 'center')
569  fig.text(0.9, 0.95, "read from input", color = "orange", horizontalalignment = 'right')
570  fig.text(0.5, 0.92, "multiple modules running", color = "blue", horizontalalignment = 'center')
571  print "> ... Saving to file: '{}'".format(pdfFile)
572  plt.savefig(pdfFile)
573 
574 #=======================================
575 if __name__=="__main__":
576  import argparse
577  import re
578  import sys
579 
580  # Program options
581  parser = argparse.ArgumentParser(description='Convert a cmsRun log with Tracer info into a stream stall graph.',
582  formatter_class=argparse.RawDescriptionHelpFormatter,
583  epilog=printHelp())
584  parser.add_argument('filename',
585  type=argparse.FileType('r'), # open file
586  help='log file to process')
587  parser.add_argument('-g', '--graph',
588  nargs='?',
589  metavar="'stall.pdf'",
590  const='stall.pdf',
591  dest='graph',
592  help='''Create pdf file of stream stall graph. If -g is specified
593  by itself, the default file name is \'stall.pdf\'. Otherwise, the
594  argument to the -g option is the filename.''')
595  parser.add_argument('-s', '--stack',
596  action='store_true',
597  help='''Create stack plot, combining all stream-specific info.
598  Can be used only when -g is specified.''')
599  args = parser.parse_args()
600 
601  # Process parsed options
602  inputFile = args.filename
603  pdfFile = args.graph
604  shownStacks = args.stack
605 
606  doGraphic = False
607  if pdfFile is not None:
608  doGraphic = True
609  import matplotlib
610  # Need to force display since problems with CMSSW matplotlib.
611  matplotlib.use("PDF")
612  import matplotlib.pyplot as plt
613  if not re.match(r'^[\w\.]+$', pdfFile):
614  print "Malformed file name '{}' supplied with the '-g' option.".format(pdfFile)
615  print "Only characters 0-9, a-z, A-Z, '_', and '.' are allowed."
616  exit(1)
617 
618  if '.' in pdfFile:
619  extension = pdfFile.split('.')[-1]
620  supported_filetypes = plt.figure().canvas.get_supported_filetypes()
621  if not extension in supported_filetypes:
622  print "A graph cannot be saved to a filename with extension '{}'.".format(extension)
623  print "The allowed extensions are:"
624  for filetype in supported_filetypes:
625  print " '.{}'".format(filetype)
626  exit(1)
627 
628  if pdfFile is None and shownStacks:
629  print "The -s (--stack) option can be used only when the -g (--graph) option is specified."
630  exit(1)
631 
632  sys.stderr.write(">reading file: '{}'\n".format(inputFile.name))
633  processingSteps,numStreams,maxNameSize = readLogFile(inputFile)
634  sys.stderr.write(">processing data\n")
635  stalledModules = findStalledModules(processingSteps, numStreams)
636  if not doGraphic:
637  sys.stderr.write(">preparing ASCII art\n")
638  createAsciiImage(processingSteps, numStreams, maxNameSize)
639  else:
640  sys.stderr.write(">creating PDF\n")
641  createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModules)
642  printStalledModulesInOrder(stalledModules)
def parseInput
Definition: tools.py:122
boost::dynamic_bitset append(const boost::dynamic_bitset<> &bs1, const boost::dynamic_bitset<> &bs2)
this method takes two bitsets bs1 and bs2 and returns result of bs2 appended to the end of bs1 ...
void add(const std::vector< const T * > &source, std::vector< const T * > &dest)
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
T min(T a, T b)
Definition: MathUtil.h:58
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
double split
Definition: MVATrainer.cc:139