/// @file party/party_bench.cpp /// @brief Time specific (2+1) flows. Examples: /// party_bench --list /// party_bench --tag beaver,bench --repeat 20 --warmup 2 /// party_bench --case beaver_dot_n32 --repeat 50 --warmup 5 /// party_bench --tag iknp --repeat 1 --warmup 0 #include "cases.hpp" #include "registry.hpp" #include "spawn.hpp" #include #include #include #include #include int main(int argc, char ** argv) { dpf::party::register_all_flows(); std::string tag; std::string case_name; std::uint64_t repeat = 10; std::uint64_t warmup = 2; bool list_only = false; bool metrics = true; for (int i = 1; i < argc; ++i) { std::string a = argv[i]; auto need = [&](const char * f) { if (i + 1 >= argc) { std::cerr << "missing value for " << f << "\n"; std::exit(2); } return std::string(argv[++i]); }; if (a == "--list") list_only = true; else if (a == "--tag") tag = need("--tag"); else if (a == "--case") case_name = need("--case"); else if (a == "--repeat") repeat = std::stoull(need("--repeat")); else if (a == "--warmup") warmup = std::stoull(need("--warmup")); else if (a == "--no-metrics") metrics = false; else if (a == "--help" || a == "-h") { std::cout << "party_bench --list | --case NAME | --tag TAGS\n" << " [--repeat N=10] [--warmup W=2] [--no-metrics]\n" << "With metrics, each role also reports bytes_sent/recv and\n" << "frames_sent/recv for the flow itself (not the repeat barrier).\n" << "Tags are AND-matched (comma/space). Common tags:\n" << " beaver product dot scale stream horner poly mux\n" << " dpf wildcard geneval ds verifiable dcf grotto extreme bench smoke\n" << " arith garble yao flute shuffle (word, stack, table, hidden column)\n" << " iknp (two-party; spawned as p0/p1 only)\n"; return 0; } } if (list_only) { dpf::party::list_flows(std::cout, tag.empty() ? case_name : tag); return 0; } std::vector flows; if (!case_name.empty()) { auto * f = dpf::party::find_flow(case_name); if (!f) { std::cerr << "unknown flow: " << case_name << "\n"; return 2; } flows.push_back(f); } else { if (tag.empty()) tag = "bench"; flows = dpf::party::select_flows(tag); if (flows.empty()) { std::cerr << "no flows match tag: " << tag << "\n"; return 2; } } dpf::party::spawn_opts opts; opts.repeat = repeat; opts.warmup = warmup; opts.metrics = metrics; auto pair_flow = [](const dpf::party::flow * f) { return dpf::party::flow_is_pair(*f); }; auto flow_of = [](const std::string & line) { const auto p = line.find("flow="); if (p == std::string::npos) return std::string{}; const auto e = line.find(' ', p + 5); return line.substr(p + 5, e == std::string::npos ? std::string::npos : e - (p + 5)); }; auto avg_ns_of = [](const std::string & line) { const auto p = line.find("avg_ns="); if (p == std::string::npos) return 0.0; return std::stod(line.substr(p + 7)); }; std::cout << "flow\twall_ms\tp0\tp1\tp2\trepeat\twarmup\n"; int fails = 0; bool mixed = false; const bool first_pair = pair_flow(flows.front()); for (const auto * f : flows) mixed = mixed || (pair_flow(f) != first_pair); // One mesh for a homogeneous tag. wall_ms on each row is that flow's // slowest role. The process startup is paid once, as suite_wall_ms. if (case_name.empty() && !mixed && flows.size() > 1) { opts.by_tag = true; auto r = first_pair ? dpf::party::spawn_pair_flow(tag, opts) : dpf::party::spawn_trio_flow(tag, opts); for (const auto * f : flows) { double slow = 0; std::vector lines; for (const auto & line : r.metrics_lines) { if (flow_of(line) != f->name) continue; lines.push_back(line); slow = std::max(slow, avg_ns_of(line) / 1.0e6); } if (lines.empty()) slow = r.wall_ms; std::cout << f->name << '\t' << slow << '\t' << r.rc[0] << '\t' << r.rc[1] << '\t' << r.rc[2] << '\t' << repeat << '\t' << warmup << '\n'; for (const auto & line : lines) std::cout << " " << line << '\n'; } std::cout << "suite_wall_ms\t" << r.wall_ms << '\n'; if (r.rc[0] || r.rc[1] || r.rc[2]) ++fails; } else { for (const auto * f : flows) { auto r = pair_flow(f) ? dpf::party::spawn_pair_flow(f->name, opts) : dpf::party::spawn_trio_flow(f->name, opts); std::cout << f->name << '\t' << r.wall_ms << '\t' << r.rc[0] << '\t' << r.rc[1] << '\t' << r.rc[2] << '\t' << repeat << '\t' << warmup << '\n'; for (const auto & line : r.metrics_lines) std::cout << " " << line << '\n'; if (r.rc[0] || r.rc[1] || r.rc[2]) ++fails; } } return fails ? 1 : 0; }