// @(#)root/test:$Id$ // Author: Nikolay Root 05/07/98 #include <stdlib.h> #include "Riostream.h" #include "TCollection.h" #include "TSortedList.h" #include "TObjArray.h" #include "TClonesArray.h" #include "TOrdCollection.h" #include "THashList.h" #include "THashTable.h" #include "TBtree.h" #include "TStopwatch.h" #include "TRandom.h" // // This program benchmarks access time to objects by name or index // for TObjArray,TOrdCollection,TList,TSortedList,THashList,TBtree, // TClonesArray and THashTable collections. // // Usage: tcollbm -h - to print a usage info // tcollbm [-n|-i|-m] [nobjects] [ntimes] - to run tests // // switches: // -n - benchmark access by name (default) // -i - benchmark access by index // -m - benchmark of objects allocation // // parameters: // nobjects - number of objects to be inserted into collections // ntimes - number of random lookups in the collection // // default values ( adjusted in 'main' according 'moda' value ) int nobjects = 100; // Number of objects to be inserted. int ntimes = 100000; // Number of random lookups. int moda = 0; // Default - access by name //_____________________________________________________________ class HitNames { // Utility class to handle the table of names Int_t fSize; // Array size char** fNames; // Array for objects names public: void InitNames(); void DeleteNames(); void Resize(Int_t s) { if(s <= fSize) return; DeleteNames(); fSize=s; InitNames(); } HitNames() : fSize(0), fNames(0) {}; HitNames(Int_t s) : fSize(s), fNames(0) { InitNames(); }; virtual ~HitNames() { DeleteNames(); }; const char *operator[](Int_t i) { return fNames[i]; }; }; void HitNames::InitNames() { // Create and fill names // cout << "HitNames::InitNames() are called" << endl; fNames = new char* [fSize]; for (int i=0;i<fSize;i++) { fNames[i] = new char [20]; snprintf(fNames[i],20,"W%d",i+1); } } void HitNames::DeleteNames() { // Clean up if(!fNames) return; // cout << "HitNames::DeleteNames() are called" << endl; for (int i=0;i<fSize;i++) { delete fNames[i]; } delete fNames; fNames = 0; } HitNames names; // We needs only one static object of this class //_______________________________________________________________ // // Just for fun, I introduce the class 'Tester' to collect // a 'TNamed' objects. I use a objects of this class to perform a real // tests. class Tester : public TObject { enum EClass { Clones, Array, BTree, Other }; EClass fWhat; Int_t fNobj; // Number of objects to be stored in collection Int_t fNtimes; // How many times to perform a test Int_t fModa; // What we need to test TCollection *fColl; // Collection under test public: void Fill(); // Fill the collection Double_t TestAllocation(); // Memory allocation test Double_t TestByName(); // benchmark by name Double_t TestByIndex(); // benchmark by index Double_t DoTest(); // Tests multiplexsor void CleanUp() { fColl->Delete(); } void Dump() const { fColl->Dump(); } virtual const char* GetName() const { return fColl->ClassName(); } Tester() : fWhat(Other),fNobj(10),fNtimes(100000),fModa(0),fColl(0) {} Tester(Int_t no,Int_t nt,Int_t m,TCollection *p) : fWhat(Other),fNobj(no),fNtimes(nt),fModa(m),fColl(p) { if(!strcmp(GetName(),"TClonesArray")) fWhat = Clones; if(!strcmp(GetName(),"TObjArray")) fWhat = Array; if(!strcmp(GetName(),"TBtree")) fWhat = BTree; } virtual ~Tester() { if(fColl) delete fColl; } }; void Tester::Fill() { if (fWhat == Clones) { TClonesArray &base = *((TClonesArray*)fColl); for (Int_t i=0;i<fNobj;i++) new (base[i]) TNamed(names[i],GetName()); } else { for (Int_t i=0;i<fNobj;i++) fColl->Add(new TNamed(names[i],GetName())); } } Double_t Tester::TestAllocation() { // benchmark memory allocation TStopwatch timer; timer.Start(); for (Int_t i=0;i<fNtimes;i++) { Fill(); CleanUp(); } timer.Stop(); return timer.CpuTime(); } Double_t Tester::TestByIndex() { // benchmark access by index Fill(); TStopwatch timer; Int_t i; if (fWhat == Clones) { TClonesArray *o = (TClonesArray*)fColl; timer.Start(); for (Int_t j=0;j<fNtimes;j++) { i=Int_t(fNobj*gRandom->Rndm(1)); if(!((*o)[i])) Printf("Object %d not found !!!",i); } timer.Stop(); } else if(fWhat == Array) { TObjArray *o = (TObjArray*)fColl; timer.Start(); for (Int_t j=0;j<fNtimes;j++) { i=Int_t(fNobj*gRandom->Rndm(1)); if(!((*o)[i])) Printf("Object %d not found !!!",i); } timer.Stop(); } else if(fWhat == BTree) { TBtree *o = (TBtree*)fColl; timer.Start(); for (Int_t j=0;j<fNtimes;j++) { i=Int_t(fNobj*gRandom->Rndm(1)); if(!((*o)[i])) Printf("Object %d not found !!!",i); } timer.Stop(); } else { cout << "Class " << GetName() << " doesn't support access by index" << endl; } CleanUp(); return timer.CpuTime(); }; Double_t Tester::TestByName() { // benchmark access by name Fill(); TStopwatch timer; Int_t i; timer.Start(); for (Int_t j=0;j<fNtimes;j++) { i=Int_t(fNobj*gRandom->Rndm(1)); if(!((*fColl)(names[i]))) Printf("Object %5s not found !!!",names[i]); } timer.Stop(); CleanUp(); return timer.CpuTime(); }; Double_t Tester::DoTest() { // Return the average time in msec. Double_t v; if(fModa==2) { printf("Memory allocation test for %-20s", GetName()); v=TestAllocation(); } else if(fModa==1) { printf("Random lookups by Index for %-20s", GetName()); v=TestByIndex(); } else { printf("Random lookups by Name for %-20s", GetName()); v=TestByName(); } Printf(" CpuTime=%7.2f seconds", v); return 1000*v/fNtimes; }; //_______________________________________________________________ const int ntests = 8; // Number of classes to be tested Double_t deltas[ntests]; // benchmark results int main(int argc,char **argv) { // Initialize the ROOT framework if(argc == 2 && !strcmp(argv[1],"-h")) { Printf("Usage: tcollbm [-n|-i] [nobjects] [ntimes]"); Printf(" -n - benchmark access by name"); Printf(" -i - benchmark access by index"); Printf(" -m - benchmark memory allocation"); Printf(" nobjects - number of objects to be inserted into collections"); Printf(" ntimes - number of random lookups in the collection"); return 1; } if(argc > 1 && !strcmp(argv[1],"-n")) { moda = 0; argc--; argv++; }; if(argc > 1 && !strcmp(argv[1],"-i")) { moda = 1; argc--; argv++; }; if(argc > 1 && !strcmp(argv[1],"-m")) { moda = 2; argc--; argv++; }; // // Set defaults values for selected test // if(moda == 0) { nobjects = 100; ntimes = 10000; } if(moda == 1) { nobjects = 100; ntimes = 1000000; } if(moda == 2) { nobjects = 1000; ntimes = 100; } Int_t no = nobjects; Int_t nt = ntimes; if(argc > 1) { no = atoi(argv[1]); } if(argc > 2) { nt = atoi(argv[2]); } if(no > 1) { nobjects = no; } else { nobjects = 2; Printf("Reset nobjects to %d",nobjects); } if(nt > 99) { ntimes = nt; } else { ntimes = 100; Printf("Reset ntimes to %d",ntimes); } Printf("Nobjects = %d , Ntimes = %d",nobjects,ntimes); if(ntimes > 5000000 || (moda==2 && nobjects*ntimes > 500000)) { Printf("This test takes some time. Be partient ..."); } names.Resize(nobjects); Double_t smin = 10000000; int j=-1,idx=-1; TObjArray array(ntests); // Array of collections array.Add(new Tester(nobjects,ntimes,moda, new TObjArray())); // Add TObjArray j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } array.Add(new Tester(nobjects,ntimes,moda, new TClonesArray("TNamed",nobjects))); // Add TClonesArray j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } array.Add(new Tester(nobjects,ntimes,moda, new TBtree())); // Add TBTree j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } if(moda != 1) { // Skip classes without operator[] from index test array.Add(new Tester(nobjects,ntimes,moda, new TOrdCollection())); // Add TOrdCollection j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } array.Add(new Tester(nobjects,ntimes,moda, new TList())); // Add TList j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } array.Add(new Tester(nobjects,ntimes,moda, new TSortedList())); // Add TSortedList j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } array.Add(new Tester(nobjects,ntimes,moda, new THashList())); // Add THashList j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } array.Add(new Tester(nobjects,ntimes,moda, new THashTable())); // Add THashTable j++; deltas[j] = ((Tester*)(array[j]))->DoTest(); if(deltas[j] < smin) { idx=j; smin=deltas[j]; } } // Results info ... j++; if(idx<0) { Printf("Can not find the winner. Sorry...\n"); array.Delete(); return 1; } if(smin==0) { Printf( "Input parameters nobjects=%d and ntimes=%d too small for Your computer.", nobjects,ntimes); Printf("Please, increase its and try again..."); array.Delete(); return 1; } Printf("\n\t\tBenchmark results\n"); Printf("\tAbsolute winner - %s",array[idx]->GetName()); Printf("\tAverage (msec)\tRatio\t\tClassName"); for (int i=0;i<j;i++) { Printf("\t%f\t%f\t%s", deltas[i],deltas[i]/smin,array[i]->GetName()); } array.Delete(); return 0; }