-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbenchmark.cpp
More file actions
116 lines (101 loc) · 3.13 KB
/
Copy pathbenchmark.cpp
File metadata and controls
116 lines (101 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// The Benchmark and Benchmark Harness are taken from the HFTU from Vitorian/hft-challenges
#include "benchmark_harness.h"
#ifndef RB_IMPL
#define RB_IMPL 1
#endif
#if RB_IMPL == 1
#include "mutex.h"
static constexpr const char* kBenchmarkName = "BM_Mutex";
#elif RB_IMPL == 2
#include "atomic.h"
static constexpr const char* kBenchmarkName = "BM_Atomic";
#elif RB_IMPL == 3
#include "boost_q_.h"
static constexpr const char* kBenchmarkName = "BM_BoostQ";
#else
#error "Unsupported RB_IMPL. Use 1=mutex, 2=atomic, 3=boostq"
#endif
#include <chrono>
#include <thread>
#include <atomic>
#include <iostream>
namespace {
inline uint64_t rdtsc_fenced() {
#if defined(__x86_64__) || defined(_M_X64)
uint32_t lo, hi;
asm volatile(
"lfence\n\t"
"rdtsc"
: "=a"(lo), "=d"(hi)
:
: "memory"
);
return (static_cast<uint64_t>(hi) << 32) | lo;
#elif defined(__aarch64__)
uint64_t val;
asm volatile("isb\n\tmrs %0, cntvct_el0" : "=r"(val));
return val;
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<uint64_t>(ts.tv_sec) * 1'000'000'000ULL + ts.tv_nsec;
#endif
}
uint64_t run_latency(size_t capacity, size_t total_ops) {
hftu::RingBuffer rb(capacity);
uint64_t total_latency = 0;
std::atomic<bool> consumer_ready{false};
std::thread consumer([&]() {
hftu::pin_to_isolated(1);
consumer_ready.store(true, std::memory_order_release);
hftu::Message msg{};
size_t count = 0;
uint64_t sum = 0;
while (count < total_ops) {
if (rb.pop(msg)) {
uint64_t now = rdtsc_fenced();
sum += now - msg.timestamp;
++count;
}
}
total_latency = sum;
});
while (!consumer_ready.load(std::memory_order_acquire)) {}
std::thread producer([&]() {
hftu::pin_to_isolated(0);
hftu::Message msg{};
for (size_t i = 0; i < total_ops; ++i) {
msg.timestamp = rdtsc_fenced();
msg.sequence = i;
msg.symbol_id = static_cast<uint32_t>(i & 0xFFF);
msg.side = static_cast<uint16_t>(i & 1);
msg.price = static_cast<int64_t>(i * 100 + 1);
msg.quantity = static_cast<int64_t>((i & 0xFF) + 1);
msg.order_id = static_cast<int64_t>(i);
while (!rb.push(msg)) {}
}
});
producer.join();
consumer.join();
return total_latency;
}
} // namespace
static hftu::RegisterBenchmark reg_solution(
kBenchmarkName, 1'000'000,
[](int iterations) -> uint64_t {
uint64_t total_cycles = 0;
for (int i = 0; i < iterations; ++i) {
total_cycles += run_latency(2048, 1'000'000);
}
return total_cycles;
}
);
int main() {
const auto wall_start = std::chrono::steady_clock::now();
const int rc = hftu::run_benchmarks();
const auto wall_end = std::chrono::steady_clock::now();
const auto elapsed_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(wall_end - wall_start).count();
std::cout << "Elapsed wall time (ms): " << elapsed_ms << std::endl;
return rc;
}