@@ -158,6 +158,11 @@ def __init__(self, id, outQueue, config):
158158 self .last_sample_time = None
159159 self .last_total = None
160160 self .software_mapper = None
161+ self .metrics_to_average = self .config .get (
162+ [self .id , "metrics_to_average" ],
163+ ["system" , "user" ])
164+ self ._average_values = {k : 0 for k in self .metrics_to_average }
165+ self ._last_averaged_values = {k : 0 for k in self .metrics_to_average }
161166
162167 software_mapper = self .config .get ([id , "software_mapper" ], None )
163168 if software_mapper is not None :
@@ -195,35 +200,62 @@ def sample(self):
195200
196201 for pid in self .pids :
197202 logger .debug ("evaluate pid: %d" , pid )
198- if not pid in self .processes .keys ():
203+ if pid not in self .processes .keys ():
199204 logger .debug ("Create new instance of Process for pid: %d" , pid )
200205 self .processes [pid ] = Process (pid , self .jobid )
201206 self .processes [pid ].update (uptime )
202207
203208 # Send information about current usage
204209 aggr , total = self ._aggregate ()
205- if self .last_sample_time :
206- time_diff = time .time () - self .last_sample_time
207- if time_diff > self .sampler_interval / 2 :
208- entry = {
209- "current" : {
210- "software" : self .map_software (aggr ),
211- "total_user" : total ["user" ],
212- "total_system" : total ["system" ],
213- "user" : (total ["user" ] - self .last_total ["user" ])
214- / time_diff ,
215- "system" : (total ["system" ] - self .last_total ["system" ])
216- / time_diff ,
217- }
210+
211+ if self .last_sample_time is None :
212+ self .last_total = total
213+ self .last_sample_time = time .time ()
214+ return
215+
216+ time_diff = time .time () - self .last_sample_time
217+ if time_diff > self .sampler_interval / 2 :
218+ entry = {
219+ "current" : {
220+ "software" : self .map_software (aggr ),
221+ "total_user" : total ["user" ],
222+ "total_system" : total ["system" ],
223+ "user" : (total ["user" ] - self .last_total ["user" ])
224+ / time_diff ,
225+ "system" : (total ["system" ] - self .last_total ["system" ])
226+ / time_diff ,
218227 }
219- self ._most_recent_sample = [self ._storage_wrapping (entry )]
220- self .store (entry )
221- self .last_total = total
222- self .last_sample_time = time .time ()
223- else :
228+ }
229+ self .compute_sample_averages (entry ["current" ])
230+ self ._most_recent_sample = [self ._storage_wrapping (entry )]
231+ self .store (entry )
224232 self .last_total = total
225233 self .last_sample_time = time .time ()
226234
235+ def compute_sample_averages (self , data ):
236+ """ Computes averages of selected measurements by
237+ means of trapezoidal quadrature, approximating
238+ that the time this function is called is the actual
239+ time of sampling. This is not completely correct but simplifies
240+ the implementation.
241+ """
242+ sample_time = time .time ()
243+ elapsed_time = sample_time - self .last_sample_time
244+ total_elapsed_time = sample_time - self .create_time
245+ for key , item in data .items ():
246+ if key in self .metrics_to_average :
247+ # Trapezoidal quadrature
248+ weighted_item = (
249+ 0.5 * (float (item ) + float (self ._last_averaged_values [key ])) * elapsed_time )
250+ self ._last_averaged_values [key ] = item
251+ previous_integral = self ._average_values [key ] * (total_elapsed_time - elapsed_time )
252+ new_integral = previous_integral + weighted_item
253+ self ._average_values [key ] = new_integral / total_elapsed_time
254+
255+ for key , item in self ._average_values .items ():
256+ data [key + '_average' ] = item
257+ data ['elapsed_time' ] = total_elapsed_time
258+
227259 def last_updated (self ):
228260 procs = list (filter (lambda p : not p .ignore , self .processes .values ()))
229261 if not procs :
@@ -250,7 +282,7 @@ def _aggregate(self):
250282 a ["system" ],
251283 )
252284 exe = a ["exe" ]
253- if not exe in aggr :
285+ if exe not in aggr :
254286 aggr [exe ] = {"user" : 0.0 , "system" : 0.0 }
255287 aggr [exe ]["user" ] += a ["user" ]
256288 aggr [exe ]["system" ] += a ["system" ]
0 commit comments