Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ipc.bench.cpp
Go to the documentation of this file.
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 poseiden_hash_direct(State& state) noexcept
24{
27 for (auto _ : state) {
28 std::vector<grumpkin::fq> to_hash{ x, y };
30 DoNotOptimize(hash);
31 }
32}
33BENCHMARK(poseiden_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
73 std::array<std::unique_ptr<ipc::IpcClient>, NumClients> clients{};
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 grumpkin::fq x{};
78 grumpkin::fq 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 // Create and connect all clients
130 for (size_t i = 0; i < NumClients; i++) {
131 if constexpr (Transport == TransportType::Socket) {
132 clients[i] = ipc::IpcClient::create_socket(ipc_path);
133 } else {
134 // Strip .shm suffix for base name
135 std::string base_name = ipc_path.substr(0, ipc_path.size() - 4);
136 clients[i] = ipc::IpcClient::create_shm(base_name);
137 }
138
139 bool connected = false;
140 for (int retry_count = 0; retry_count < 5; retry_count++) {
141 if (clients[i]->connect()) {
142 connected = true;
143 break;
144 }
145 std::this_thread::sleep_for(std::chrono::milliseconds(50));
146 }
147
148 if (!connected) {
149 kill(bb_pid, SIGKILL);
150 waitpid(bb_pid, nullptr, 0);
151 throw std::runtime_error("Failed to connect to BB IPC server after retries");
152 }
153 }
154
155 // Spawn background threads for MPSC scenarios (NumClients > 1)
156 if constexpr (NumClients > 1) {
157 for (size_t i = 1; i < NumClients; i++) {
158 background_threads[i - 1] = std::thread([this, i]() {
161
162 while (!stop_background.load(std::memory_order_relaxed)) {
163 // Create Poseidon2Hash command
165 hash_cmd.inputs = { uint256_t(bx), uint256_t(by) };
166 bb::bbapi::Command command{ std::move(hash_cmd) };
167
168 // Serialize command with tuple wrapping for CBIND compatibility
169 msgpack::sbuffer cmd_buffer;
170 msgpack::pack(cmd_buffer, std::make_tuple(command));
171
172 // Send with retry on backpressure (100ms timeout)
173 constexpr uint64_t TIMEOUT_NS = 100000000; // 100ms
174 while (!clients[i]->send(cmd_buffer.data(), cmd_buffer.size(), TIMEOUT_NS)) {
175 // Ring buffer full, retry
176 if (stop_background.load(std::memory_order_relaxed)) {
177 return; // Exit if shutting down
178 }
179 }
180
181 // Receive with retry (100ms timeout)
183 while ((response = clients[i]->receive(TIMEOUT_NS)).empty()) {
184 // Response not ready, retry
185 if (stop_background.load(std::memory_order_relaxed)) {
186 return; // Exit if shutting down
187 }
188 }
189
190 // Release the message
191 clients[i]->release(response.size());
192 }
193 });
194 }
195 }
196
197 // Pre-generate test inputs for benchmark thread (client 0)
200 }
201
202 void TearDown(const ::benchmark::State& /*unused*/) override
203 {
204 // Stop background threads if any
205 if constexpr (NumClients > 1) {
206 stop_background.store(true, std::memory_order_relaxed);
207 for (size_t i = 0; i < NumClients - 1; i++) {
208 if (background_threads[i].joinable()) {
209 background_threads[i].join();
210 }
211 }
212 }
213
214 // Send Shutdown command to bb so it exits gracefully (use client 0)
215 if (clients[0]) {
216 // Create Shutdown command
217 bb::bbapi::Shutdown shutdown_cmd;
218 bb::bbapi::Command command{ std::move(shutdown_cmd) };
219
220 // Serialize command with tuple wrapping for CBIND compatibility
221 msgpack::sbuffer cmd_buffer;
222 msgpack::pack(cmd_buffer, std::make_tuple(command));
223
224 // Send shutdown command with retry (1s timeout)
225 constexpr uint64_t TIMEOUT_NS = 1000000000; // 1 second
226 while (!clients[0]->send(cmd_buffer.data(), cmd_buffer.size(), TIMEOUT_NS)) {
227 // Retry on backpressure
228 }
229
231 while ((response = clients[0]->receive(TIMEOUT_NS)).empty()) {
232 // Retry until response ready
233 }
234
235 clients[0]->release(response.size());
236 }
237
238 // Close all clients
239 for (auto& client : clients) {
240 if (client) {
241 client->close();
242 }
243 }
244
245 // Wait for bb to exit gracefully (destructors will clean up resources)
246 if (bb_pid > 0) {
247 int status = 0;
248 pid_t result = waitpid(bb_pid, &status, 0); // Blocking wait
249 if (result <= 0) {
250 // If wait failed, force kill
251 kill(bb_pid, SIGKILL);
252 waitpid(bb_pid, nullptr, 0);
253 }
254 }
255 }
256
257 // Benchmark implementation shared across all variants
258 void run_benchmark(benchmark::State& state)
259 {
260 constexpr uint64_t TIMEOUT_NS = 1000000000; // 1 second
261
262 for (auto _ : state) {
263 // Create Poseidon2Hash command
265 hash_cmd.inputs = { uint256_t(x), uint256_t(y) };
266 bb::bbapi::Command command{ std::move(hash_cmd) };
267
268 // Serialize command with tuple wrapping for CBIND compatibility
269 msgpack::sbuffer cmd_buffer;
270 msgpack::pack(cmd_buffer, std::make_tuple(command));
271
272 // Send command with retry on backpressure
273 while (!clients[0]->send(cmd_buffer.data(), cmd_buffer.size(), TIMEOUT_NS)) {
274 // Ring buffer full, retry (shouldn't happen often in benchmarks)
275 }
276
277 // Receive response with retry
279 while ((resp = clients[0]->receive(TIMEOUT_NS)).empty()) {
280 // Response not ready, retry
281 }
282
283 // Deserialize response
284 auto unpacked = msgpack::unpack(reinterpret_cast<const char*>(resp.data()), resp.size());
286 unpacked.get().convert(response);
287
288 // Release the message
289 clients[0]->release(resp.size());
290
291 // Extract hash from response
292 const auto& response_variant = static_cast<const bb::bbapi::CommandResponse::VariantType&>(response);
293 const auto* hash_response = std::get_if<bb::bbapi::Poseidon2Hash::Response>(&response_variant);
294 if (hash_response == nullptr) {
295 state.SkipWithError("Invalid response type");
296 break;
297 }
298
299 DoNotOptimize(hash_response->hash);
300 }
301 }
302};
303
304// Type aliases for specific test cases
305// SPSC: Single client
306using Poseidon2BBSocketSPSC = Poseidon2BBMsgpack<TransportType::Socket, 1>;
307using Poseidon2BBShmSPSC = Poseidon2BBMsgpack<TransportType::Shm, 1>;
308
309// MPSC: Multiple clients (socket only - SHM is SPSC-only now)
310using Poseidon2BBSocketMPSC = Poseidon2BBMsgpack<TransportType::Socket, 3>;
311
312// Macro to register benchmark variants
313#define REGISTER_BB_BENCHMARK(fixture_name) \
314 BENCHMARK_DEFINE_F(fixture_name, poseiden_hash_roundtrip)(benchmark::State & state) \
315 { \
316 run_benchmark(state); \
317 } \
318 BENCHMARK_REGISTER_F(fixture_name, poseiden_hash_roundtrip)->Unit(benchmark::kMicrosecond)->Iterations(10000)
319
320REGISTER_BB_BENCHMARK(Poseidon2BBSocketSPSC);
321REGISTER_BB_BENCHMARK(Poseidon2BBSocketMPSC);
322REGISTER_BB_BENCHMARK(Poseidon2BBShmSPSC);
323
324} // namespace
325
A wrapper around std::variant that provides msgpack serialization based on type names.
VariantType & get()
std::variant< Types... > VariantType
static std::unique_ptr< IpcClient > create_socket(const std::string &socket_path)
static std::unique_ptr< IpcClient > create_shm(const std::string &base_name)
BENCHMARK_MAIN()
#define REGISTER_BB_BENCHMARK(fixture_name)
void hash(State &state) noexcept
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
Compute Poseidon2 hash of input field elements.
std::vector< fr > inputs
static field random_element(numeric::RNG *engine=nullptr) noexcept