1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#include "TFile.h"
#include "TTree.h"
#include "TBranch.h"
#include "TClass.h"
#include "TThread.h"
#include "TVirtualStreamerInfo.h"
#include "TList.h"
#include "TMap.h"
#include "TObjString.h"
#include "TH1F.h"
#include "FWCore/FWLite/interface/FWLiteEnabler.h"
#include <memory>
#include <cassert>
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<bool> waitToStart{true};
void printHelp(const char* iName, int iDefaultNThreads) {
std::cout << iName << " [number of threads] [filename] [gDebug value]\n\n"
<< "[number of threads] number of threads to use in test\n"
<< "[filename] name of CMSSW file to read\n"
<< "[gDebug value] value of gDebug to pass to ROOT (gDebug=1 is useful)\n"
<< "If no arguments are given " << iDefaultNThreads
<< " threads will be used and a dummy file will be created." << std::endl;
}
const std::string kDefaultFileName("read_test_dummy.root");
std::tuple<int, std::string, int> parseOptions(int argc, char** argv) {
constexpr int kDefaultNThreads = 4;
int kDefaultgDebug = gDebug;
int nThreads = kDefaultNThreads;
int newGDebug = kDefaultgDebug;
std::string fileName(kDefaultFileName);
if (argc >= 2) {
if (strcmp("-h", argv[1]) == 0) {
printHelp(argv[0], kDefaultNThreads);
exit(0);
}
nThreads = atoi(argv[1]);
}
if (argc >= 3) {
fileName = argv[2];
}
if (argc == 4) {
newGDebug = atoi(argv[3]);
}
if (argc > 4) {
printHelp(argv[0], kDefaultNThreads);
exit(1);
}
return std::make_tuple(nThreads, fileName, newGDebug);
}
void createDummyFile() {
auto theList = new TList();
for (unsigned int i = 0; i < 10; ++i) {
theList->Add(new TList());
theList->Add(new TMap());
theList->Add(new TObjString());
theList->Add(new TH1F());
theList->Add(new TH1D());
}
TFile f(kDefaultFileName.c_str(), "RECREATE", "test");
auto listTree = new TTree("Events", "TheList");
listTree->Branch("theList", "TList", &theList);
for (unsigned int i = 0; i < 100; ++i) {
listTree->Fill();
}
f.Write();
f.Close();
}
int main(int argc, char** argv) {
auto options = parseOptions(argc, argv);
const int kNThreads = std::get<0>(options);
auto const kFileName = std::get<1>(options);
gDebug = std::get<2>(options);
FWLiteEnabler::enable();
//Tell Root we want to be multi-threaded
TThread::Initialize();
//When threading, also have to keep ROOT from logging all TObjects into a list
TObject::SetObjectStat(false);
//Have to avoid having Streamers modify themselves after they have been used
TVirtualStreamerInfo::Optimize(false);
if (kFileName == kDefaultFileName) {
createDummyFile();
}
std::vector<std::shared_ptr<std::thread>> threads;
threads.reserve(kNThreads);
for (int i = 0; i < kNThreads; ++i) {
threads.push_back(std::make_shared<std::thread>(std::thread([&kFileName]() {
static thread_local TThread s_thread_guard;
while (waitToStart)
;
std::unique_ptr<TFile> f{TFile::Open(kFileName.c_str())};
assert(f.get());
TTree* eventTree = dynamic_cast<TTree*>(f.get()->Get("Events"));
assert(eventTree);
for (Long64_t i = 0, iEnd = eventTree->GetEntries(); i != iEnd; ++i) {
eventTree->GetEntry(i, 1);
}
f.get()->Close();
})));
}
waitToStart = false;
for (auto& t : threads) {
t->join();
}
return 0;
}
|