CARLA
Loading...
Searching...
No Matches
test_benchmark_streaming.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7#include "test.h"
8
9#include <carla/Buffer.h>
10#include <carla/BufferView.h>
13
14#include <boost/asio/executor_work_guard.hpp>
15#include <boost/asio/post.hpp>
16
17#include <algorithm>
18
19using namespace carla::streaming;
20using namespace std::chrono_literals;
21
22static auto make_special_message(size_t size) {
23 std::vector<uint32_t> v(size/sizeof(uint32_t), 42u);
24 carla::Buffer msg(v);
25 EXPECT_EQ(msg.size(), size);
27
28 return BufView;
29}
30
31class Benchmark {
32public:
33
34 Benchmark(uint16_t port, size_t message_size, double success_ratio)
35 : _server(port),
36 _client(),
37 _message(make_special_message(message_size)),
39 _work_to_do(boost::asio::make_work_guard(_client_callback)),
40 _success_ratio(success_ratio) {
41 // Sync mode blocks on a pending write instead of dropping the message.
43 }
44
45 void AddStream() {
46 Stream stream = _server.MakeStream();
47
48 _client.Subscribe(stream.token(), [this](carla::Buffer msg) {
49 carla::SharedBufferView BufView = carla::BufferView::CreateFrom(std::move(msg));
50 DEBUG_ASSERT_EQ(BufView->size(), _message->size());
51 boost::asio::post(_client_callback, [this]() {
52 CARLA_PROFILE_FPS(client, listen_callback);
53 ++_number_of_messages_received;
54 });
55 });
56
57 _streams.push_back(stream);
58 }
59
60 void AddStreams(size_t count) {
61 for (auto i = 0u; i < count; ++i) {
62 AddStream();
63 }
64 }
65
66 void Run(size_t number_of_messages) {
67 _threads.CreateThread([this]() { _client_callback.run(); });
68 _server.AsyncRun(_streams.size());
69 _client.AsyncRun(_streams.size());
70
71 std::this_thread::sleep_for(1s); // the client needs to be ready so we make
72 // sure we get all the messages.
73
74 for (auto &&stream : _streams) {
75 _threads.CreateThread([=]() mutable {
76 for (auto i = 0u; i < number_of_messages; ++i) {
77 std::this_thread::sleep_for(11ms); // ~90FPS.
78 {
79 CARLA_PROFILE_SCOPE(game, write_to_stream);
80 stream.Write(_message);
81 }
82 }
83 });
84 }
85
86 const auto expected_number_of_messages = _streams.size() * number_of_messages;
87 const auto threshold =
88 static_cast<size_t>(_success_ratio * static_cast<double>(expected_number_of_messages));
89
90 for (auto i = 0u; i < 10; ++i) {
91 std::cout << "received " << _number_of_messages_received
92 << " of " << expected_number_of_messages
93 << " messages,";
94 if (_number_of_messages_received >= expected_number_of_messages) {
95 break;
96 }
97 std::cout << " waiting..." << std::endl;
98 std::this_thread::sleep_for(1s);
99 }
100
101 _client_callback.stop();
102 _threads.JoinAll();
103 std::cout << " done." << std::endl;
104
105#ifdef NDEBUG
106 ASSERT_GE(_number_of_messages_received, threshold);
107#else
108 if (_number_of_messages_received < threshold) {
109 carla::log_warning("threshold unmet:", _number_of_messages_received, '/', threshold);
110 }
111#endif // NDEBUG
112 }
113
114private:
115
117
119
121
123
124 boost::asio::io_context _client_callback;
125
126 boost::asio::executor_work_guard<boost::asio::io_context::executor_type> _work_to_do;
127
128 const double _success_ratio;
129
130 std::vector<Stream> _streams;
131
132 std::atomic_size_t _number_of_messages_received{0u};
133};
134
135static size_t get_max_concurrency() {
136 size_t concurrency = std::thread::hardware_concurrency() / 2u;
137 return std::max(static_cast<size_t>(2u), concurrency);
138}
139
140static void benchmark_image(
141 const size_t dimensions,
142 const size_t number_of_streams = 1u,
143 const double success_ratio = 1.0) {
144 constexpr auto number_of_messages = 100u;
145 carla::logging::log("Benchmark:", number_of_streams, "streams at 90FPS.");
146 Benchmark benchmark(TESTING_PORT, 4u * dimensions, success_ratio);
147 benchmark.AddStreams(number_of_streams);
148 benchmark.Run(number_of_messages);
149}
150
151TEST(benchmark_streaming, image_200x200) {
152 benchmark_image(200u * 200u);
153}
154
155TEST(benchmark_streaming, image_800x600) {
156 benchmark_image(800u * 600u, 1u, 0.9);
157}
158
159TEST(benchmark_streaming, image_1920x1080) {
160 benchmark_image(1920u * 1080u, 1u, 0.9);
161}
162
163TEST(benchmark_streaming, image_200x200_mt) {
164 benchmark_image(200u * 200u, get_max_concurrency());
165}
166
167TEST(benchmark_streaming, image_800x600_mt) {
168 benchmark_image(800u * 600u, get_max_concurrency(), 0.9);
169}
170
171TEST(benchmark_streaming, image_1920x1080_mt) {
172 benchmark_image(1920u * 1080u, get_max_concurrency(), 0.9);
173}
#define CARLA_PROFILE_SCOPE(context, profiler_name)
Definition Profiler.h:10
Benchmark(uint16_t port, size_t message_size, double success_ratio)
void Run(size_t number_of_messages)
const carla::SharedBufferView _message
boost::asio::io_context _client_callback
std::vector< Stream > _streams
void AddStreams(size_t count)
carla::ThreadGroup _threads
boost::asio::executor_work_guard< boost::asio::io_context::executor_type > _work_to_do
const double _success_ratio
static std::shared_ptr< BufferView > CreateFrom(Buffer &&buffer)
Definition BufferView.h:56
A piece of raw data.
size_type size() const noexcept
A client able to subscribe to multiple streams.
void Subscribe(const Token &token, Functor &&callback)
A streaming server.
void SetSynchronousMode(bool is_synchro)
Token token() const
Token associated with this stream.
static void log(Args &&... args)
Definition Logging.h:59
std::shared_ptr< BufferView > SharedBufferView
Definition BufferView.h:151
static void log_warning(Args &&... args)
Definition Logging.h:96
constexpr uint16_t TESTING_PORT
Definition test.h:24
static auto make_special_message(size_t size)
static void benchmark_image(const size_t dimensions, const size_t number_of_streams=1u, const double success_ratio=1.0)
static size_t get_max_concurrency()
TEST(benchmark_streaming, image_200x200)