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
25 changes: 23 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,32 @@ set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
CPMAddPackage("gh:Quicr/libquicr#main")
CPMAddPackage("gh:jarro2783/cxxopts@3.3.1")

#=============================================================================#
# Build QPerf executable
#=============================================================================#

add_executable(qperf_meeting src/qperf_meeting.cpp src/publisher_track_handler.cpp src/subscriber_track_handler.cpp)
target_link_libraries(qperf_meeting PRIVATE quicr cxxopts spdlog::spdlog)
target_include_directories(qperf_meeting PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)

target_compile_options(qperf_meeting PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>: -Wpedantic -Wextra -Wall>
$<$<CXX_COMPILER_ID:MSVC>: >
)

set_target_properties(qperf_meeting PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS OFF
)

target_compile_definitions(qperf_meeting PRIVATE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG)

#=============================================================================#
# Build QPerf Publication executable
#=============================================================================#

add_executable(qperf_pub src/qperf_pub.cc)
add_executable(qperf_pub src/qperf_pub.cpp src/publisher_track_handler.cpp)
target_link_libraries(qperf_pub PRIVATE quicr cxxopts spdlog::spdlog)
target_include_directories(qperf_pub PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)

Expand All @@ -44,7 +65,7 @@ target_compile_definitions(qperf_pub PRIVATE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DE
# Build QPerf Subscription executable
#=============================================================================#

add_executable(qperf_sub src/qperf_sub.cc)
add_executable(qperf_sub src/qperf_sub.cpp src/subscriber_track_handler.cpp)
target_link_libraries(qperf_sub PRIVATE quicr cxxopts spdlog::spdlog)
target_include_directories(qperf_sub PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)

Expand Down
52 changes: 31 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
# qperf

Utility to evaluate relay performance.
Utility to evaluate relay performance.

## Command Line Programs

### > src/qperf_pub

**qperf_pub** is a publisher that publishes based on the `-c <config.ini>` profile.

### > src/qperf_sub

**qperf_sub** is a subscriber that consumes the tracks based on the `-c <config.ini>` profile.

It is intended that the same `config.ini` file be used for both pub and sub. This way they have
the same settings.

## config.ini
## Configuration

Profile configuration per track is configured in a `config.ini` file that is provided to the program using
the `-c` option. See the [config.ini template](templates/config.template.ini) for an example configuration.
the `-c` option. See the [config.ini template](templates/config.template.ini) for an example configuration.

Each section in the `config.ini` defines a test for a publish track and subscribe track.
The `namespace` and `name` together should be **unique** for the section, which is the track.

The `namespace` and `name` together should be **unique** for the section, which is the track.

Sections are laid out as follows:

```ini
[TRACK] ; MUST be unique
namespace = ; MAY be the same across tracks, entries delimited by /
name = ; SHOULD be unique to other tracks
track_mode = ; (datagram|stream)
priority = ; (0-255)
ttl = ; TTL in ms
time_interval = ; transmit interval in floating point ms
objects_per_group = ; number of objects per group >=1
first_object_size = ; size in bytes of the first object in a group
object_size = ; size in bytes of remaining objects in a group
start_delay = ; start delay in ms - after control messages are sent and acknowledged
total_transmit_time = ; total transmit time in ms
```

> [!IMPORTANT]
> Each section **MUST** not share the same `namespace + name`. If namespace is the same, the name
> should be different.
> Each section **MUST** not share the same `namespace + name` combination. If `namespace` is the same between sections, `name`
> **MUST** be different between sections.

## Building

Expand All @@ -42,4 +45,11 @@ Build the programs using the following:
cmake --build build -j 4
```

The binaries will be under `./build`
The binaries will be under `./build`

## Using

The `qperf` program uses a config file to build tracks. It builds a conference
client by creating 1 publisher track for every Track section in the config file,
and N - 1 subscriber tracks for every Track section. The client does not subscribe
to its own publisher track.
12 changes: 12 additions & 0 deletions examples/config-audio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Audio]
namespace = perf/audio/{} ; MAY be the same across tracks, entries delimited by /
name = 1 ; SHOULD be unique to other tracks
track_mode = datagram ; (datagram|stream)
priority = 2 ; (0-255)
ttl = 5000 ; TTL in ms
time_interval = 20 ; transmit interval in floating point ms
objects_per_group = 1 ; number of objects per group >=1
first_object_size = 120 ; size in bytes of the first object in a group
object_size = 120 ; size in bytes of remaining objects in a group
start_delay = 5000 ; start delay in ms - after control messages are sent and acknowledged
total_transmit_time = 35000 ; total transmit time in ms - including start delay
25 changes: 25 additions & 0 deletions examples/config-av.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[Audio]
namespace = perf/audio/{} ; MAY be the same across tracks, entries delimited by /
name = 1 ; SHOULD be unique to other tracks
track_mode = datagram ; (datagram|stream)
priority = 2 ; (0-255)
ttl = 5000 ; TTL in ms
time_interval = 20 ; transmit interval in floating point ms
objects_per_group = 1 ; number of objects per group >=1
first_object_size = 120 ; size in bytes of the first object in a group
object_size = 120 ; size in bytes of remaining objects in a group
start_delay = 5000 ; start delay in ms - after control messages are sent and acknowledged
total_transmit_time = 35000 ; total transmit time in ms

[360p Video]
namespace = perf/video/{} ; MAY be the same across tracks, entries delimited by /
name = 1 ; SHOULD be unique to other tracks
track_mode = stream ; (datagram|stream)
priority = 3 ; (0-255)
ttl = 5000 ; TTL in ms
time_interval = 33.33 ; transmit interval in floating point ms
objects_per_group = 150 ; number of objects per group >=1
first_object_size = 21333 ; size in bytes of the first object in a group
object_size = 2666 ; size in bytes of remaining objects in a group
start_delay = 5000 ; start delay in ms - after control messages are sent and acknowledged
total_transmit_time = 35000 ; total transmit time in ms
23 changes: 3 additions & 20 deletions include/qperf_pub.hpp → include/publisher_track_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ namespace qperf {
PerfPublishTrackHandler(const PerfConfig&);

public:
static auto Create(const std::string& section_name, ini::IniFile& inif);
static std::shared_ptr<PerfPublishTrackHandler> Create(const std::string& section_name,
ini::IniFile& inif,
std::uint32_t instance_id);
void StatusChanged(Status status) override;
void MetricsSampled(const quicr::PublishTrackMetrics& metrics) override;

Expand Down Expand Up @@ -43,23 +45,4 @@ namespace qperf {
qperf::TestMetrics test_metrics_;
std::mutex mutex_;
};

class PerfPubClient : public quicr::Client
{
public:
PerfPubClient(const quicr::ClientConfig& cfg, const std::string& configfile);
void StatusChanged(Status status) override;
void MetricsSampled(const quicr::ConnectionMetrics&) override;
bool GetTerminateStatus();
bool HandlersComplete();
void Terminate();

private:
bool terminate_;
std::string configfile_;
ini::IniFile inif_;
std::vector<std::shared_ptr<PerfPublishTrackHandler>> track_handlers_;
std::mutex track_handlers_mutex_;
};

} // namespace qperf
46 changes: 26 additions & 20 deletions include/qperf.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#pragma once

#include <cstdint>
#include "inicpp.h"

#include <quicr/client.h>

#include "inicpp.h"
#include <cstdint>

namespace qperf {
struct PerfConfig
Expand All @@ -15,11 +16,11 @@ namespace qperf {
uint32_t ttl;
double transmit_interval;
uint32_t objects_per_group;
uint32_t bytes_per_group_start;
uint32_t bytes_per_group;
uint32_t first_object_size;
uint32_t object_size;
uint64_t start_delay;
uint64_t total_test_time;
uint64_t total_transmit_time;
uint64_t total_test_time;
};

enum class TestMode : uint8_t
Expand Down Expand Up @@ -65,19 +66,24 @@ namespace qperf {
return { quicr::TrackNamespace{ track_namespace }, { track_name.begin(), track_name.end() } };
}

static bool PopulateScenarioFields(const std::string section_name, ini::IniFile& inif, PerfConfig& perf_config)
inline bool PopulateScenarioFields(const std::string section_name,
std::uint32_t instance_id,
ini::IniFile& inif,
PerfConfig& perf_config)
{
bool parsed = false;
std::string scenario_namespace = "";
std::string scenario_name = "";

perf_config.test_name = section_name;

scenario_namespace = inif[section_name]["namespace"].as<std::string>();
scenario_name = inif[section_name]["name"].as<std::string>();
auto& section = inif[section_name];

scenario_namespace = fmt::vformat(section["namespace"].as<std::string>(), fmt::make_format_args(instance_id));
scenario_name = section["name"].as<std::string>();
perf_config.full_track_name = MakeFullTrackName(scenario_namespace, scenario_name);

std::string track_mode_ini_str = inif[section_name]["track_mode"].as<std::string>();
std::string track_mode_ini_str = section["track_mode"].as<std::string>();
if (track_mode_ini_str == "datagram") {
perf_config.track_mode = quicr::TrackMode::kDatagram;
} else if (track_mode_ini_str == "stream") {
Expand All @@ -87,15 +93,15 @@ namespace qperf {
SPDLOG_WARN("Invalid or missing track mode in scenario. Using default `stream`");
}

perf_config.priority = inif[section_name]["priority"].as<std::uint32_t>();
perf_config.ttl = inif[section_name]["ttl"].as<std::uint32_t>();
perf_config.transmit_interval = inif[section_name]["time_interval"].as<double>();
perf_config.objects_per_group = inif[section_name]["objs_per_group"].as<std::uint32_t>();
perf_config.bytes_per_group_start = inif[section_name]["bytes_per_group_start"].as<std::uint32_t>();
perf_config.bytes_per_group = inif[section_name]["bytes_per_group"].as<std::uint32_t>();
perf_config.start_delay = inif[section_name]["start_delay"].as<std::uint64_t>();
perf_config.total_test_time = inif[section_name]["total_test_time"].as<std::uint64_t>();
perf_config.total_transmit_time = perf_config.total_test_time - perf_config.start_delay;
perf_config.priority = section["priority"].as<std::uint32_t>();
perf_config.ttl = section["ttl"].as<std::uint32_t>();
perf_config.transmit_interval = section["time_interval"].as<double>();
perf_config.objects_per_group = section["objects_per_group"].as<std::uint32_t>();
perf_config.first_object_size = section["first_object_size"].as<std::uint32_t>();
perf_config.object_size = section["object_size"].as<std::uint32_t>();
perf_config.start_delay = section["start_delay"].as<std::uint64_t>();
perf_config.total_transmit_time = section["total_transmit_time"].as<std::uint64_t>();
perf_config.total_test_time = perf_config.total_transmit_time + perf_config.start_delay;

SPDLOG_INFO("--------------------------------------------");
SPDLOG_INFO("Test config:");
Expand All @@ -105,8 +111,8 @@ namespace qperf {
SPDLOG_INFO(" pri {}", perf_config.priority);
SPDLOG_INFO(" ttl {}", perf_config.ttl);
SPDLOG_INFO(" objspergroup {}", perf_config.objects_per_group);
SPDLOG_INFO(" bytes per group start {}", perf_config.bytes_per_group_start);
SPDLOG_INFO(" bytes per group {}", perf_config.bytes_per_group);
SPDLOG_INFO(" bytes per group start {}", perf_config.first_object_size);
SPDLOG_INFO(" bytes per group {}", perf_config.object_size);
SPDLOG_INFO(" transmit interval {}", perf_config.transmit_interval);
SPDLOG_INFO(" start_delay {}", perf_config.start_delay);
SPDLOG_INFO(" total test time {}", perf_config.total_test_time);
Expand Down
25 changes: 3 additions & 22 deletions include/qperf_sub.hpp → include/subscriber_track_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ namespace qperf {
PerfSubscribeTrackHandler(const PerfConfig& perf_config, std::uint32_t test_identifier);

public:
static auto Create(const std::string& section_name, ini::IniFile& inif, std::uint32_t test_identifier);
static std::shared_ptr<PerfSubscribeTrackHandler> Create(const std::string& section_name,
ini::IniFile& inif,
std::uint32_t test_identifier);
void ObjectReceived(const quicr::ObjectHeaders&, quicr::BytesSpan) override;
void StatusChanged(Status status) override;
void MetricsSampled(const quicr::SubscribeTrackMetrics& metrics) override;
Expand Down Expand Up @@ -56,25 +58,4 @@ namespace qperf {
std::int64_t total_arrival_delta_;
};

class PerfSubClient : public quicr::Client
{
public:
PerfSubClient(const quicr::ClientConfig& cfg, const std::string& configfile, std::uint32_t test_identifier);
void StatusChanged(Status status) override;
void MetricsSampled(const quicr::ConnectionMetrics&) override {}

bool HandlersComplete();
void Terminate();

private:
bool terminate_;
std::string configfile_;
ini::IniFile inif_;
std::uint32_t test_identifier_;

std::vector<std::shared_ptr<PerfSubscribeTrackHandler>> track_handlers_;

std::mutex track_handlers_mutex_;
};

} // namespace
45 changes: 45 additions & 0 deletions scripts/run_parallel_meetings.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/bin/sh

LOGS_DIR=qperf_logs

if [ -z "$1" ]; then
echo "Using default number of meetings"
MEETINGS=${1:-100}
elif [ "$1" -eq 0 ]; then
echo "Num meetings must be greater than 0"
exit 1
else
MEETINGS=${1:-$1}
fi

if [ -z "$2" ]; then
RELAY="moq://localhost:33435"
else
RELAY="$2"
fi

if [ -z "$3" ]; then
echo "Config file is required"
exit 1
else
CONFIG_PATH="$3"
fi

if [ -z "$4" ]; then
echo "Using default number of clients"
INSTANCES=${1:-100}
elif [ "$4" -eq 0 ]; then
echo "Num clients must be greater than 0"
exit 1
else
INSTANCES=${4:-$4}
fi

echo "Running $MEETINGS meetings with $INSTANCES clients each"

rm -rf $LOGS_DIR
mkdir -p $LOGS_DIR

for conference_id in $(seq 1 $MEETINGS); do
parallel -j ${INSTANCES} "./qperf --conference_id $conference_id -i {} -n $INSTANCES -c $CONFIG_PATH --connect_uri $RELAY > $LOGS_DIR/t_$conference_id{}logs.txt 2>&1 &" ::: $(seq ${INSTANCES})
done
Loading