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