Line data Source code
1 : // $Id$
2 :
3 : //**************************************************************************
4 : //* This file is property of and copyright by the *
5 : //* ALICE Experiment at CERN, All rights reserved. *
6 : //* *
7 : //* Primary Authors: Matthias Richter <Matthias.Richter@ift.uib.no> *
8 : //* *
9 : //* Permission to use, copy, modify and distribute this software and its *
10 : //* documentation strictly for non-commercial purposes is hereby granted *
11 : //* without fee, provided that the above copyright notice appears in all *
12 : //* copies and that both the copyright notice and this permission notice *
13 : //* appear in the supporting documentation. The authors make no claims *
14 : //* about the suitability of this software for any purpose. It is *
15 : //* provided "as is" without express or implied warranty. *
16 : //**************************************************************************
17 :
18 : /// @file AliHLTRootSchemaEvolutionComponent.cxx
19 : /// @author Matthias Richter
20 : /// @date 2009-10-18
21 : /// @brief Handler component for ROOT schema evolution of streamed objects
22 : ///
23 :
24 : #include "AliHLTRootSchemaEvolutionComponent.h"
25 : #include "AliHLTMessage.h"
26 : #include "AliHLTReadoutList.h"
27 : #include "AliHLTMisc.h"
28 : #include "TObjArray.h"
29 : #include "TStreamerInfo.h"
30 : #include "TList.h"
31 : #include "TFile.h"
32 : #include "TStopwatch.h"
33 : #include "TTimeStamp.h"
34 : #include "TDatime.h"
35 :
36 : #include "AliCDBStorage.h"
37 : #include "AliCDBManager.h"
38 : #include "AliCDBPath.h"
39 : #include "AliCDBId.h"
40 : #include "AliCDBMetaData.h"
41 : #include "AliCDBEntry.h"
42 :
43 : #include <numeric>
44 : using namespace std;
45 :
46 : namespace
47 : {
48 : // Helper class for std::accumulate algorithm.
49 : class AliTimeSum {
50 : public:
51 : typedef int first_argument_type;
52 : typedef AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem second_argument_type;
53 : typedef bool result_type;
54 : int operator() (int a, AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem b) {
55 0 : return a+b.GetTotalTime();
56 : }
57 : };
58 : } // end of namespace
59 :
60 : /** ROOT macro for the implementation of ROOT specific class methods */
61 8 : ClassImp(AliHLTRootSchemaEvolutionComponent)
62 :
63 : AliHLTRootSchemaEvolutionComponent::AliHLTRootSchemaEvolutionComponent()
64 3 : : AliHLTCalibrationProcessor()
65 3 : , fList()
66 3 : , fPropertyFlags(kFXS)
67 3 : , fpStreamerInfos(NULL)
68 3 : , fpEventTimer(NULL)
69 3 : , fpCycleTimer(NULL)
70 3 : , fMaxEventTime(500)
71 3 : , fFXSPrescaler(0)
72 3 : , fFileName()
73 15 : {
74 : // Collects streamer info for all input objects and produces the corresponding
75 : // calibration object for reconstruction of HLT. The component runs with a
76 : // configurable rate constraint and skips the processing of known data blocks
77 : // for the sake of performance. New data blocks are always processed and added
78 : // to the list.
79 : //
80 : // Component ID: \b ROOTSchemaEvolutionComponent <br>
81 : // Library: \b libAliHLTUtil.so <br>
82 : // Input Data Types: ::kAliHLTAnyDataType <br>
83 : // Output Data Types: none <br>
84 6 : }
85 :
86 : // FIXME: read below when defining an OCDB object here
87 : const char* AliHLTRootSchemaEvolutionComponent::fgkConfigurationObject=NULL;
88 : const AliHLTUInt32_t AliHLTRootSchemaEvolutionComponent::fgkTimeScale=1000000;
89 :
90 : AliHLTRootSchemaEvolutionComponent::~AliHLTRootSchemaEvolutionComponent()
91 18 : {
92 : // destructor
93 3 : if (fpStreamerInfos) {
94 0 : fpStreamerInfos->Clear();
95 0 : delete fpStreamerInfos;
96 : }
97 3 : fpStreamerInfos=NULL;
98 9 : }
99 :
100 : void AliHLTRootSchemaEvolutionComponent::GetInputDataTypes(AliHLTComponentDataTypeList& list)
101 : {
102 : // overloaded from AliHLTComponent
103 0 : list.push_back(kAliHLTAnyDataType);
104 0 : }
105 :
106 : AliHLTComponentDataType AliHLTRootSchemaEvolutionComponent::GetOutputDataType()
107 : {
108 : // overloaded from AliHLTComponent
109 0 : return kAliHLTDataTypeStreamerInfo;
110 : }
111 :
112 : void AliHLTRootSchemaEvolutionComponent::GetOutputDataSize(unsigned long& constBase, double& inputMultiplier)
113 : {
114 : // overloaded from AliHLTComponent
115 :
116 : // this is nothing more than an assumption, in fact it's very difficult to predict how
117 : // much output the component produces
118 0 : constBase=100*1024;
119 0 : inputMultiplier=3;
120 0 : }
121 :
122 : int AliHLTRootSchemaEvolutionComponent::InitCalibration()
123 : {
124 : // overloaded from AliHLTCalibrationProcessor: initialization
125 :
126 : int iResult=0;
127 :
128 : // default configuration from CDB
129 : // FIXME: has to be called from AliHLTCalibrationProcessor::DoInit in order to set
130 : // the default parameters from OCDB before the custom argument scan
131 : // not valid at the moment because fgkConfigurationObject==NULL
132 0 : if (iResult>=0 && fgkConfigurationObject!=NULL) iResult=ConfigureFromCDBTObjString(fgkConfigurationObject);
133 :
134 0 : if (iResult>=0) {
135 0 : fpStreamerInfos=new TObjArray();
136 0 : if (!fpStreamerInfos) iResult=-ENOMEM;
137 :
138 0 : fpEventTimer=new TStopwatch;
139 0 : if (fpEventTimer) {
140 0 : fpEventTimer->Reset();
141 0 : }
142 0 : fpCycleTimer=new TStopwatch;
143 0 : if (fpCycleTimer) {
144 0 : fpCycleTimer->Reset();
145 0 : }
146 : }
147 :
148 0 : return 0;
149 0 : }
150 :
151 : int AliHLTRootSchemaEvolutionComponent::DeinitCalibration()
152 : {
153 : // overloaded from AliHLTCalibrationProcessor: termination and cleanup
154 0 : if (fFileName.IsNull()==0) {
155 0 : WriteToFile(fFileName, fpStreamerInfos);
156 0 : fFileName.Clear();
157 0 : }
158 :
159 0 : if (fpStreamerInfos) {
160 0 : fpStreamerInfos->Clear();
161 0 : delete fpStreamerInfos;
162 : }
163 0 : fpStreamerInfos=NULL;
164 :
165 0 : if (fpEventTimer) {
166 0 : delete fpEventTimer;
167 0 : fpEventTimer=NULL;
168 0 : }
169 0 : if (fpCycleTimer) {
170 0 : delete fpCycleTimer;
171 0 : fpCycleTimer=NULL;
172 0 : }
173 0 : return 0;
174 : }
175 :
176 : int AliHLTRootSchemaEvolutionComponent::ProcessCalibration( const AliHLTComponentEventData& /*evtData*/,
177 : AliHLTComponentTriggerData& /*trigData*/ )
178 : {
179 : // overloaded from AliHLTCalibrationProcessor: event processing
180 : int iResult=0;
181 0 : AliHLTUInt32_t eventType=gkAliEventTypeUnknown;
182 0 : if (!IsDataEvent(&eventType) &&
183 0 : eventType==gkAliEventTypeStartOfRun) {
184 0 : return 0;
185 : }
186 :
187 0 : AliHLTUInt32_t listtime=accumulate(fList.begin(), fList.end(), int(0), AliTimeSum());
188 : AliHLTUInt32_t averageEventTime=0;
189 : AliHLTUInt32_t averageCycleTime=0;
190 :
191 : AliHLTUInt32_t proctime=0;
192 0 : if (fpEventTimer) {
193 0 : averageEventTime=AliHLTUInt32_t(fpEventTimer->RealTime()*fgkTimeScale)/(GetEventCount()+1);
194 0 : proctime=AliHLTUInt32_t(fpEventTimer->RealTime()*fgkTimeScale);
195 0 : fpEventTimer->Start(kFALSE);
196 0 : }
197 0 : if (fpCycleTimer) {
198 0 : fpCycleTimer->Stop();
199 0 : averageCycleTime=AliHLTUInt32_t(fpCycleTimer->RealTime()*fgkTimeScale)/(GetEventCount()+1);
200 0 : }
201 :
202 : // scale down the event processing according to the required rate
203 : // and average processing time.
204 0 : for (const AliHLTComponentBlockData* pBlock=GetFirstInputBlock();
205 0 : pBlock && iResult>=0;
206 0 : pBlock=GetNextInputBlock()) {
207 : bool processBlock=true;
208 0 : AliHLTDataBlockItem* item=FindItem(pBlock->fDataType, pBlock->fSpecification);
209 0 : if (item) {
210 : // TODO: do a selection of blocks on basis of the time spent in its processing
211 : // for now only the global processing time is checked
212 : // process if the average event time is smaller then the cycle time, i.e.
213 : // the time is spent outside the component
214 : // apply a factor 4 margin
215 0 : processBlock=4*averageEventTime<fMaxEventTime || 2*averageEventTime<averageCycleTime;
216 0 : } else {
217 : // always process new incoming blocks
218 : processBlock=true;
219 0 : fList.push_back(AliHLTDataBlockItem(pBlock->fDataType, pBlock->fSpecification));
220 0 : item=&fList[fList.size()-1];
221 : }
222 0 : if (processBlock) {
223 0 : TObject* pObj=item->Extract(pBlock);
224 0 : if (pObj) {
225 0 : AliHLTMessage msg(kMESS_OBJECT);
226 0 : msg.EnableSchemaEvolution();
227 0 : if ((iResult=item->Stream(pObj, msg))>=0) {
228 0 : iResult=UpdateStreamerInfos(msg.GetStreamerInfos(), fpStreamerInfos);
229 0 : } else {
230 0 : HLTError("failed to stream object %s of type %s", pObj->GetName(), pObj->ClassName());
231 : }
232 0 : delete pObj;
233 : pObj=NULL;
234 0 : }
235 0 : }
236 : }
237 :
238 0 : if (fpEventTimer) {
239 0 : fpEventTimer->Stop();
240 0 : proctime=AliHLTUInt32_t(fpEventTimer->RealTime()*fgkTimeScale)-proctime;
241 0 : averageEventTime=AliHLTUInt32_t(fpEventTimer->RealTime()*fgkTimeScale)/(GetEventCount()+1);
242 :
243 : // info output once every 2 seconds
244 : static UInt_t lastTime=0;
245 0 : TDatime time;
246 0 : if (time.Get()-lastTime>2) {
247 0 : lastTime=time.Get();
248 0 : HLTInfo("event time %d, average time %d, list time %d, cycle time %d", proctime, averageEventTime, listtime, averageCycleTime);
249 : }
250 0 : }
251 0 : if (fpCycleTimer) {
252 0 : fpCycleTimer->Start(kFALSE);
253 0 : }
254 :
255 0 : if (iResult>=0) {
256 0 : if ((TestBits(kHLTOUTatFirstEvent) && GetEventCount()==0) ||
257 0 : (TestBits(kHLTOUTatAllEvents))) {
258 0 : PushBack(fpStreamerInfos, kAliHLTDataTypeStreamerInfo);
259 0 : }
260 : }
261 :
262 0 : if (TestBits(kFXS) && fFXSPrescaler>0 && (GetEventCount()%fFXSPrescaler)==0) {
263 : // push to FXS
264 0 : AliHLTReadoutList rdList(AliHLTReadoutList::kHLT);
265 0 : PushToFXS((TObject*)fpStreamerInfos, "HLT", "StreamerInfo", &rdList );
266 0 : }
267 :
268 : return iResult;
269 0 : }
270 :
271 : int AliHLTRootSchemaEvolutionComponent::ShipDataToFXS( const AliHLTComponentEventData& /*evtData*/,
272 : AliHLTComponentTriggerData& /*trigData*/)
273 : {
274 : // overloaded from AliHLTCalibrationProcessor: ship data
275 0 : if (TestBits(kFXS)) {
276 : // push to FXS
277 0 : AliHLTReadoutList rdList(AliHLTReadoutList::kHLT);
278 0 : PushToFXS((TObject*)fpStreamerInfos, "HLT", "StreamerInfo", &rdList );
279 0 : }
280 :
281 0 : if (fFileName.IsNull()==0) {
282 0 : WriteToFile(fFileName, fpStreamerInfos);
283 0 : fFileName.Clear();
284 0 : }
285 :
286 0 : if (TestBits(kHLTOUTatEOR)) {
287 0 : PushBack(fpStreamerInfos, kAliHLTDataTypeStreamerInfo);
288 0 : }
289 :
290 0 : for (unsigned i=0; i<fList.size(); i++) {
291 0 : if (CheckFilter(kHLTLogDebug)) fList[i].Print("short");
292 0 : else if (fList[i].IsObject()) {
293 0 : HLTInfo("AliHLTDataBlockItem %s %08x\n"
294 : " average extraction time: %d usec\n"
295 : " average streaming time: %d usec"
296 : , AliHLTComponent::DataType2Text(fList[i]).c_str()
297 : , fList[i].GetSpecification()
298 : , fList[i].GetExtractionTime()
299 : , fList[i].GetStreamingTime());
300 : }
301 : }
302 :
303 0 : return 0;
304 0 : }
305 :
306 : int AliHLTRootSchemaEvolutionComponent::UpdateStreamerInfos(const TObjArray* list, TObjArray* infos) const
307 : {
308 : // update streamer infos
309 : int iResult=0;
310 0 : if (!list || !infos) {
311 0 : return -EINVAL;
312 : }
313 :
314 : TObject* element=NULL;
315 0 : TIter next((TList*)list);
316 0 : while ((element = next())) {
317 0 : TStreamerInfo* pInfo=dynamic_cast<TStreamerInfo*>(element);
318 0 : if (!pInfo) continue;
319 0 : TString name=pInfo->GetName();
320 : int i=0;
321 0 : if (pInfo->GetClassVersion()==0) continue; // skip classes which are not for storage
322 0 : for (; i<infos->GetEntriesFast(); i++) {
323 0 : if (name.CompareTo(infos->At(i)->GetName())==0 &&
324 0 : pInfo->GetClassVersion() == ((TStreamerInfo*)infos->At(i))->GetClassVersion()) {
325 : // have it already
326 : break;
327 : }
328 : }
329 :
330 : // Add streamer info if not yet there
331 0 : if (i>=infos->GetEntriesFast()) {
332 0 : infos->Add(pInfo);
333 : }
334 0 : }
335 :
336 : return iResult;
337 0 : }
338 :
339 : int AliHLTRootSchemaEvolutionComponent::ScanConfigurationArgument(int argc, const char** argv)
340 : {
341 : // overloaded from AliHLTComponent
342 : int iResult=0;
343 0 : if (argc<=0) return 0;
344 : int i=0;
345 0 : TString argument=argv[i];
346 :
347 : // -hltout=[all,first,eor,off]
348 0 : if (argument.Contains("-hltout")) {
349 0 : argument.ReplaceAll("-hltout", "");
350 0 : argument.ReplaceAll("=", "");
351 0 : if (argument.IsNull() || argument.CompareTo("all")==0) {
352 0 : SetBits(kHLTOUTatAllEvents|kHLTOUTatEOR);
353 0 : } else if (argument.CompareTo("first")==0) {
354 0 : SetBits(kHLTOUTatFirstEvent);
355 0 : } else if (argument.CompareTo("eor")==0) {
356 0 : SetBits(kHLTOUTatEOR);
357 0 : } else if (argument.CompareTo("off")==0) {
358 0 : ClearBits(kHLTOUTatAllEvents | kHLTOUTatFirstEvent | kHLTOUTatEOR);
359 : } else {
360 0 : HLTError("invalid parameter for argument -hltout= : %s", argument.Data());
361 0 : return -EINVAL;
362 : }
363 0 : return 1;
364 : }
365 :
366 : // -fxs=[n,off]
367 0 : if (argument.Contains("-fxs")) {
368 0 : argument.ReplaceAll("-fxs", "");
369 0 : argument.ReplaceAll("=", "");
370 0 : SetBits(kFXS);
371 0 : if (argument.IsNull()) {
372 0 : } else if (argument.CompareTo("off")==0) {
373 0 : ClearBits(kFXS);
374 0 : } else if (argument.IsDigit()) {
375 0 : fFXSPrescaler=argument.Atoi();
376 : } else {
377 0 : HLTError("invalid parameter for argument -fxs= : %s", argument.Data());
378 0 : return -EINVAL;
379 : }
380 0 : return 1;
381 : }
382 :
383 : // -file=<filename>
384 0 : if (argument.Contains("-file=")) {
385 0 : argument.ReplaceAll("-file=", "");
386 0 : if (!argument.IsNull()) {
387 0 : fFileName=argument;
388 : } else {
389 0 : HLTError("argument -file= expects file name");
390 0 : return -EINVAL;
391 : }
392 0 : return 1;
393 : }
394 :
395 0 : if (argument.Contains("-rate=")) {
396 0 : argument.ReplaceAll("-rate=", "");
397 0 : AliHLTUInt32_t rate=argument.Atoi();
398 0 : if (rate>0 && rate<fgkTimeScale) {
399 0 : fMaxEventTime=fgkTimeScale/rate;
400 : } else {
401 0 : HLTError("argument -file= expects number [Hz]");
402 0 : return -EINVAL;
403 : }
404 0 : return 1;
405 : }
406 :
407 0 : return iResult;
408 0 : }
409 :
410 : int AliHLTRootSchemaEvolutionComponent::WriteToFile(const char* filename, const TObjArray* infos) const
411 : {
412 : // write aray of streamer infos to file
413 0 : if (!filename || !infos) return -EINVAL;
414 :
415 0 : TFile out(filename, "RECREATE");
416 0 : if (out.IsZombie()) {
417 0 : HLTError("failed to open file %s", filename);
418 0 : return -EBADF;
419 : }
420 :
421 : const char* entrypath="HLT/Calib/StreamerInfo";
422 : int version = -1;
423 0 : AliCDBEntry* existingEntry=AliHLTMisc::Instance().LoadOCDBEntry(entrypath);
424 0 : if (existingEntry) {
425 0 : version=existingEntry->GetId().GetVersion();
426 0 : }
427 0 : version++;
428 :
429 : TObjArray* clone=NULL;
430 :
431 0 : if (existingEntry && existingEntry->GetObject()) {
432 0 : TObject* cloneObj=existingEntry->GetObject()->Clone();
433 0 : if (cloneObj) clone=dynamic_cast<TObjArray*>(cloneObj);
434 0 : if (AliHLTMisc::Instance().MergeStreamerInfo(clone, infos)==0) {
435 : // no change, store with identical version
436 0 : version=existingEntry->GetId().GetVersion();
437 0 : }
438 0 : } else {
439 0 : TObject* cloneObj=infos->Clone();
440 0 : if (cloneObj) clone=dynamic_cast<TObjArray*>(cloneObj);
441 : }
442 0 : if (!clone) {
443 0 : HLTError("failed to clone streamer info object array");
444 0 : return -ENOMEM;
445 : }
446 :
447 0 : AliCDBPath cdbPath(entrypath);
448 0 : AliCDBId cdbId(cdbPath, AliCDBManager::Instance()->GetRun(), AliCDBRunRange::Infinity(), version, 0);
449 0 : AliCDBMetaData* cdbMetaData=new AliCDBMetaData;
450 0 : cdbMetaData->SetResponsible("ALICE HLT Matthias.Richter@cern.ch");
451 0 : cdbMetaData->SetComment("Streamer info for HLTOUT payload");
452 0 : AliCDBEntry* entry=new AliCDBEntry(clone, cdbId, cdbMetaData, kTRUE);
453 :
454 0 : out.cd();
455 0 : entry->Write();
456 : // this is a small memory leak
457 : // seg fault in ROOT object handling if the two objects are deleted
458 : // investigate later
459 : //delete entry;
460 : //delete cdbMetaData;
461 0 : out.Close();
462 :
463 : return 0;
464 0 : }
465 :
466 : AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem*
467 : AliHLTRootSchemaEvolutionComponent::FindItem(AliHLTComponentDataType dt,
468 : AliHLTUInt32_t spec)
469 : {
470 : /// find item in the list
471 : // vector<AliHLTDataBlockItem>::iterator element=std::find(fList.begin(), fList.end(), AliHLTDataBlockItem(dt,spec));
472 : // if (element!=fList.end()) return &(*element);
473 0 : for (unsigned i=0; i<fList.size(); i++) {
474 0 : if (fList[i]==dt && fList[i]==spec) return &fList[i];
475 : }
476 0 : return NULL;
477 0 : }
478 :
479 : AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem::AliHLTDataBlockItem(AliHLTComponentDataType dt,
480 : AliHLTUInt32_t spec)
481 0 : : fDt(dt)
482 0 : , fSpecification(spec)
483 0 : , fIsObject(false)
484 0 : , fNofExtractions(0)
485 0 : , fExtractionTimeUsec(0)
486 0 : , fLastExtraction(0)
487 0 : , fNofStreamings(0)
488 0 : , fStreamingTimeUsec(0)
489 0 : , fLastStreaming(0)
490 0 : {
491 : // helper class to keep track of input data blocks
492 : // in the AliHLTRootSchemaEvolutionComponent
493 : //
494 0 : }
495 :
496 : AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem::~AliHLTDataBlockItem()
497 0 : {
498 : // destructor
499 0 : }
500 :
501 : TObject* AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem::Extract(const AliHLTComponentBlockData* bd)
502 : {
503 : /// extract data block to root object, and update performance parameters
504 : /// object needs to be deleted externally
505 0 : if (!bd || !bd->fPtr || bd->fSize<8) return NULL;
506 :
507 0 : AliHLTUInt32_t firstWord=*((AliHLTUInt32_t*)bd->fPtr);
508 0 : if (!(fIsObject=(firstWord==bd->fSize-sizeof(AliHLTUInt32_t)))) return NULL;
509 :
510 0 : TStopwatch sw;
511 0 : sw.Start();
512 0 : AliHLTMessage msg(bd->fPtr, bd->fSize);
513 0 : TClass* objclass=msg.GetClass();
514 0 : if (!(fIsObject=(objclass!=NULL))) return NULL;
515 0 : TObject* pObj=msg.ReadObject(objclass);
516 0 : if (!(fIsObject=(pObj!=NULL))) return NULL;
517 0 : sw.Stop();
518 0 : AliHLTUInt32_t usec=AliHLTUInt32_t(sw.RealTime()*fgkTimeScale);
519 0 : fNofExtractions++;
520 0 : fExtractionTimeUsec+=usec;
521 0 : TTimeStamp ts;
522 0 : fLastExtraction=(ts.GetSec()%1000)*fgkTimeScale + ts.GetNanoSec()/1000;
523 : return pObj;
524 0 : }
525 :
526 : int AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem::Stream(const TObject* obj, AliHLTMessage& msg)
527 : {
528 : /// stream object and update performance parameters
529 0 : if (!obj) return -EINVAL;
530 0 : TStopwatch sw;
531 0 : sw.Start();
532 0 : msg.WriteObject(obj);
533 :
534 0 : AliHLTUInt32_t usec=AliHLTUInt32_t(sw.RealTime()*fgkTimeScale);
535 0 : fNofStreamings++;
536 0 : fStreamingTimeUsec+=usec;
537 0 : TTimeStamp ts;
538 0 : fLastStreaming=(ts.GetSec()%1000)*fgkTimeScale + ts.GetNanoSec()/1000;
539 : return 0;
540 0 : }
541 :
542 : void AliHLTRootSchemaEvolutionComponent::AliHLTDataBlockItem::Print(const char* option) const
543 : {
544 : /// print status
545 0 : if (fIsObject || !(strcmp(option, "short")==0))
546 0 : cout << "AliHLTDataBlockItem: " << AliHLTComponent::DataType2Text(fDt).c_str() << " " << hex << fSpecification << dec << endl;
547 0 : if (fIsObject) {
548 0 : if (fNofExtractions>0) cout << " average extraction time: " << fExtractionTimeUsec/fNofExtractions << " usec" << endl;
549 0 : else cout << " never extracted" << endl;
550 0 : if (fNofStreamings>0) cout << " average streaming time: " << fStreamingTimeUsec/fNofStreamings << " usec" << endl;
551 0 : else cout << " never streamed" << endl;
552 : }
553 0 : }
|