Salome HOME
[EDF30399] : Steer directory hosting replay files
[modules/kernel.git] / src / Container / SALOME_ContainerHelper.py
1 #  -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2023-2024  CEA, EDF
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 from collections import defaultdict
22
23 import pickle
24
25 class ScriptExecInfo:
26     @classmethod
27     def GetRepresentationOfTimeDelta(cls,endTime, startTime):
28        if endTime is None and startTime is None:
29           return "not measured"
30        td = endTime - startTime
31        import time
32        ts_of_td = time.gmtime(td.total_seconds())
33        return "{}.{:06d}".format(time.strftime("%H:%M:%S",ts_of_td),td.microseconds)
34     
35     @classmethod
36     def MemRepr(cls,memInByte):
37       m = int( memInByte )
38       UNITS=["B","kB","MB","GB"]
39       remain = 0
40       oss = ""
41       for i in range( len(UNITS) ):
42           if m<1024:
43               oss = "{:03d}".format( int( (remain/1024)*1000 ) )
44               oss = "{}.{} {}".format(m,oss,UNITS[i])
45               return oss
46           else:
47               if i!=3:
48                   remain = m%1024
49                   m//=1024
50       return "{} {}".format(m,UNITS[3])
51     
52     @classmethod
53     def SpeedRepr(cls,memInBytePerS):
54        return "{}/s".format( cls.MemRepr(memInBytePerS) )
55
56     def __init__(self):
57       self._measure_time_resolution_ms = None
58       self._cpu_mem_during_exec = None
59       self._start_exec_time = None
60       self._end_exec_time = None
61       self._start_input_time = None
62       self._end_input_time = None
63       self._start_output_time = None
64       self._end_output_time = None
65       self._input_mem = 0
66       self._input_hdd_mem = None
67       self._output_mem = 0
68       self._output_hdd_mem = None
69       self._start_pos_log = None
70       self._stop_pos_log = None
71       self._freestyle_log = []
72
73     @property
74     def freestyle(self):
75        return self._freestyle_log
76     
77     @freestyle.setter
78     def freestyle(self, value):
79        self._freestyle_log.append( value )
80
81     @property
82     def measureTimeResolution(self):
83       return self._measure_time_resolution_ms
84     
85     @measureTimeResolution.setter
86     def measureTimeResolution(self, value):
87       self._measure_time_resolution_ms = value
88
89     @property
90     def tracePosStart(self):
91       return self._start_pos_log
92     
93     @tracePosStart.setter
94     def tracePosStart(self,value):
95       self._start_pos_log = value
96
97     @property
98     def tracePosStop(self):
99       return self._stop_pos_log
100     
101     @tracePosStop.setter
102     def tracePosStop(self,value):
103       self._stop_pos_log = value
104       
105     @property
106     def CPUMemDuringExec(self):
107       return self._cpu_mem_during_exec
108     
109     @CPUMemDuringExec.setter
110     def CPUMemDuringExec(self,value):
111       self._cpu_mem_during_exec = value
112
113     @property
114     def CPUMemDuringExecStr(self):
115       return [(a,ScriptExecInfo.MemRepr(b)) for a,b in self._cpu_mem_during_exec.data]
116
117     @property
118     def inputMem(self):
119       return self._input_mem
120     
121     @inputMem.setter
122     def inputMem(self,value):
123       self._input_mem = value
124        
125     @property
126     def inputMemStr(self):
127       return ScriptExecInfo.MemRepr( self.inputMem )
128     
129     @property
130     def outputMem(self):
131       return self._output_mem
132     
133     @outputMem.setter
134     def outputMem(self,value):
135       self._output_mem = value
136        
137     @property
138     def outputMemStr(self):
139       return ScriptExecInfo.MemRepr( self.outputMem )
140     
141     def inputReadHDDSize(self):
142        return self.inputHDDMem.getSizeOfFileRead()
143     
144     def inputReadHDDSizeRepr(self):
145        return ScriptExecInfo.MemRepr( self.inputReadHDDSize() )
146     
147     def inputReadHDDSpeed(self):
148        return self.inputReadHDDSize() / ( self.endInputTime - self.startInputTime ).total_seconds()
149     
150     def inputReadHDDSpeedRepr(self):
151        return ScriptExecInfo.SpeedRepr( self.inputReadHDDSpeed() )
152     
153     def outputWriteHDDSize(self):
154        return self.outputHDDMem.getSizeOfFileRead()
155     
156     def outputWriteHDDSizeRepr(self):
157        return ScriptExecInfo.MemRepr( self.outputWriteHDDSize() )
158
159     def outputWriteHDDSpeed(self):
160       return self.outputWriteHDDSize() / ( self.endOutputTime - self.startOutputTime ).total_seconds()
161     
162     def outputWriteHDDSpeedRepr(self):
163       return ScriptExecInfo.SpeedRepr( self.outputWriteHDDSpeed() )
164     
165     @property
166     def inputHDDMem(self):
167       return self._input_hdd_mem
168     
169     @inputHDDMem.setter
170     def inputHDDMem(self,value):
171       self._input_hdd_mem = value
172
173     @property
174     def inputHDDMemStr(self):
175       if self._input_hdd_mem is None:
176          return "not computed"
177       return " ".join( [ ScriptExecInfo.MemRepr( elt.getSizeOfFileRead() ) for elt in self._input_hdd_mem] )
178     
179     @property
180     def outputHDDMem(self):
181       return self._output_hdd_mem
182     
183     @outputHDDMem.setter
184     def outputHDDMem(self,value):
185       self._output_hdd_mem = value
186
187     @property
188     def outputHDDMemStr(self):
189       if self._output_hdd_mem is None:
190          return "not computed"
191       return " ".join( [ ScriptExecInfo.MemRepr( elt.getSizeOfFileRead() ) for elt in self._output_hdd_mem] )
192
193     @property
194     def startInputTime(self):
195       return self._start_input_time
196     
197     @startInputTime.setter
198     def startInputTime(self,value):
199       self._start_input_time = value
200
201     @property
202     def endInputTime(self):
203       return self._end_input_time
204     
205     @endInputTime.setter
206     def endInputTime(self,value):
207       self._end_input_time = value
208
209     @property
210     def startExecTime(self):
211       return self._start_exec_time
212     
213     @startExecTime.setter
214     def startExecTime(self,value):
215       self._start_exec_time = value
216
217     @property
218     def endExecTime(self):
219       return self._end_exec_time
220     
221     @endExecTime.setter
222     def endExecTime(self,value):
223       self._end_exec_time = value
224
225     @property
226     def execTime(self):
227       if ( self.endExecTime is not None ) and (self.startExecTime is not None):
228         return self.endExecTime - self.startExecTime
229       return None
230     
231     @property
232     def fullExecTime(self):
233       return self.endOutputTime - self.startInputTime
234
235     @property
236     def startOutputTime(self):
237       return self._start_output_time
238     
239     @startOutputTime.setter
240     def startOutputTime(self,value):
241       self._start_output_time = value
242
243     @property
244     def endOutputTime(self):
245       return self._end_output_time
246     
247     @endOutputTime.setter
248     def endOutputTime(self,value):
249       self._end_output_time = value
250
251     @property
252     def execTimeStr(self):
253        return ScriptExecInfo.GetRepresentationOfTimeDelta(self.endExecTime,self.startExecTime)
254     
255     @property
256     def inputTimeStr(self):
257        return ScriptExecInfo.GetRepresentationOfTimeDelta(self.endInputTime,self.startInputTime)
258     
259     @property
260     def outputTimeStr(self):
261        return ScriptExecInfo.GetRepresentationOfTimeDelta(self.endOutputTime,self.startOutputTime)
262
263     def __str__(self):
264       CPUMemDuringExecForStr = self.CPUMemDuringExecStr
265       if len( CPUMemDuringExecForStr ) > 30:
266          CPUMemDuringExecForStr = "{} ...".format( str(CPUMemDuringExecForStr[:30]) )
267       else:
268          CPUMemDuringExecForStr = str( CPUMemDuringExecForStr )
269       return """start exec time = {self.startExecTime}
270 end exec time = {self.endExecTime}
271 exec_time = {self.execTimeStr}
272 Measure time resolution = {self.measureTimeResolution} ms
273 CPU and mem monitoring = {CPUMemDuringExecForStr}
274 input unpickling and ev load from disk time = {self.inputTimeStr}
275 output serialization and ev write to disk time = {self.outputTimeStr}
276 input memory size before exec (MemoryPeak 2x) = {self.inputMemStr}
277 input memory size from HDD = {self.inputHDDMemStr}
278 output memory size after exec (MemoryPeak 2x) = {self.outputMemStr}
279 output memory size from HDD = {self.outputHDDMemStr}
280 Start position in log = {self.tracePosStart}
281 End position in log = {self.tracePosStop}""".format(**locals())
282
283 class ScriptExecInfoDeco:
284   def __init__(self, eff, father):
285     self._eff = eff
286     self._father = father
287   @property
288   def father(self):
289     return self._father
290   def get(self):
291     return self._eff
292   def __getitem__(self,i):
293     return self._eff[i]
294   def __str__(self):
295     return self._eff.__str__()
296   def __repr__(self):
297     return self._eff.__repr__()
298   def log(self):
299     with open(self.father.father.logfile,"rb") as f:
300        cont = f.read()
301     return cont[self._eff.tracePosStart:self._eff.tracePosStop].decode()
302   @property
303   def nodeName(self):
304     return self.father.get().nodeName
305   @property
306   def code(self):
307     return self.father.code
308   @property
309   def ns_entry(self):
310     return self.father.father.ns_entry
311   @property
312   def computingNode(self):
313     return self.father.computingNode
314   
315   @property
316   def freestyle(self):
317       return self.get().freestyle
318   
319   @property
320   def measureTimeResolution(self):
321     return self.get().measureTimeResolution
322     
323   @property
324   def CPUMemDuringExec(self):
325     return self.get().CPUMemDuringExec
326
327   @property
328   def inputMem(self):
329     return self.get().inputMem
330   
331   @property
332   def outputMem(self):
333     return self.get().outputMem
334   
335   @property
336   def inputHDDMem(self):
337     return self.get().inputHDDMem
338   
339   @property
340   def outputHDDMem(self):
341     return self.get().outputHDDMem
342   
343   def inputReadHDDSize(self):
344     return self.get().inputReadHDDSize()
345   
346   def inputReadHDDSizeRepr(self):
347     return self.get().inputReadHDDSizeRepr()
348   
349   def inputReadHDDSpeed(self):
350     return self.get().inputReadHDDSpeed()
351   
352   def inputReadHDDSpeedRepr(self):
353     return self.get().inputReadHDDSpeedRepr()
354   
355   def outputWriteHDDSize(self):
356     return self.get().outputWriteHDDSize()
357   
358   def outputWriteHDDSizeRepr(self):
359     return self.get().outputWriteHDDSizeRepr()
360   
361   def outputWriteHDDSpeed(self):
362     return self.get().outputWriteHDDSpeed()
363   
364   def outputWriteHDDSpeedRepr(self):
365     return self.get().outputWriteHDDSpeedRepr()
366
367   @property
368   def startInputTime(self):
369     return self.get().startInputTime
370
371   @property
372   def endInputTime(self):
373     return self.get().endInputTime
374
375   @property
376   def startExecTime(self):
377     return self.get().startExecTime
378
379   @property
380   def endExecTime(self):
381     return self.get().endExecTime
382
383   @property
384   def execTime(self):
385     return self.get().execTime
386   
387   @property
388   def fullExecTime(self):
389      return self.get().fullExecTime
390
391   @property
392   def startOutputTime(self):
393     return self.get().startOutputTime
394
395   @property
396   def endOutputTime(self):
397     return self.get().endOutputTime
398
399 class ScriptInfoAbstract:
400   def __init__(self, scriptPtr):
401       self._node_name = scriptPtr.getName()
402       self._code = scriptPtr.getCode()
403       self._exec = [pickle.loads(elt.getObj()) for elt in scriptPtr.listOfExecs()]
404
405   @property
406   def execs(self):
407       return self._exec
408
409   @property
410   def nodeName(self):
411       return self._node_name
412
413   @property
414   def code(self):
415       return self._code
416   
417   @code.setter
418   def code(self,value):
419       self._code = value
420
421   def __len__(self):
422       return len( self._exec )
423   
424   def __getitem__(self,i):
425       return self._exec[i]
426
427   def __str__(self):
428       return """name = {self.nodeName}\ncode = {self.code}\nexecs = {self.execs}""".format(**locals())
429   
430   def __repr__(self):
431       return """ScriptInfo \"{self.nodeName}\"""".format(**locals())
432   
433 class ScriptInfoClt(ScriptInfoAbstract):
434   def __init__(self, scriptPtr):
435       def unPickledSafe( dataPickled ):
436         if len(dataPickled) > 0:
437           return pickle.loads(dataPickled)
438         else:
439            return None  
440       self._node_name = scriptPtr.getName()
441       self._code = scriptPtr.getCode()
442       self._exec = [unPickledSafe(elt.getObj()) for elt in scriptPtr.listOfExecs()]
443
444 class ScriptInfo(ScriptInfoAbstract):
445   def __init__(self, nodeName, code, execs):
446       self._node_name = nodeName
447       self._code = code
448       self._exec = execs
449
450 class ScriptInfoDeco:
451   def __init__(self, eff, father):
452     self._eff = eff
453     self._father = father
454   @property
455   def father(self):
456     return self._father
457   def get(self):
458     return self._eff
459   @property
460   def nodeName(self):
461     return self.get().nodeName
462   @property
463   def code(self):
464     return self.get().code
465   @property
466   def ns_entry(self):
467     return self.father.ns_entry
468   @property
469   def computingNode(self):
470     return self.father.computingNode
471   def __getitem__(self,i):
472     return ScriptExecInfoDeco( self._eff[i], self )
473   def __len__(self):
474     return self._eff.__len__()
475   def __str__(self):
476     return self._eff.__str__()
477   def __repr__(self):
478     return self._eff.__repr__()
479
480 class ContainerLogInfoAbstract:
481   
482   @property
483   def log(self):
484     with open(self.logfile,"rb") as f:
485        cont = f.read()
486     return cont.decode()
487
488   @property
489   def ns_entry(self):
490     return self._ns_entry
491   
492   @property
493   def logfile(self):
494     return self._log_file
495   
496   @property
497   def computingNode(self):
498     return ContainerLogInfoAbstract.ComputingNodeFromNSEntry( self.ns_entry )
499   
500   def __len__(self):
501     return len( self._scripts )
502   
503   def __getitem__(self,i):
504     return ScriptInfoDeco( self._scripts[i], self)
505   
506   def __str__(self):
507     return """NS entry = {self.ns_entry} LogFile = {self.logfile}""".format(**locals())
508   
509   @classmethod
510   def ComputingNodeFromNSEntry(cls, nsEntry):
511     return nsEntry.split("/")[2]
512   
513 class ContainerLogInfoClt(ContainerLogInfoAbstract):
514   def __init__(self,contLogPtr):
515     self._log_file = contLogPtr.getLogFile()
516     self._ns_entry = contLogPtr.getContainerEntryInNS()
517     self._scripts = [ScriptInfoClt(elt) for elt in contLogPtr.listOfScripts()]
518     
519 class ContainerLogInfo(ContainerLogInfoAbstract):
520   def __init__(self, nsEntry, logFile, scripts):
521      self._log_file = logFile
522      self._ns_entry = nsEntry
523      self._scripts = scripts
524
525 from abc import ABC, abstractmethod
526
527 class InOutputObjVisitorAbstract(ABC):
528   def __init__(self):
529       self._cur_obj = None
530       self._data = []
531
532   def enter(self):
533       self._cur_obj = ObjMemModel()
534       return self._cur_obj
535   
536   def leave(self):
537       self._data.append( self._cur_obj )
538       self._cur_obj = None
539
540   def getSizeOfFileRead(self):
541       return sum( [elt.getSizeOfFileRead() for elt in self._data] )
542       
543   def visitor(self):
544       return self
545
546   def setHDDMem(self, v):
547       pass
548   
549   def setFileName(self, fileName):
550       pass
551
552   @abstractmethod
553   def getRepr(self):
554       pass
555
556 class InOutputObjVisitorIter:
557   def __init__(self, visitor):
558       self._visitor = visitor
559       self._current = 0
560
561   def __next__(self):
562       if self._current >= len(self._visitor._data):
563             raise StopIteration
564       else:
565         ret = self._visitor._data[ self._current ]
566         self._current += 1
567         return ret
568
569 class InOutputObjVisitor(InOutputObjVisitorAbstract):
570   def __init__(self):
571       super().__init__()
572       
573   def getRepr(self):
574       return self.getSizeOfFileRead()
575   
576   def __iter__(self):
577      return InOutputObjVisitorIter(self)
578
579 class ObjMemModel(InOutputObjVisitorAbstract):
580   def __init__(self):
581       super().__init__()
582       self._hdd_mem = 0
583       self._file_name = None
584       
585   def setHDDMem(self, v):
586       self._hdd_mem = v
587       del self._data
588
589   def setFileName(self, fileName):
590       self._file_name = fileName
591       pass
592       
593   def getSizeOfFileRead(self):
594       if hasattr(self,"_data"):
595         return super().getSizeOfFileRead()
596       else:
597         return self._hdd_mem
598   
599   def getRepr(self):
600       return self.getSizeOfFileRead()
601
602 class FakeObjVisitor:
603   def setHDDMem(self, v):
604       pass
605   
606   def setFileName(self, fileName):
607       pass
608     
609   def visitor(self):
610       return None
611
612 class InOutputObjVisitorCM:
613   def __init__(self, visitor):
614      self._visitor = visitor
615   def __enter__(self):
616       if self._visitor:
617         r = self._visitor.enter()
618         return r
619       else:
620         return FakeObjVisitor()
621   def __exit__(self,exctype, exc, tb):
622       if self._visitor:
623         self._visitor.leave()
624       pass
625   
626 class OffsetType:
627   def __init__(self,i):
628     self._i = i
629   def __int__(self):
630     return self._i
631   def __iadd__(self,delta):
632     self._i += delta
633     return self
634
635 def unserializeInt(structData, offset):
636     from ctypes import c_int
637     sz_of_cint = 4
638     sz = c_int.from_buffer_copy( structData[int(offset):int(offset)+sz_of_cint] ).value
639     offset += sz_of_cint
640     return sz
641
642 def unserializeString(structData,offset):
643     sz = unserializeInt(structData,offset)
644     ret = structData[int(offset):int(offset)+sz].decode()
645     offset += sz
646     return ret
647
648 def unserializeContainerScriptExecPerfLog(structData, offset):
649     import pickle
650     sz = unserializeInt(structData,offset)
651     inst = None
652     if sz > 0:
653         inst = pickle.loads( structData[int(offset):int(offset)+sz] )
654     offset += sz
655     return inst
656
657 def unserializeContainerScriptPerfLog(structData, offset):
658     name = unserializeString(structData,offset)
659     code = unserializeString(structData,offset)
660     numberOfSessions = unserializeInt(structData,offset)
661     sessions = []
662     for _ in range(numberOfSessions):
663         session = unserializeContainerScriptExecPerfLog(structData,offset)
664         sessions.append( session )
665     return ScriptInfo(name,code,sessions)
666
667 def unserializeContainerPerfLog(structData, offset):
668     nsEntry = unserializeString(structData,offset)
669     logFile = unserializeString(structData,offset)
670     scripts = []
671     nbScripts = unserializeInt(structData,offset)
672     for _ in range(nbScripts):
673         script = unserializeContainerScriptPerfLog(structData,offset)
674         scripts.append( script )
675     return ContainerLogInfo(nsEntry,logFile,scripts)
676
677 def unserializeLogManager(structData):
678     offset = OffsetType(0)
679     numberOfScripts = unserializeInt(structData,offset)
680     logManagerInst = []
681     for _ in range(numberOfScripts):
682         containerPerfLogInst = unserializeContainerPerfLog(structData,offset)
683         logManagerInst.append( containerPerfLogInst )
684     if int(offset) != len(structData):
685         raise RuntimeError("Something went wrong during unserialization phase.")
686     return logManagerInst
687
688 def ListAllExecContainIn( listOfContainerLogInfo ):
689     """
690     For all ContainerLogInfo contained in listOfContainerLogInfo extract all ScriptExecInfo contained recursively
691     in it. This method filters all "side" executions like those positionning environment for exemple.
692
693     See also : ListAllExecFinishedContainIn
694
695     Args:
696     -----
697
698     listOfContainerLogInfo (list<ContainerLogInfo>) : instance typically returned by salome.LogManagerLoadFromFile
699
700     Returns
701     -------
702
703     list<ScriptExecInfoDeco> : all ScriptExecInfoDeco instance contained recursively in all input ContainerLogInfo instances
704
705     """
706     allexecs = sum( [sum( [[myexec for myexec in ps] for ps in cont],[] ) for cont in listOfContainerLogInfo], [] )
707     return [elt for elt in allexecs if elt.get() is not None]
708
709 def ListAllExecFinishedContainIn( listOfContainerLogInfo ):
710     """
711     For all ContainerLogInfo contained in listOfContainerLogInfo extract all ScriptExecInfo contained recursively
712     in it. This method filters all "side" executions like those positionning environment for exemple and those not finished.
713
714     See also : ListAllExecContainIn
715
716     Args:
717     -----
718
719     listOfContainerLogInfo (list<ContainerLogInfo>) : instance typically returned by salome.LogManagerLoadFromFile
720
721     Returns
722     -------
723
724     list<ScriptExecInfoDeco> : all ScriptExecInfoDeco instance contained recursively in all input ContainerLogInfo instances
725
726     """
727     beforeFiltering = ListAllExecContainIn( listOfContainerLogInfo )
728     return [elt for elt in beforeFiltering if elt.get().execTime is not None]
729
730 def IsExecTimeHigherThan( execInstDeco, limitDuration ):
731     """
732     Example of Usage :
733
734     [elt for elt in allexecs if IsExecTimeHigherThan(elt,datetime.timedelta(hours=1))]
735     
736     Args:
737     -----
738
739     execInstDeco (ScriptExecInfoDeco)
740     limitDuration (datetime.timedelta)  Ex (datetime.timedelta(hours=1))
741
742     """
743     if execInstDeco.get() is not None:
744         return execInstDeco.get().execTime > limitDuration
745     else:
746         return False
747     
748 def GetMaxTimeExec( listOfFinishedExecs ):
749     """
750     Returns the instance among listOfFinishedExecs that spends the max time to be executed
751
752     Args:
753     -----
754
755     listOfFinishedExecs ( list<ScriptExecInfoDeco> ) : instance of ScriptExecInfoDeco that have finished to be executed
756     Typically returned by ListAllExecFinishedContainIn
757     
758     Returns
759     -------
760
761     ScriptExecInfoDeco :
762     """
763     return listOfFinishedExecs[ max([(i,elt.execTime) for i,elt in enumerate( listOfFinishedExecs) ],key = lambda x : x[1])[0] ]
764
765 def GetMinTimeExec( listOfFinishedExecs ):
766     """
767     Returns the instance among listOfFinishedExecs that spends the min time to be executed
768
769     Args:
770     -----
771
772     listOfFinishedExecs ( list<ScriptExecInfoDeco> ) : instance of ScriptExecInfoDeco that have finished to be executed
773     Typically returned by ListAllExecFinishedContainIn
774     
775     Returns
776     -------
777
778     ScriptExecInfoDeco :
779     """
780     return listOfFinishedExecs[ min([(i,elt.execTime) for i,elt in enumerate( listOfFinishedExecs) ],key = lambda x : x[1])[0] ]
781
782 class DistriutionClass:
783     def __init__(self, begin, end, listOfExecs):
784         self._begin = begin
785         self._end = end
786         self._list_of_execs = sorted( listOfExecs, key = lambda x : x.execTime)
787     def __getitem__(self, *args):
788         return self._list_of_execs.__getitem__( *args )
789     @property
790     def begin(self):
791         return self._begin
792     @property
793     def end(self):
794         return self._end
795     @property
796     def listOfExecs(self):
797         return self._list_of_execs
798     @property
799     def size(self):
800         return len( self.listOfExecs )
801     @property
802     def reprGlobal(self):
803         return f"[{self.begin}->{self.end}] : {self.size} execs"
804
805 class DistributionOfExecs:
806     """
807     Class in charge to distribute execution log instance into equi-classes
808     """
809     def __init__(self, listOfFinishedExecs, nbOfClasses):
810         self._list_of_finished_execs = listOfFinishedExecs
811         self._nb_of_classes = nbOfClasses
812         self._classes = DistributionOfExecs.ComputeDistributionOfExecTime(self._list_of_finished_execs,self._nb_of_classes)
813     
814     def __getitem__(self, *args):
815         return self._classes.__getitem__( *args )
816     def reprOfExecs(self):
817         import numpy as np
818         cs = np.cumsum( [elt.size for elt in self._classes] )
819         ret = []
820         for i,(elt,cum_nb) in enumerate( zip(self._classes,cs) ):
821             ratio = (cum_nb / cs[-1])*100.0
822             ret.append( f"- For class {i} - {elt.reprGlobal} ( {ratio:.1f}% )" )
823         return "\n".join( ret )
824     @classmethod
825     def ComputeDistributionOfExecTime( cls, listOfFinishedExecs, nbOfClasses ):
826         maxt = GetMaxTimeExec( listOfFinishedExecs )
827         mint = GetMinTimeExec( listOfFinishedExecs )
828         deltaTime = ( maxt.execTime - mint.execTime ) / nbOfClasses
829         res = []
830         for i in range( nbOfClasses ):
831             begin = mint.execTime + i * deltaTime
832             end = mint.execTime + (i+1) * deltaTime
833             res.append( DistriutionClass(begin,end,[elt for elt in listOfFinishedExecs if elt.execTime >=begin and elt.execTime <= end]) )
834         return res