23 std::string input =
"lha/si_input.flha";
24 std::string output =
"benchmark_bkstarll_threads.csv";
27 unsigned int max_threads = 0;
29 std::vector<std::pair<double, double>>
bins {{17.0, 19.0}};
30 bool include_unbinned =
false;
33QCDOrder parse_order(std::string value) {
34 std::transform(value.begin(), value.end(), value.begin(), [](
unsigned char c) { return static_cast<char>(std::toupper(c)); });
39 LOG_ERROR(
"ArgumentError",
"Unknown QCD order:", value,
"expected LO, NLO, NNLO or NONE.");
43void print_usage(
const char* exe) {
45 <<
"Usage: " << exe <<
" [options]\n"
47 <<
" --input PATH Input FLHA/LHA file (default: default/lha/testInput.flha)\n"
48 <<
" --out PATH Output CSV file (default: benchmark_bkstarll_threads.csv)\n"
49 <<
" --repeats N Timed repetitions per thread count (default: 30)\n"
50 <<
" --warmups N Untimed warm-up calls per thread count (default: 3)\n"
51 <<
" --max-threads N Maximum tested thread count (default: hardware_concurrency)\n"
52 <<
" --order ORDER QCD order: LO, NLO, NNLO or NONE (default: NLO)\n"
53 <<
" --bin QMIN:QMAX Add a q^2 bin for binned observables; can be repeated (default: 1:6)\n"
54 <<
" --include-unbinned Also benchmark non-binned BKstarll observables such as q0(A_FB)\n"
55 <<
" --help Show this message\n";
58std::pair<double, double> parse_bin(
const std::string& text) {
59 const auto pos = text.find(
':');
60 if (pos == std::string::npos) {
61 LOG_ERROR(
"ArgumentError",
"Invalid --bin value:", text,
"expected QMIN:QMAX.");
63 double qmin = std::stod(text.substr(0, pos));
64 double qmax = std::stod(text.substr(pos + 1));
66 LOG_ERROR(
"ArgumentError",
"Invalid --bin value:", text,
"expected QMIN < QMAX.");
71std::string format_bins(
const std::vector<std::pair<double, double>>& bins) {
73 for (
size_t i = 0; i <
bins.size(); ++i) {
74 if (i != 0) out +=
";";
75 out += std::to_string(bins[i].first) +
":" + std::to_string(bins[i].second);
80Options parse_args(
int argc,
char** argv) {
82 for (
int i = 1; i < argc; ++i) {
83 const std::string
arg = argv[i];
84 auto require_value = [&](
const std::string&
name) -> std::string {
86 LOG_ERROR(
"ArgumentError",
"Missing value after", name);
91 if (arg ==
"--input") opt.input = require_value(arg);
92 else if (arg ==
"--out") opt.output = require_value(arg);
93 else if (arg ==
"--repeats") opt.repeats = std::stoi(require_value(arg));
94 else if (arg ==
"--warmups") opt.warmups = std::stoi(require_value(arg));
95 else if (arg ==
"--max-threads") opt.max_threads =
static_cast<unsigned int>(std::stoul(require_value(arg)));
96 else if (arg ==
"--order") opt.order = parse_order(require_value(arg));
97 else if (arg ==
"--include-unbinned") opt.include_unbinned =
true;
98 else if (arg ==
"--bin") {
99 if (opt.bins.size() == 1 && opt.bins.front() == std::pair<double, double>{1.0, 6.0}) {
102 opt.bins.push_back(parse_bin(require_value(arg)));
104 else if (arg ==
"--help" || arg ==
"-h") {
105 print_usage(argv[0]);
108 LOG_ERROR(
"ArgumentError",
"Unknown option:", arg);
112 if (opt.repeats <= 0)
LOG_ERROR(
"ArgumentError",
"--repeats must be positive.");
113 if (opt.warmups < 0)
LOG_ERROR(
"ArgumentError",
"--warmups must be non-negative.");
114 if (opt.bins.empty())
LOG_ERROR(
"ArgumentError",
"At least one --bin is required.");
116 if (opt.max_threads == 0) {
117 opt.max_threads = std::thread::hardware_concurrency();
118 if (opt.max_threads == 0) opt.max_threads = 1;
123double checksum(
const std::map<
ObservableId, std::vector<ObservableValue>>& results) {
125 for (
const auto& [_, values] :
results) {
126 for (
const auto& value : values) {
136 const std::vector<std::pair<double, double>>& bins,
137 bool include_unbinned)
140 if (observables.empty()) {
144 bool added_any =
false;
145 for (
const auto& obs : observables) {
147 for (
const auto& bin :
bins) {
151 }
else if (include_unbinned) {
159 "has no selected observable after applying benchmark filters.");
164 double mean_ms = 0.0;
165 double stddev_ms = 0.0;
172 stats.mean_ms = std::accumulate(samples.begin(), samples.end(), 0.0) /
static_cast<double>(samples.size());
173 stats.min_ms = *std::min_element(samples.begin(), samples.end());
174 stats.max_ms = *std::max_element(samples.begin(), samples.end());
176 double variance = 0.0;
177 for (
double x : samples) {
178 variance += (x - stats.mean_ms) * (x - stats.mean_ms);
180 variance /=
static_cast<double>(samples.size());
181 stats.stddev_ms = std::sqrt(variance);
187int main(
int argc,
char** argv) {
189 const Options opt = parse_args(argc, argv);
194 hyperiso.
init(opt.input, config);
196 std::ofstream csv(opt.output);
198 LOG_ERROR(
"IOError",
"Cannot open output CSV:", opt.output);
201 csv <<
"threads,repeats,warmups,order,bins,include_unbinned,n_observables,mean_ms,stddev_ms,min_ms,max_ms,checksum\n";
203 add_decay_observables_with_bins(interface,
Decays::B__K_l_l, opt.order, opt.bins, opt.include_unbinned);
205 for (
unsigned int threads = 1; threads <= opt.max_threads; ++threads) {
208 for (
int i = 0; i < opt.warmups; ++i) {
209 std::cout <<
"warmup : " << i << std::endl;
210 volatile double sink = checksum(interface.
compute_all());
214 std::vector<double> samples_ms;
215 samples_ms.reserve(
static_cast<size_t>(opt.repeats));
216 double last_checksum = 0.0;
217 size_t n_observables = 0;
219 for (
int i = 0; i < opt.repeats; ++i) {
220 std::cout <<
"repeat : " << i << std::endl;
221 const auto start = std::chrono::steady_clock::now();
223 const auto stop = std::chrono::steady_clock::now();
225 n_observables = results.size();
226 last_checksum = checksum(results);
227 samples_ms.push_back(std::chrono::duration<double, std::milli>(stop - start).count());
230 const Stats stats = compute_stats(samples_ms);
231 csv << threads <<
','
232 << opt.repeats <<
','
233 << opt.warmups <<
','
235 << format_bins(opt.bins) <<
','
236 << (opt.include_unbinned ? 1 : 0) <<
','
237 << n_observables <<
','
238 << stats.mean_ms <<
','
239 << stats.stddev_ms <<
','
240 << stats.min_ms <<
','
241 << stats.max_ms <<
','
242 << last_checksum <<
'\n';
245 std::cout <<
"threads=" << threads
246 <<
" mean_ms=" << stats.mean_ms
247 <<
" stddev_ms=" << stats.stddev_ms
248 <<
" n_observables=" << n_observables <<
'\n';
251 std::cout <<
"Wrote " << opt.output <<
'\n';
High-level helpers for initializing and monitoring the Hyperiso framework.
#define LOG_ERROR(type,...)
Macro for logging error messages and terminating the application.
High-level, user-facing entry point to compute flavor observables.
static std::vector< Observables > get_observables(Decays d)
Legacy static lookup of builtin observables for a builtin decay.
static std::string str(const IdOf< QCDOrderTag > &id)
Returns the string representation associated with an identifier.
static std::string str(const IdOf< DecayTag > &id)
Returns the string representation of an identifier.
High-level interface to initialize and monitor the main framework configuration.
void init(const std::string &lhaFile, HyperisoConfig config)
Initializes Hyperiso using a LHA file and a full Config object.
void setLevel(LogLevel level)
Sets the logging level.
static Logger * getInstance()
Retrieves the singleton instance of the Logger.
bool is_observable_binned(Observables obs) const
Return whether a specific observable requires q² bins.
void set_bkll_threads(size_t n_threads)
Set the thread option for the bkll decay.
std::map< ObservableId, std::vector< ObservableValue > > compute_all()
Compute all currently registered observables.
ObservableInterface & add_observable(Observables obs, QCDOrder order, bool add_dependencies=false)
Add an observable to the manager (enum API).
Dict[str, float] compute_stats(np.ndarray Y, np.ndarray R)
Identifies an observable together with a numerical bin.
Configuration object controlling model, input flags and optional MARTY resources.
Model model
Current model.