Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
TMPIFile.cxx
Go to the documentation of this file.
1// @(#)root/io:$Id$
2// Author: Amit Bashyal, August 2018
3
4/*************************************************************************
5 * Copyright (C) 1995-2002, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "TMPIFile.h"
13#include "TFileCacheWrite.h"
14#include "TKey.h"
15#include "THashTable.h"
16#include "TMath.h"
17
18
19/** \class TMPIFile
20A TFile implimentation that uses MPI to enable multiple parallel MPI
21processes to write to a single file.
22
23### Example usage
24
25Begin_Macro (source)
26{
27 Int_t n_collectors = 2;
28 TMPIFile *newfile = new TMPIFile("mpi_output.root", "RECREATE", n_collectors);
29
30 if (newfile->IsCollector())
31 // collector rank, listens for incoming data
32 newfile->RunCollector();
33 else{
34 // worker rank
35
36 // generate data objects
37
38 // syncronize data across ranks
39 newfile->Sync();
40 }
41
42 newfile->Close();
43}
44End_Macro
45
46See TMPIFile class for the list of functions
47*/
48
50
51////////////////////////////////////////////////////////////////////////////////
52/// TMPIFile constructor
53///
54/// See TMemFile for constructor explanation of the syntax.
55///
56/// \param[split] is the number of collectors to use
57
58TMPIFile::TMPIFile(const char *name, char *buffer, Long64_t size, Option_t *option, Int_t split, const char *ftitle,
59 Int_t compress)
60 : TMemFile(name, buffer, size, option, ftitle, compress), fSplitLevel(split), fMPIColor(0), fMPIRequest(0),
61 fSendBuf(0)
62{
63 // check that split is set to reasonable value
65 // split the MPI COMM_WORLD into sub-communicators
66 // one for each split level
68}
69
70////////////////////////////////////////////////////////////////////////////////
71/// TMPIFile constructor
72///
73/// See TMemFile for constructor explanation of the syntax.
74///
75/// \param[split] is the number of collectors to use
76
77TMPIFile::TMPIFile(const char *name, Option_t *option, Int_t split, const char *ftitle, Int_t compress)
78 : TMemFile(name, option, ftitle, compress), fSplitLevel(split), fMPIColor(0), fMPIRequest(0), fSendBuf(0)
79{
80 // check that split is set to reasonable value
82 // split the MPI COMM_WORLD into sub-communicators
83 // one for each split level
85}
86
87////////////////////////////////////////////////////////////////////////////////
88/// TMPIFile destructor
89///
90/// This ensures the TMPIFile::Close function is called and any MPI
91/// communicators are freed.
92
94{
95 // Sub communicators should be freed
96 Int_t finalized = 0;
97 MPI_Finalized(&finalized);
98 if (!finalized && (fSplitLevel > 1)) {
99 MPI_Comm_free(&fSubComm);
100 }
101 Close();
102}
103
104////////////////////////////////////////////////////////////////////////////////
105/// As worker ranks exit, they send the collector empty messages.
106/// This counter keeps track of the number of empty messages the collector
107/// has received. Thereby the collector knows when all workers have exited
108/// and it can exit
109
114
115////////////////////////////////////////////////////////////////////////////////
116/// This is the core of the Collector rank which listens for incoming
117/// messages from Worker ranks. The Collector
118
120{
121 // update the user set filename with the current process ID and Rank ID
122 // this ensures collectors do not overwrite one anothers files
123 this->SetOutputName();
124 Info("RunCollector", "writing to filename: %s", fMPIFilename.Data());
125 THashTable mergers;
126
127 Int_t client_Id = 0;
128 std::vector<char> buffer(0);
129
130 // loop until all other ranks in the subcommunicator have exited
131 while (fEndProcess != fMPILocalSize - 1) {
132 // Info("RunCollector","process counter %i",fEndProcess);
133 // check if message has been received
134 MPI_Status status;
135 // this call blocks until a message is received
136 MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, fSubComm, &status);
137
138 // get bytes received
139 Int_t number_bytes;
140 MPI_Get_count(&status, MPI_CHAR, &number_bytes);
141 buffer.resize(number_bytes);
142 char *buf = buffer.data();
143
144 Int_t source = status.MPI_SOURCE;
145 Int_t tag = status.MPI_TAG;
146
147 // retrieve the message
148 MPI_Recv(buf, number_bytes, MPI_CHAR, source, tag, fSubComm, MPI_STATUS_IGNORE);
149
150 // empty message signifies a Worker exited
151 if (number_bytes == 0) {
152 this->UpdateEndProcess();
153 } else {
154 // create a TMemFile from the buffer
155 TMemFile *transient = new TMemFile(fMPIFilename, buf, number_bytes, "UPDATE");
156 if (transient->IsZombie()) {
157 Error("RunCollector", "Failed to create TMemFile from buffer");
158 }
159 // match compression settings of this TMPIFile object
161
162 // retrieve existing output file object
164 // if exiting file does not exist, create a new one
165 if (!info) {
167 // add file to hash table
168 mergers.Add(info);
169 }
170
171 // first merge needs extra care
172 if (info->NeedInitialMerge(transient)) {
173 info->InitialMerge(transient);
174 }
175
176 // merge the data
177 info->RegisterClient(client_Id, transient);
178 info->Merge();
179 transient = 0;
180
181 client_Id++;
182 }
183 buffer.resize(0);
184 }
185
186 if (fEndProcess == fMPILocalSize - 1) {
187 mergers.Delete();
188 return;
189 }
190}
191
192////////////////////////////////////////////////////////////////////////////////
193/// Constructor for ParallelFileMerger class
194
195TMPIFile::ParallelFileMerger::ParallelFileMerger(const char *filename, Int_t compression_settings, Bool_t writeCache)
197{
198 fMerger.SetPrintLevel(0);
199 if (!fMerger.OutputFile(filename, "RECREATE")) {
200 Error("ParallelFileMerger", "Cannot recreate the output file");
201 }
202 fMerger.GetOutputFile()->SetCompressionSettings(compression_settings);
203 if (writeCache) {
204 new TFileCacheWrite(fMerger.GetOutputFile(), 32 * 1024 * 1024);
205 }
206}
207
208////////////////////////////////////////////////////////////////////////////////
209/// Deconstructor for ParallelFileMerger class
210///
211/// taken from root/tutorials/net/parallelMergeServer.C
212
214{
215 for (auto& client : fClients)
216 delete client.GetFile();
217}
218
220{
221 if (dir == 0)
222 return;
223
224 TIter nextkey(dir->GetListOfKeys());
225 TKey *key;
226 while ((key = (TKey *)nextkey())) {
228 if (cl->InheritsFrom(TDirectory::Class())) {
229 TDirectory *subdir = (TDirectory *)dir->GetList()->FindObject(key->GetName());
230 if (!subdir) {
231 subdir = (TDirectory *)key->ReadObj();
232 }
233 DeleteObject(subdir, withReset);
234 } else {
235 Bool_t todelete = kFALSE;
236 if (withReset) {
237 todelete = (0 != cl->GetResetAfterMerge());
238 } else {
239 todelete = (0 == cl->GetResetAfterMerge());
240 }
241 if (todelete) {
242 key->Delete();
243 dir->GetListOfKeys()->Remove(key);
244 delete key;
245 }
246 }
247 }
248}
249
251{
252 if (dir == 0)
253 return kFALSE;
254 TIter nextkey(dir->GetListOfKeys());
255 TKey *key;
256 while ((key = (TKey *)nextkey())) {
258 if (cl->InheritsFrom(TDirectory::Class())) {
259 TDirectory *subdir = (TDirectory *)dir->GetList()->FindObject(key->GetName());
260 if (!subdir) {
261 subdir = (TDirectory *)key->ReadObj();
262 }
263 if (NeedInitialMerge(subdir)) {
264 return kTRUE;
265 }
266 } else {
267 if (0 != cl->GetResetAfterMerge()) {
268 return kTRUE;
269 }
270 }
271 }
272 return kFALSE;
273}
274
275////////////////////////////////////////////////////////////////////////////////
276/// Initial merge of the input to copy the resetable object (TTree) into the output
277/// and remove them from the input file.
278///
279/// taken from root/tutorials/net/parallelMergeServer.C
280
282{
283 // Initial merge of the input to copy the resetable object (TTree) into the
284 // output and remove them from the input file.
285 fMerger.AddFile(input);
286 Bool_t result =
288 DeleteObject(input, kTRUE);
289 return result;
290}
291
292////////////////////////////////////////////////////////////////////////////////
293/// Merge the current inputs into the output file.
294///
295/// taken from root/tutorials/net/parallelMergeServer.C
296
298{
299 // Remove object that can *not* be incrementally merge and will *not* be reset by the client code.
300 DeleteObject(fMerger.GetOutputFile(), kFALSE);
301 for (UInt_t f = 0; f < fClients.size(); ++f) {
302 fMerger.AddFile(fClients[f].GetFile());
303 }
305
306 // Remove any 'resetable' object (like TTree) from the input file so that they
307 // will not be re-merged. Keep only the object that always need to be
308 // re-merged (Histograms).
309 for (UInt_t f = 0; f < fClients.size(); ++f) {
310 if (fClients[f].GetFile()) {
312 } else {
313 // We back up the file (probably due to memory constraint)
314 TFile *file = TFile::Open(fClients[f].GetLocalName(), "UPDATE");
315 if (file->IsZombie()) {
316 Error("Merge", "output file unavailable");
317 }
318 // Remove object that can be incrementally merge and will be reset by the client code.
320 file->Write();
321 delete file;
322 }
323 }
326 fClientsContact.Clear();
327
328 return result;
329}
330
331////////////////////////////////////////////////////////////////////////////////
332/// Register that a client has sent a file.
333///
334/// taken from root/tutorials/net/parallelMergeServer.C
335
337{
339 fClientsContact.SetBitNumber(clientID);
340 TMPIClientInfo ntcl(std::string(fFilename).c_str(), clientID);
341 if (fClients.size() < clientID + 1) {
342 fClients.push_back(ntcl);
343 }
344 fClients[clientID].SetFile(file);
345}
346
347////////////////////////////////////////////////////////////////////////////////
348/// Return true, if enough client have reported
349///
350/// In the case of TMPIFile this happens everytime a client/worker sends
351/// the buffer (tested).
352///
353/// taken from root/tutorials/net/parallelMergeServer.C
354
356{
357
358 if (fClients.size() == 0) {
359 return kFALSE;
360 }
361
362 // Calculate average and rms of the time between the last 2 contacts.
363 Double_t sum = 0.;
364 Double_t sum2 = 0.;
365 for (UInt_t c = 0; c < fClients.size(); ++c) {
366 sum += fClients[c].GetTimeSincePrevContact();
367 sum2 += fClients[c].GetTimeSincePrevContact() * fClients[c].GetTimeSincePrevContact();
368 }
369 Double_t avg = sum / fClients.size();
370 Double_t sigma = sum2 ? TMath::Sqrt(sum2 / fClients.size() - avg * avg) : 0;
371 Double_t target = avg + 2 * sigma;
372 TTimeStamp now;
373 if ((now.AsDouble() - fLastMerge.AsDouble()) > target) {
374 return kTRUE;
375 }
376 Float_t cut = clientThreshold * fClients.size();
377 return fClientsContact.CountBits() > cut || fNClientsContact > 2 * cut;
378}
379
380
381
382////////////////////////////////////////////////////////////////////////////////
383/// return True if this is the Collector rank, otherwise False
384
389
390////////////////////////////////////////////////////////////////////////////////
391/// Called by the Workers only: Copies the current content in memory and
392/// sends it asynchronously to the Collector for merging and writing to disk.
393///
394
396{
397 if (this->IsCollector()) {
398 Error("CreateBufferAndSend", " should not be called by a collector");
399 return;
400 }
401 this->Write();
402 Int_t count = this->GetEND();
403 fSendBuf = new char[count];
404 this->CopyTo(fSendBuf, count);
405 MPI_Isend(fSendBuf, count, MPI_CHAR, 0, fMPIColor, fSubComm, &fMPIRequest);
406}
407
408////////////////////////////////////////////////////////////////////////////////
409/// For Workers: Creates an empty buffer and sends it to the Collector.
410/// This indicates the completion of the worker.
411
413{
414 if (this->IsCollector()) {
415 return;
416 }
417
418 if (!IsReceived()) {
419 MPI_Wait(&fMPIRequest, MPI_STATUS_IGNORE);
420 }
421 delete[] fSendBuf; // empty the buffer once received by master
422 fSendBuf = nullptr;
423 MPI_Send(fSendBuf, 0, MPI_CHAR, 0, fMPIColor, fSubComm);
424}
425
426////////////////////////////////////////////////////////////////////////////////
427/// Called by the Workers only: Called periodically by workers and triggers
428/// the sending of data to the Collector for writing.
429
431{
432 // check if the previous send request is accepted by master.
433 if (!IsReceived()) {
434 MPI_Wait(&fMPIRequest, MPI_STATUS_IGNORE);
435 }
436 delete[] fSendBuf; // empty the buffer once received by master
437 fSendBuf = nullptr;
440}
441
442////////////////////////////////////////////////////////////////////////////////
443/// Closes the file. For Worker ranks, this function will signal to the
444/// Collector that the Worker has exited. It also closes the inherited TMemFile.
445
447{
448 if (IsOpen()) {
449 // sends empty buffer
451 // call parent close function
452 TMemFile::Close(option);
453
454 // check to see that MPI has not already been finalized
455 Int_t finalized = 0;
456 MPI_Finalized(&finalized);
457 if (!finalized) {
458 MPI_Finalize();
459 }
460 }
461}
462
463////////////////////////////////////////////////////////////////////////////////
464/// Called by the Collector only: edits the input filename from the user
465/// to append the rank ID of the Collector so that each collector has a
466/// unique filename.
467
469{
470 std::string _filename = this->GetName();
471
472 ULong_t found = _filename.rfind(".root");
473 if (found != std::string::npos) {
474 _filename.resize(found);
475 }
476 fMPIFilename = _filename;
477 fMPIFilename += "_";
479 fMPIFilename += ".root";
480}
481
482////////////////////////////////////////////////////////////////////////////////
483/// Checks that the split level is more than one.
484/// There must be at least one Worker and one Collector rank.
485
487{
488 if (fSplitLevel < 1) {
489 Error("CheckSplitLevel", "At least one collector is required instead of %d", fSplitLevel);
490 }
491}
492
493////////////////////////////////////////////////////////////////////////////////
494/// Called by all ranks to create the sub communicators (if more than one
495/// rank).
496///
497
499{
500 // Initialize MPI if it is not already initialized...
501 Int_t flag;
502 MPI_Initialized(&flag);
503 if (!flag) {
504 MPI_Init(nullptr, nullptr);
505 }
506 // get global size and current global rank
507 MPI_Comm_size(MPI_COMM_WORLD, &fMPIGlobalSize);
508 MPI_Comm_rank(MPI_COMM_WORLD, &fMPIGlobalRank);
509
511 Error("TMPIFile",
512 "Number of Output File is larger than number of Processors Allocated."
513 " Number of processors should be two times larger than outpts. For %d outputs at least %d "
514 "should be allocated instead of %d",
516 }
517
518 // using one collector
519 if (fSplitLevel == 1) {
520 fSubComm = MPI_COMM_WORLD;
521 }
522 // using more than one collector
523 else {
524 // number of ranks per sub-communicator
525 Int_t comm_size = fMPIGlobalSize / fSplitLevel;
526 if (fMPIGlobalSize % fSplitLevel != 0) {
527 comm_size++;
528 }
529 fMPIColor = fMPIGlobalRank / comm_size;
530 // split the COMM_WORLD communicator by color
531 MPI_Comm_split(MPI_COMM_WORLD, fMPIColor, fMPIGlobalRank, &fSubComm);
532 }
533 // get the sub-communicator size and rank
534 MPI_Comm_size(fSubComm, &fMPILocalSize);
535 MPI_Comm_rank(fSubComm, &fMPILocalRank);
536}
537
538////////////////////////////////////////////////////////////////////////////////
539/// Checks the member MPI_REQEUST object to see if a message has been received.
540
542{
543 if (!fMPIRequest) {
544 return kTRUE;
545 }
546 Int_t flag = 0;
547 MPI_Test(&fMPIRequest, &flag, MPI_STATUS_IGNORE);
548 return flag;
549}
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
int Int_t
Signed integer 4 bytes (int).
Definition RtypesCore.h:59
unsigned int UInt_t
Unsigned integer 4 bytes (unsigned int).
Definition RtypesCore.h:60
unsigned long ULong_t
Unsigned long integer 4 bytes (unsigned long). Size depends on architecture.
Definition RtypesCore.h:69
bool Bool_t
Boolean (0=false, 1=true) (bool).
Definition RtypesCore.h:77
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
double Double_t
Double 8 bytes.
Definition RtypesCore.h:73
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:83
float Float_t
Float 4 bytes (float).
Definition RtypesCore.h:71
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
const char Option_t
Option string (const char).
Definition RtypesCore.h:80
Error("WriteTObject","The current directory (%s) is not associated with a file. The object (%s) has not been written.", GetName(), objname)
if(name) objname
void Info(const char *location, const char *msgfmt,...)
Use this function for informational messages.
Definition TError.cxx:241
char name[80]
Definition TGX11.cxx:148
const Int_t MIN_FILE_NUM
Definition TMPIFile.cxx:49
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2994
TFile * GetFile() const override
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
virtual void Close(Option_t *option="")
Delete all objects from memory and directory structure itself.
A cache when writing files over the network.
A class to pass information from the TFileMerger to the objects being merged.
@ kIncremental
Merge the input file with the content of the output file (if already existing).
Definition TFileMerger.h:82
@ kKeepCompression
Keep compression level unchanged for each input files.
Definition TFileMerger.h:92
@ kResetable
Only the objects with a MergeAfterReset member function.
Definition TFileMerger.h:83
@ kAllIncremental
Merge incrementally all type of objects.
Definition TFileMerger.h:88
Int_t GetCompressionSettings() const
Definition TFile.h:489
virtual Bool_t IsOpen() const
Returns kTRUE in case file is open and kFALSE if file is not open.
Definition TFile.cxx:1482
virtual void SetCompressionSettings(Int_t settings=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault)
Used to specify the compression level and algorithm.
Definition TFile.cxx:2391
TFile(const TFile &)=delete
virtual Long64_t GetEND() const
Definition TFile.h:319
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
Definition TFile.cxx:3788
THashTable implements a hash table to store TObject's.
Definition THashTable.h:35
void Add(TObject *obj) override
Add object to the hash table.
TObject * FindObject(const char *name) const override
Find object using its name.
void Delete(Option_t *option="") override
Remove all objects from the table AND delete all heap based objects.
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
void Delete(Option_t *option="") override
Delete an object from the file.
Definition TKey.cxx:572
virtual const char * GetClassName() const
Definition TKey.h:77
virtual TObject * ReadObj()
To read a TObject* from the file.
Definition TKey.cxx:792
void CheckSplitLevel()
Checks that the split level is more than one.
Definition TMPIFile.cxx:486
Int_t fSplitLevel
Definition TMPIFile.h:44
char * fSendBuf
Definition TMPIFile.h:57
void CreateEmptyBufferAndSend()
For Workers: Creates an empty buffer and sends it to the Collector.
Definition TMPIFile.cxx:412
~TMPIFile() override
TMPIFile destructor.
Definition TMPIFile.cxx:93
Int_t fMPIGlobalRank
Definition TMPIFile.h:47
void CreateBufferAndSend()
Called by the Workers only: Copies the current content in memory and sends it asynchronously to the C...
Definition TMPIFile.cxx:395
Int_t fMPILocalSize
Definition TMPIFile.h:50
Bool_t IsReceived()
Checks the member MPI_REQEUST object to see if a message has been received.
Definition TMPIFile.cxx:541
void UpdateEndProcess()
As worker ranks exit, they send the collector empty messages.
Definition TMPIFile.cxx:110
Bool_t IsCollector()
return True if this is the Collector rank, otherwise False
Definition TMPIFile.cxx:385
Int_t fMPIColor
Definition TMPIFile.h:45
TString fMPIFilename
Definition TMPIFile.h:55
void SetOutputName()
Called by the Collector only: edits the input filename from the user to append the rank ID of the Col...
Definition TMPIFile.cxx:468
void RunCollector(Bool_t cache=kFALSE)
This is the core of the Collector rank which listens for incoming messages from Worker ranks.
Definition TMPIFile.cxx:119
Int_t fMPILocalRank
Definition TMPIFile.h:49
Int_t fMPIGlobalSize
Definition TMPIFile.h:48
void Close(Option_t *option="") final
Closes the file.
Definition TMPIFile.cxx:446
Int_t fEndProcess
Definition TMPIFile.h:43
TMPIFile(const char *name, char *buffer, Long64_t size=0, Option_t *option="", Int_t split=1, const char *ftitle="", Int_t compress=4)
TMPIFile constructor.
Definition TMPIFile.cxx:58
void Sync()
Called by the Workers only: Called periodically by workers and triggers the sending of data to the Co...
Definition TMPIFile.cxx:430
MPI_Comm fSubComm
Definition TMPIFile.h:52
void SplitMPIComm()
Called by all ranks to create the sub communicators (if more than one rank).
Definition TMPIFile.cxx:498
MPI_Request fMPIRequest
Definition TMPIFile.h:53
TMemFile(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Long64_t defBlockSize=0LL)
Usual Constructor.
Definition TMemFile.cxx:161
virtual Long64_t CopyTo(void *to, Long64_t maxsize) const
Copy the binary representation of the TMemFile into the memory area starting at 'to' and of length at...
Definition TMemFile.cxx:254
void ResetAfterMerge(TFileMergeInfo *) override
Wipe all the data from the permanent buffer but keep, the in-memory object alive.
Definition TMemFile.cxx:339
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Bool_t IsZombie() const
Definition TObject.h:161
The TTimeStamp encapsulates seconds and ns since EPOCH.
Definition TTimeStamp.h:45
Double_t AsDouble() const
Definition TTimeStamp.h:112
auto filename
void file()
Definition file.C:11
Double_t result(Double_t *x, Double_t *par)
Definition gr201_waves.C:37
const Double_t sigma
Definition h1analysis.C:161
s0 Close()
Double_t Sqrt(Double_t x)
Returns the square root of x.
Definition TMath.h:673
void Write()
void dir(char *path=0)
Definition rootalias.C:42
Bool_t NeedMerge(Float_t clientThreshold)
Return true, if enough client have reported.
Definition TMPIFile.cxx:355
void RegisterClient(UInt_t clientID, TFile *file)
Register that a client has sent a file.
Definition TMPIFile.cxx:336
Bool_t Merge()
Merge the current inputs into the output file.
Definition TMPIFile.cxx:297
Bool_t InitialMerge(TFile *input)
Initial merge of the input to copy the resetable object (TTree) into the output and remove them from ...
Definition TMPIFile.cxx:281
~ParallelFileMerger() override
Deconstructor for ParallelFileMerger class.
Definition TMPIFile.cxx:213
ParallelFileMerger(const char *filename, Int_t compression_settings, Bool_t writeCache=kFALSE)
Constructor for ParallelFileMerger class.
Definition TMPIFile.cxx:195
static void DeleteObject(TDirectory *dir, Bool_t withReset)
Definition TMPIFile.cxx:219
static Bool_t NeedInitialMerge(TDirectory *dir)
Definition TMPIFile.cxx:250
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338