Belle II Software development
benchmark.py
1#!/usr/bin/env python3
2
3
10
11
15
16import basf2 as b2
17from generators import add_kkmc_generator
18from simulation import add_simulation
19from validation_gt import get_validation_globaltags
20from rawdata import add_unpackers
21from reconstruction import add_reconstruction
22from argparse import ArgumentParser
23import os
24import glob
25import sys
26
27# parse command line options
28parser = ArgumentParser(description="Measure the execution time.")
29parser.add_argument(
30 "-m",
31 "--multiplicity",
32 default="high",
33 choices=["high", "low", "data"],
34 help="Multiplicity or type of events",
35)
36parser.add_argument("-l", "--limits", help="Name of file containing limits")
37parser.add_argument("-f", "--file", help="Name of benchmark output file")
38parser.add_argument("-c", "--csv", help="Name of statistics csv file")
39args = parser.parse_args()
40
41# create path and reduce log level
42main = b2.create_path()
43b2.set_log_level(b2.LogLevel.ERROR)
44
45if args.multiplicity == "data":
46 # Global Tag needed as these are Raw data
47 b2.conditions.override_globaltags(get_validation_globaltags())
48
49 input_files = glob.glob(
50 os.environ.get("BELLE2_VALIDATION_DATA_DIR", "")
51 + "/rawdata/physics.0010.05095*.root"
52 )
53 main.add_module("RootInput", inputFileNames=input_files)
54
55 main.add_module("EventInfoPrinter").set_log_level(b2.LogLevel.INFO)
56
57 # gearbox and geometry
58 main.add_module("Gearbox")
59 main.add_module("Geometry", useDB=True)
60
61 # unpacking
62 add_unpackers(main)
63
64else:
65 # specify number of events to be generated
66 main.add_module("EventInfoSetter", evtNumList=[1000])
67 main.add_module("EventInfoPrinter").set_log_level(b2.LogLevel.INFO)
68
69 if args.multiplicity == "high":
70 # generate BBbar events if high multiplicity is selected
71 main.add_module("EvtGenInput")
72
73 elif args.multiplicity == "low":
74 # generate mu pair events if low multiplicity is selected
75 add_kkmc_generator(main, "mu+mu-")
76
77 # detector and L1 trigger simulation
78 add_simulation(
79 main,
80 bkgfiles=glob.glob(
81 os.environ.get("BELLE2_BACKGROUND_DIR", "/sw/belle2/bkg") + "/*.root"
82 ),
83 )
84
85# reconstruction
86add_reconstruction(main)
87
88# process events, print call statistics, and write statistics to a csv file
89b2.process(main, calculateStatistics=True)
90print(b2.statistics)
91if args.csv is not None:
92 b2.statistics.csv(args.csv)
93
94# read limits
95limits = {}
96limits_file = args.limits
97default_limits_file = args.multiplicity + ".limits"
98if limits_file is None and os.path.isfile(default_limits_file):
99 limits_file = default_limits_file
100if limits_file is not None:
101 for line in open(limits_file).readlines():
102 entries = line.split()
103 limits[entries[0]] = float(entries[1])
104 if len(entries) > 2:
105 limits[entries[0]] /= float(entries[2])
106
107# get execution times
108categories = []
109times = {}
110for module in b2.statistics.modules:
111 if "Sum_" not in module.name:
112 continue
113 category = module.name[4:]
114 categories.append(category)
115 if category not in times.keys():
116 times[category] = 0
117 times[category] += module.time_mean(b2.statistics.EVENT) * 1e-6
118
119# open output file
120output = None
121if args.file is not None:
122 output = open(args.file, "w")
123
124# print benchmark results and write them to the output file
125b2.set_log_level(b2.LogLevel.INFO)
126max_fraction = -1
127for category in categories:
128 if category not in times.keys():
129 continue
130 time = times[category]
131 message = f"Execution time per event for {category} is {time:.0f} ms"
132 fraction = -1
133 if category in limits.keys():
134 fraction = time / limits[category]
135 if fraction > max_fraction:
136 max_fraction = fraction
137 message += f" = {fraction:.0%} of the limit."
138 if fraction <= 0.9:
139 b2.B2INFO(message)
140 elif fraction <= 1:
141 b2.B2WARNING(message)
142 else:
143 b2.B2ERROR(message)
144 else:
145 b2.B2INFO(message)
146
147 if output is not None:
148 output.write(f"{category} {time:.2f}")
149 if fraction >= 0:
150 output.write(f" {fraction:.4f}")
151 output.write("\n")
152
153if output is not None:
154 output.close()
155
156# fail if above limit
157sys.exit(0 if max_fraction <= 1 else 1)