libdpf/party/party_bench.cpp
Ryan Henry 0d22946a0e Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 05:59:19 -06:00

173 lines
5.7 KiB
C++

/// @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 <algorithm>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
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<const dpf::party::flow *> 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<std::string> 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;
}