Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_executable(bench_matching_engine bench_matching_engine.cpp)
add_executable(bench_risk_manager bench_risk_manager.cpp)
add_executable(bench_execution_gateway bench_execution_gateway.cpp)
add_executable(bench_feed_handler bench_feed_handler.cpp)
add_executable(bench_book_snapshot bench_book_snapshot.cpp)

# Link libraries for each
foreach(bench_target
Expand All @@ -15,7 +16,8 @@ foreach(bench_target
bench_matching_engine
bench_risk_manager
bench_execution_gateway
bench_feed_handler)
bench_feed_handler
bench_book_snapshot)
target_link_libraries(${bench_target}
velox_core
benchmark::benchmark
Expand Down
169 changes: 169 additions & 0 deletions benchmarks/bench_book_snapshot.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#include <benchmark/benchmark.h>
#include <thread>
#include <vector>
#include "velox/book/order_book.hpp"
#include "velox/book/book_snapshot.hpp"
#include "lockfree/pool.hpp"
#include <chrono>

using namespace velox;

class BookSnapshotBench {
public:
BookSnapshotBench() : pool(std::make_unique<lockfree::ObjectPool<Order, 100000>>()) {
book = std::make_unique<OrderBook>("AAPL");
// Add some bids and asks to make the snapshot non‑trivial
for (int i = 0; i < 10; ++i) {
auto bid = create_order(i, OrderSide::BUY, 10000 + i * 10, 100);
book->add_order(bid);
auto ask = create_order(1000 + i, OrderSide::SELL, 10100 + i * 10, 100);
book->add_order(ask);
}
manager = std::make_unique<BookSnapshotManager>(5); // capture top 5 levels
manager->update(*book);
}

Order* create_order(uint64_t id, OrderSide side, int64_t price, uint32_t qty) {
auto order = pool->acquire();
order->order_id = id;
order->side = side;
order->price = price;
order->quantity = qty;
order->remaining_quantity = qty;
order->filled_quantity = 0;
std::strncpy(order->symbol, "AAPL", 7);
Order* raw = order.get();
owned_orders.push_back(std::move(order));
return raw;
}

std::unique_ptr<lockfree::ObjectPool<Order, 100000>> pool;
std::unique_ptr<OrderBook> book;
std::unique_ptr<BookSnapshotManager> manager;
std::vector<lockfree::PooledPtr<Order, 100000>> owned_orders;
};

static void BM_Snapshot_Update(benchmark::State& state) {
BookSnapshotBench bench;
for (auto _ : state) {
bench.manager->update(*bench.book);
benchmark::DoNotOptimize(bench.manager);
}
}
BENCHMARK(BM_Snapshot_Update);

static void BM_Snapshot_Get(benchmark::State& state) {
BookSnapshotBench bench;
bench.manager->update(*bench.book); // initial snapshot
for (auto _ : state) {
const BookSnapshot* snap = bench.manager->get_snapshot();
benchmark::DoNotOptimize(snap);
bench.manager->release_snapshot(snap);
}
}
BENCHMARK(BM_Snapshot_Get);

static void BM_Snapshot_GetAndTouch(benchmark::State& state) {
BookSnapshotBench bench;
bench.manager->update(*bench.book);
for (auto _ : state) {
const BookSnapshot* snap = bench.manager->get_snapshot();
int64_t bid = snap->best_bid;
int64_t ask = snap->best_ask;
uint32_t depth = snap->bid_depth;
// touch a few price levels
if (!snap->bids.empty()) {
benchmark::DoNotOptimize(snap->bids[0].price);
}
if (!snap->asks.empty()) {
benchmark::DoNotOptimize(snap->asks[0].price);
}
benchmark::DoNotOptimize(bid);
benchmark::DoNotOptimize(ask);
benchmark::DoNotOptimize(depth);
bench.manager->release_snapshot(snap);
}
}
BENCHMARK(BM_Snapshot_GetAndTouch);

static void BM_Snapshot_Concurrent(benchmark::State& state) {
const int num_readers = state.range(0);
BookSnapshotBench bench;
bench.manager->update(*bench.book);

std::vector<std::thread> readers;
std::atomic<bool> stop{false};
std::atomic<uint64_t> total_ops{0};

// Start reader threads
for (int i = 0; i < num_readers; ++i) {
readers.emplace_back([&]() {
while (!stop.load(std::memory_order_relaxed)) {
const BookSnapshot* snap = bench.manager->get_snapshot();
if (snap) {
// Touch a field to simulate real work
benchmark::DoNotOptimize(snap->best_bid);
bench.manager->release_snapshot(snap);
total_ops.fetch_add(1, std::memory_order_relaxed);
}
}
});
}

// Let the system stabilise
std::this_thread::sleep_for(std::chrono::milliseconds(10));

// Run the benchmark: measure operations over a fixed time window
for (auto _ : state) {
uint64_t before = total_ops.load(std::memory_order_relaxed);
// Run for a short, fixed duration
auto start = std::chrono::high_resolution_clock::now();
while (std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start) < std::chrono::milliseconds(10)) {
// spin – wait for the time window to expire
}
uint64_t after = total_ops.load(std::memory_order_relaxed);
state.SetIterationTime(0.01); // 10 ms
state.SetItemsProcessed(after - before);
}

stop = true;
for (auto& t : readers) t.join();
}
BENCHMARK(BM_Snapshot_Concurrent)->Arg(1)->Arg(2)->Arg(4)->Arg(8)->Arg(16);

static void BM_Snapshot_UpdateThroughput(benchmark::State& state) {
BookSnapshotBench bench;
for (auto _ : state) {
bench.manager->update(*bench.book);
benchmark::DoNotOptimize(bench.manager);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK(BM_Snapshot_UpdateThroughput);

static void BM_Snapshot_GetThroughput(benchmark::State& state) {
BookSnapshotBench bench;
bench.manager->update(*bench.book);
for (auto _ : state) {
const BookSnapshot* snap = bench.manager->get_snapshot();
benchmark::DoNotOptimize(snap);
bench.manager->release_snapshot(snap);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK(BM_Snapshot_GetThroughput);

static void BM_Snapshot_UpdateLatency(benchmark::State& state) {
BookSnapshotBench bench;
for (auto _ : state) {
auto start = std::chrono::high_resolution_clock::now();
bench.manager->update(*bench.book);
auto end = std::chrono::high_resolution_clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
benchmark::DoNotOptimize(ns);
}
}
BENCHMARK(BM_Snapshot_UpdateLatency);

BENCHMARK_MAIN();
Loading