Skip to content

Commit f249a59

Browse files
Implement LoLa benchmark
1 parent 4cd4956 commit f249a59

File tree

5 files changed

+416
-0
lines changed

5 files changed

+416
-0
lines changed

score/mw/com/benchmark/BUILD

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
13+
load("@score-baselibs//score/language/safecpp:toolchain_features.bzl", "COMPILER_WARNING_FEATURES")
14+
15+
cc_binary(
16+
name = "benchmark",
17+
srcs = [
18+
"benchmark.cpp",
19+
"benchmark.h",
20+
],
21+
data = ["etc/mw_com_config.json"],
22+
features = COMPILER_WARNING_FEATURES + [
23+
"aborts_upon_exception",
24+
],
25+
deps = [
26+
"//score/mw/com",
27+
"@score-baselibs//score/mw/log",
28+
"//third_party/boost:program_options",
29+
"@score-baselibs//score/language/futurecpp",
30+
],
31+
)
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/********************************************************************************
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
#include "score/mw/com/impl/instance_specifier.h"
14+
#include "benchmark.h"
15+
16+
#include "score/concurrency/notification.h"
17+
18+
#include <cstdlib>
19+
#include <iostream>
20+
#include <sys/syscall.h>
21+
#include <thread>
22+
#include <score/latch.hpp>
23+
24+
#include "score/mw/com/runtime.h"
25+
26+
using namespace std::chrono_literals;
27+
using namespace score::mw::com;
28+
using namespace std::chrono;
29+
30+
std::mutex cout_mutex{};
31+
score::cpp::latch benchmark_ab_start_point{3}, benchmark_ab_finish_point{3}, init_ab_sync_point{3}, deinit_ab_sync_point{2};
32+
score::cpp::latch benchmark_multi_start_point{1 + kThreadsMultiTotal}, benchmark_multi_finish_point{1 + kThreadsMultiTotal}, init_multi_sync_point{kThreadsMultiTotal}, deinit_multi_sync_point{kThreadsMultiTotal};
33+
const auto instance_specifier_skeleton_a_optional = InstanceSpecifier::Create("benchmark/SkeletonA");
34+
const auto instance_specifier_skeleton_b_optional = InstanceSpecifier::Create("benchmark/SkeletonB");
35+
36+
score::cpp::optional<std::reference_wrapper<impl::ProxyEvent<DummyBenchmarkData>>> GetBenchmarkDataProxyEvent(
37+
BenchmarkProxy& proxy)
38+
{
39+
return proxy.dummy_benchmark_data_;
40+
}
41+
42+
void SetupThread(int cpu)
43+
{
44+
auto id = std::this_thread::get_id();
45+
auto native_handle = *reinterpret_cast<std::thread::native_handle_type*>(&id);
46+
47+
int max_priority = sched_get_priority_max(SCHED_RR);
48+
struct sched_param params;
49+
params.sched_priority = max_priority;
50+
if (pthread_setschedparam(native_handle, SCHED_RR, &params))
51+
{
52+
std::cout << "Failed to setschedparam: " << std::strerror(errno) << std::endl;
53+
std::cout << "App needs to be run as root" << std::endl;
54+
return;
55+
}
56+
57+
cpu_set_t cpuset;
58+
CPU_ZERO(&cpuset);
59+
CPU_SET(cpu, &cpuset);
60+
if (pthread_setaffinity_np(native_handle, sizeof(cpu_set_t), &cpuset))
61+
{
62+
std::cout << "Failed to setaffinity_np: " << std::strerror(errno) << std::endl;
63+
std::cout << "App needs to be run as root" << std::endl;
64+
}
65+
}
66+
67+
void Transmitter(int cpu, bool starter, impl::InstanceSpecifier skeleton_instance_specifier, impl::InstanceSpecifier proxy_instance_specifier)
68+
{
69+
SetupThread(cpu);
70+
71+
auto create_result = BenchmarkSkeleton::Create(skeleton_instance_specifier);
72+
if (!create_result.has_value())
73+
{
74+
std::cerr << "Unable to construct skeleton: " << create_result.error() << "!" << std::endl;
75+
return;
76+
}
77+
auto& skeleton = create_result.value();
78+
const auto offer_result = skeleton.OfferService();
79+
if (!offer_result.has_value())
80+
{
81+
std::cerr << "Unable to offer service for skeleton: " << offer_result.error() << "!" << std::endl;
82+
return;
83+
}
84+
85+
ServiceHandleContainer<impl::HandleType> handle{};
86+
do
87+
{
88+
auto handles_result = BenchmarkProxy::FindService(proxy_instance_specifier);
89+
if (!handles_result.has_value())
90+
{
91+
std::cerr << "Unable to find service: " << handles_result.error() << "!" << std::endl;
92+
return;
93+
}
94+
handle = std::move(handles_result).value();
95+
if (handle.size() == 0)
96+
{
97+
std::this_thread::sleep_for(500ms);
98+
}
99+
} while (handle.size() == 0);
100+
101+
auto proxy_result = BenchmarkProxy::Create(std::move(handle.front()));
102+
if (!proxy_result.has_value())
103+
{
104+
std::cerr << "Unable to construct proxy: " << proxy_result.error() << "!" << std::endl;
105+
return;
106+
}
107+
auto& proxy = proxy_result.value();
108+
109+
auto dummy_data_event_optional = GetBenchmarkDataProxyEvent(proxy);
110+
if (!dummy_data_event_optional.has_value())
111+
{
112+
std::cerr << "Could not get dummy_data proxy event" << std::endl;
113+
return;
114+
}
115+
impl::ProxyEvent<score::mw::com::DummyBenchmarkData>& dummy_data_event = dummy_data_event_optional.value().get();
116+
score::Result<score::mw::com::impl::SampleAllocateePtr<score::mw::com::DummyBenchmarkData>> sample_result;
117+
118+
dummy_data_event.Subscribe(1);
119+
120+
init_ab_sync_point.arrive_and_wait();
121+
benchmark_ab_start_point.arrive_and_wait();
122+
if (starter)
123+
{
124+
do {
125+
sample_result = skeleton.dummy_benchmark_data_.Allocate();
126+
} while (!sample_result.has_value());
127+
skeleton.dummy_benchmark_data_.Send(std::move(sample_result).value());
128+
}
129+
for (std::size_t cycle = 0U; cycle < kIterations; cycle++)
130+
{
131+
while (!dummy_data_event.GetNewSamples((
132+
[](SamplePtr<DummyBenchmarkData> sample) noexcept {
133+
std::ignore = sample;
134+
}),
135+
1).has_value()) {};
136+
do {
137+
sample_result = skeleton.dummy_benchmark_data_.Allocate();
138+
} while (!sample_result.has_value());
139+
skeleton.dummy_benchmark_data_.Send(std::move(sample_result).value());
140+
141+
}
142+
benchmark_ab_finish_point.arrive_and_wait();
143+
dummy_data_event.Unsubscribe();
144+
deinit_ab_sync_point.arrive_and_wait();
145+
skeleton.StopOfferService();
146+
}
147+
148+
void Subscriber(int cpu, impl::InstanceSpecifier proxy_instance_specifier)
149+
{
150+
SetupThread(cpu);
151+
152+
ServiceHandleContainer<impl::HandleType> handle{};
153+
do
154+
{
155+
auto handles_result = BenchmarkProxy::FindService(proxy_instance_specifier);
156+
if (!handles_result.has_value())
157+
{
158+
std::cerr << "Unable to find service: " << handles_result.error() << "!" << std::endl;
159+
return;
160+
}
161+
handle = std::move(handles_result).value();
162+
if (handle.size() == 0)
163+
{
164+
std::this_thread::sleep_for(500ms);
165+
}
166+
} while (handle.size() == 0);
167+
168+
auto proxy_result = BenchmarkProxy::Create(std::move(handle.front()));
169+
if (!proxy_result.has_value())
170+
{
171+
std::cerr << "Unable to construct proxy: " << proxy_result.error() << "!" << std::endl;
172+
return;
173+
}
174+
auto& proxy = proxy_result.value();
175+
176+
auto dummy_data_event_optional = GetBenchmarkDataProxyEvent(proxy);
177+
if (!dummy_data_event_optional.has_value())
178+
{
179+
std::cerr << "Could not get dummy_data proxy event" << std::endl;
180+
return;
181+
}
182+
impl::ProxyEvent<score::mw::com::DummyBenchmarkData>& dummy_data_event = dummy_data_event_optional.value().get();
183+
184+
dummy_data_event.Subscribe(1);
185+
init_multi_sync_point.arrive_and_wait();
186+
benchmark_multi_start_point.arrive_and_wait();
187+
for (std::size_t cycle = 0U; cycle < kIterations; cycle++)
188+
{
189+
while (!dummy_data_event.GetNewSamples((
190+
[](SamplePtr<DummyBenchmarkData> sample) noexcept {
191+
std::ignore = sample;
192+
}),
193+
1).has_value()) {};
194+
195+
}
196+
benchmark_multi_finish_point.arrive_and_wait();
197+
dummy_data_event.Unsubscribe();
198+
deinit_multi_sync_point.arrive_and_wait();
199+
}
200+
201+
int main()
202+
{
203+
int cpu = 0;
204+
205+
if (!instance_specifier_skeleton_a_optional.has_value() || !instance_specifier_skeleton_b_optional.has_value())
206+
{
207+
std::cerr << "Invalid instance specifier, terminating." << std::endl;
208+
return EXIT_FAILURE;
209+
}
210+
const auto& instance_specifier_skeleton_a = instance_specifier_skeleton_a_optional.value();
211+
const auto& instance_specifier_skeleton_b = instance_specifier_skeleton_b_optional.value();
212+
213+
std::cout << "Starting benchmark" << std::endl;
214+
215+
std::thread transmitterA(Transmitter, cpu++, true, std::ref(instance_specifier_skeleton_a), std::ref(instance_specifier_skeleton_b));
216+
std::thread transmitterB(Transmitter, cpu++, false, std::ref(instance_specifier_skeleton_b), std::ref(instance_specifier_skeleton_a));
217+
#if kSubscribers > 0
218+
std::thread subscribers[kSubscribers];
219+
if (kSubscribers > 0)
220+
{
221+
subscribers[i] = std::thread(Subscriber, cpu++, std::ref(instance_specifier_skeleton_a));
222+
subscribers[i] = std::thread(Subscriber, cpu++, std::ref(instance_specifier_skeleton_b));
223+
}
224+
#endif
225+
init_ab_sync_point.arrive_and_wait();
226+
const auto benchmark_ab_start_time = std::chrono::steady_clock::now();
227+
benchmark_ab_start_point.arrive_and_wait();
228+
benchmark_ab_finish_point.arrive_and_wait();
229+
const auto benchmark_ab_stop_time = std::chrono::steady_clock::now();
230+
const auto benchmark_ab_time = benchmark_ab_stop_time - benchmark_ab_start_time;
231+
232+
transmitterA.join();
233+
transmitterB.join();
234+
#if kSubscribers > 0
235+
for (std::size_t i = 0; i < kSubscribers; i++)
236+
{
237+
subscribers[i].join();
238+
}
239+
#endif
240+
std::cout << "Results:" << "\t" <<
241+
"Iterations: " << kIterations << ", " << "\t" <<
242+
"Time: " << duration<float>(benchmark_ab_time).count() << "s, " << "\t" <<
243+
"Latency: " << duration_cast<nanoseconds>(benchmark_ab_time).count() / (kIterations * 2) << "ns, " << "\t" <<
244+
"Sample Size: " << kSampleSize <<
245+
"Additional subscribers: " << kSubscribers << std::endl;
246+
}

score/mw/com/benchmark/benchmark.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/********************************************************************************
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
#ifndef SCORE_IPC_BRIDGE_BENCHMARK_H
14+
#define SCORE_IPC_BRIDGE_BENCHMARK_H
15+
16+
#include "score/mw/com/types.h"
17+
18+
namespace score::mw::com
19+
{
20+
21+
constexpr std::size_t kSampleSize = 8192;
22+
constexpr std::uint32_t kIterations = 1000000;
23+
constexpr std::size_t kSubscribers = 2;
24+
constexpr std::size_t kThreadsMultiTotal = kSubscribers + 2;
25+
26+
struct DummyBenchmarkData
27+
{
28+
DummyBenchmarkData() = default;
29+
30+
DummyBenchmarkData(DummyBenchmarkData&&) = default;
31+
32+
DummyBenchmarkData(const DummyBenchmarkData&) = default;
33+
34+
DummyBenchmarkData& operator=(DummyBenchmarkData&&) = default;
35+
36+
DummyBenchmarkData& operator=(const DummyBenchmarkData&) = default;
37+
38+
std::array<std::uint32_t, kSampleSize / sizeof(std::uint32_t)> dummy_data;
39+
40+
};
41+
42+
template <typename Trait>
43+
class IpcBridgeInterface : public Trait::Base
44+
{
45+
public:
46+
using Trait::Base::Base;
47+
48+
typename Trait::template Event<DummyBenchmarkData> dummy_benchmark_data_{*this, "dummy_data_arrived"};
49+
};
50+
51+
using BenchmarkProxy = AsProxy<IpcBridgeInterface>;
52+
using BenchmarkSkeleton = AsSkeleton<IpcBridgeInterface>;
53+
54+
} // namespace score::mw::com
55+
56+
#endif // SCORE_IPC_BRIDGE_BENCHMARK_H
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"appId": "IPBR",
3+
"appDesc": "ipc_bridge",
4+
"logLevel": "kOff",
5+
"logLevelThresholdConsole": "kOff",
6+
"logMode": "kConsole",
7+
"dynamicDatarouterIdentifiers" : true
8+
}

0 commit comments

Comments
 (0)