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