Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ipc.bench.cpp
Go to the documentation of this file.
2#include "barretenberg/bbapi/generated/bb_ipc_client.hpp"
3#include "barretenberg/bbapi/generated/bb_types.hpp"
6#include <array>
7#include <atomic>
8#include <benchmark/benchmark.h>
9#include <chrono>
10#include <cstring>
11#include <fcntl.h>
12#include <signal.h>
13#include <sys/stat.h>
14#include <sys/wait.h>
15#include <thread>
16#include <unistd.h>
17
18using namespace benchmark;
19using namespace bb;
20
21namespace {
22
23void poseidon_hash_direct(State& state) noexcept
24{
27 for (auto _ : state) {
28 std::vector<fr> to_hash{ x, y };
30 DoNotOptimize(hash);
31 }
32}
33BENCHMARK(poseidon_hash_direct)->Unit(benchmark::kMicrosecond)->Iterations(10000);
34
35// Helper: Spawn bb binary for msgpack benchmarks
36static pid_t spawn_bb_msgpack_server(const std::string& path)
37{
38 pid_t bb_pid = fork();
39 if (bb_pid == 0) {
40 // Child process - redirect stdout/stderr to /dev/null
41 int devnull = open("/dev/null", O_WRONLY);
42 if (devnull >= 0) {
43 dup2(devnull, STDOUT_FILENO);
44 dup2(devnull, STDERR_FILENO);
45 close(devnull);
46 }
47
48 // Try multiple bb binary paths
49 const std::array<const char*, 5> bb_paths = { "./bb", // Same directory
50 "./build/bin/bb", // From cpp/
51 "./bin/bb", // From cpp/build
52 "../bin/bb", // From subdirectory
53 "bb" }; // From PATH
54 for (const char* bb_path : bb_paths) {
55 execl(bb_path, bb_path, "msgpack", "run", "--input", path.c_str(), nullptr);
56 }
57 _exit(1);
58 }
59 return bb_pid;
60}
61
62// Transport type enum for template specialization
63enum class TransportType { Socket, Shm };
64
65// BB Binary Msgpack Benchmark: Full stack test with actual bb binary
66// Template parameters:
67// - Transport: Socket or Shm
68// - NumClients: Number of concurrent clients (1 for SPSC, >1 for MPSC)
69template <TransportType Transport, size_t NumClients> class Poseidon2BBMsgpack : public Fixture {
70 public:
71 static_assert(NumClients >= 1, "Must have at least 1 client");
72
74 pid_t bb_pid{ 0 };
75 std::array<std::thread, (NumClients > 1 ? NumClients - 1 : 1)> background_threads{};
76 std::atomic<bool> stop_background{ false };
77 fr x{};
78 fr y{};
79
80 std::string ipc_path;
81
82 Poseidon2BBMsgpack()
83 {
84 if constexpr (Transport == TransportType::Socket) {
85 ipc_path = "/tmp/poseidon_bb_msgpack_bench.sock";
86 } else {
87 // Use short name for macOS shm_open 31-char limit
88 ipc_path = "/p2_bench.shm";
89 }
90 }
91
92 // Helper to check if socket file exists (only for socket transport)
93 bool socket_exists(const char* path, int max_attempts = 20)
94 {
95 for (int i = 0; i < max_attempts; i++) {
96 struct stat st;
97 if (stat(path, &st) == 0 && S_ISSOCK(st.st_mode)) {
98 return true;
99 }
100 std::this_thread::sleep_for(std::chrono::milliseconds(50));
101 }
102 return false;
103 }
104
105 void SetUp(const ::benchmark::State& /*unused*/) override
106 {
107 stop_background.store(false, std::memory_order_relaxed);
108
109 // Spawn bb binary in IPC server mode
110 bb_pid = spawn_bb_msgpack_server(ipc_path);
111 if (bb_pid < 0) {
112 throw std::runtime_error("Failed to fork bb process");
113 }
114
115 // Wait for server to be ready
116 if constexpr (Transport == TransportType::Socket) {
117 // Wait for socket file to be created
118 if (!socket_exists(ipc_path.c_str())) {
119 kill(bb_pid, SIGKILL);
120 waitpid(bb_pid, nullptr, 0);
121 throw std::runtime_error("BB binary failed to create socket within timeout");
122 }
123 std::this_thread::sleep_for(std::chrono::milliseconds(50));
124 } else {
125 // Shared memory needs more time to initialize
126 std::this_thread::sleep_for(std::chrono::milliseconds(500));
127 }
128
129 for (size_t i = 0; i < NumClients; i++) {
130 clients[i] = std::make_unique<bbapi::BbIpcClient>(ipc_path);
131 }
132
133 // Spawn background threads for MPSC scenarios (NumClients > 1)
134 if constexpr (NumClients > 1) {
135 for (size_t i = 1; i < NumClients; i++) {
136 background_threads[i - 1] = std::thread([this, i]() {
137 fr bx = fr::random_element();
138 fr by = fr::random_element();
139
140 while (!stop_background.load(std::memory_order_relaxed)) {
141 auto response = clients[i]->poseidon2_hash(
142 { .inputs = { bb::bbapi::fr_to_wire(bx), bb::bbapi::fr_to_wire(by) } });
143 DoNotOptimize(response.hash);
144 }
145 });
146 }
147 }
148
149 // Pre-generate test inputs for benchmark thread (client 0)
150 x = fr::random_element();
151 y = fr::random_element();
152 }
153
154 void TearDown(const ::benchmark::State& /*unused*/) override
155 {
156 // Stop background threads if any
157 if constexpr (NumClients > 1) {
158 stop_background.store(true, std::memory_order_relaxed);
159 for (size_t i = 0; i < NumClients - 1; i++) {
160 if (background_threads[i].joinable()) {
161 background_threads[i].join();
162 }
163 }
164 }
165
166 // Close all clients
167 for (auto& client : clients) {
168 client.reset();
169 }
170
171 // Ask bb to exit gracefully, then wait for it to release IPC resources.
172 if (bb_pid > 0) {
173 kill(bb_pid, SIGTERM);
174 int status = 0;
175 pid_t result = waitpid(bb_pid, &status, 0); // Blocking wait
176 if (result <= 0) {
177 // If wait failed, force kill
178 kill(bb_pid, SIGKILL);
179 waitpid(bb_pid, nullptr, 0);
180 }
181 }
182 }
183
184 // Benchmark implementation shared across all variants
185 void run_benchmark(benchmark::State& state)
186 {
187 for (auto _ : state) {
188 auto response =
189 clients[0]->poseidon2_hash({ .inputs = { bb::bbapi::fr_to_wire(x), bb::bbapi::fr_to_wire(y) } });
190 DoNotOptimize(response.hash);
191 }
192 }
193};
194
195// Type aliases for specific test cases
196// SPSC: Single client
197using Poseidon2BBSocketSPSC = Poseidon2BBMsgpack<TransportType::Socket, 1>;
198using Poseidon2BBShmSPSC = Poseidon2BBMsgpack<TransportType::Shm, 1>;
199
200// MPSC: Multiple clients (socket only - SHM is SPSC-only now)
201using Poseidon2BBSocketMPSC = Poseidon2BBMsgpack<TransportType::Socket, 3>;
202
203// Macro to register benchmark variants
204#define REGISTER_BB_BENCHMARK(fixture_name) \
205 BENCHMARK_DEFINE_F(fixture_name, poseidon_hash_roundtrip)(benchmark::State & state) \
206 { \
207 run_benchmark(state); \
208 } \
209 BENCHMARK_REGISTER_F(fixture_name, poseidon_hash_roundtrip)->Unit(benchmark::kMicrosecond)->Iterations(10000)
210
211REGISTER_BB_BENCHMARK(Poseidon2BBSocketSPSC);
212REGISTER_BB_BENCHMARK(Poseidon2BBSocketMPSC);
213REGISTER_BB_BENCHMARK(Poseidon2BBShmSPSC);
214
215} // namespace
216
Wire <-> domain conversion helpers for the bbapi handlers.
static FF hash(const std::vector< FF > &input)
Hashes a vector of field elements.
BENCHMARK_MAIN()
#define REGISTER_BB_BENCHMARK(fixture_name)
Fr fr_to_wire(const bb::fr &d)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
BENCHMARK(bench_commit_structured_random_poly< curve::BN254 >) -> Unit(benchmark::kMillisecond)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static field random_element(numeric::RNG *engine=nullptr) noexcept