diff --git a/CMakeLists.txt b/CMakeLists.txt index 674a741..fe6fe38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 + $<$,$,$>: -Wpedantic -Wextra -Wall> + $<$: > +) + +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) @@ -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) diff --git a/README.md b/README.md index e124060..0df0825 100644 --- a/README.md +++ b/README.md @@ -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 ` profile. - -### > src/qperf_sub - -**qperf_sub** is a subscriber that consumes the tracks based on the `-c ` 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 @@ -42,4 +45,11 @@ Build the programs using the following: cmake --build build -j 4 ``` -The binaries will be under `./build` \ No newline at end of file +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. diff --git a/examples/config-audio.ini b/examples/config-audio.ini new file mode 100644 index 0000000..d093dd6 --- /dev/null +++ b/examples/config-audio.ini @@ -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 diff --git a/examples/config-av.ini b/examples/config-av.ini new file mode 100644 index 0000000..f234c14 --- /dev/null +++ b/examples/config-av.ini @@ -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 diff --git a/include/qperf_pub.hpp b/include/publisher_track_handler.hpp similarity index 64% rename from include/qperf_pub.hpp rename to include/publisher_track_handler.hpp index 222364d..e80efa8 100644 --- a/include/qperf_pub.hpp +++ b/include/publisher_track_handler.hpp @@ -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 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; @@ -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> track_handlers_; - std::mutex track_handlers_mutex_; - }; - } // namespace qperf diff --git a/include/qperf.hpp b/include/qperf.hpp index af7cdc3..36c9d04 100644 --- a/include/qperf.hpp +++ b/include/qperf.hpp @@ -1,9 +1,10 @@ #pragma once -#include +#include "inicpp.h" + #include -#include "inicpp.h" +#include namespace qperf { struct PerfConfig @@ -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 @@ -65,7 +66,10 @@ 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 = ""; @@ -73,11 +77,13 @@ namespace qperf { perf_config.test_name = section_name; - scenario_namespace = inif[section_name]["namespace"].as(); - scenario_name = inif[section_name]["name"].as(); + auto& section = inif[section_name]; + + scenario_namespace = fmt::vformat(section["namespace"].as(), fmt::make_format_args(instance_id)); + scenario_name = section["name"].as(); perf_config.full_track_name = MakeFullTrackName(scenario_namespace, scenario_name); - std::string track_mode_ini_str = inif[section_name]["track_mode"].as(); + std::string track_mode_ini_str = section["track_mode"].as(); if (track_mode_ini_str == "datagram") { perf_config.track_mode = quicr::TrackMode::kDatagram; } else if (track_mode_ini_str == "stream") { @@ -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(); - perf_config.ttl = inif[section_name]["ttl"].as(); - perf_config.transmit_interval = inif[section_name]["time_interval"].as(); - perf_config.objects_per_group = inif[section_name]["objs_per_group"].as(); - perf_config.bytes_per_group_start = inif[section_name]["bytes_per_group_start"].as(); - perf_config.bytes_per_group = inif[section_name]["bytes_per_group"].as(); - perf_config.start_delay = inif[section_name]["start_delay"].as(); - perf_config.total_test_time = inif[section_name]["total_test_time"].as(); - perf_config.total_transmit_time = perf_config.total_test_time - perf_config.start_delay; + perf_config.priority = section["priority"].as(); + perf_config.ttl = section["ttl"].as(); + perf_config.transmit_interval = section["time_interval"].as(); + perf_config.objects_per_group = section["objects_per_group"].as(); + perf_config.first_object_size = section["first_object_size"].as(); + perf_config.object_size = section["object_size"].as(); + perf_config.start_delay = section["start_delay"].as(); + perf_config.total_transmit_time = section["total_transmit_time"].as(); + perf_config.total_test_time = perf_config.total_transmit_time + perf_config.start_delay; SPDLOG_INFO("--------------------------------------------"); SPDLOG_INFO("Test config:"); @@ -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); diff --git a/include/qperf_sub.hpp b/include/subscriber_track_handler.hpp similarity index 70% rename from include/qperf_sub.hpp rename to include/subscriber_track_handler.hpp index 6a584d4..57ed0ab 100644 --- a/include/qperf_sub.hpp +++ b/include/subscriber_track_handler.hpp @@ -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 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; @@ -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> track_handlers_; - - std::mutex track_handlers_mutex_; - }; - } // namespace diff --git a/scripts/run_parallel_meetings.sh b/scripts/run_parallel_meetings.sh new file mode 100755 index 0000000..5a0a189 --- /dev/null +++ b/scripts/run_parallel_meetings.sh @@ -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 diff --git a/src/qperf_pub.cc b/src/publisher_track_handler.cpp similarity index 67% rename from src/qperf_pub.cc rename to src/publisher_track_handler.cpp index de171d3..26a3a39 100644 --- a/src/qperf_pub.cc +++ b/src/publisher_track_handler.cpp @@ -1,16 +1,15 @@ // SPDX-FileCopyrightText: Copyright (c) 2025 Cisco Systems // SPDX-License-Identifier: BSD-2-Clause +#include "publisher_track_handler.hpp" +#include "qperf.hpp" + #include #include -#include #include #include -#include "qperf_pub.hpp" - #include -#include #include #include @@ -28,10 +27,12 @@ namespace qperf { memset(&test_metrics_, '\0', sizeof(test_metrics_)); } - auto PerfPublishTrackHandler::Create(const std::string& section_name, ini::IniFile& inif) + std::shared_ptr PerfPublishTrackHandler::Create(const std::string& section_name, + ini::IniFile& inif, + std::uint32_t instance_id) { PerfConfig perf_config; - PopulateScenarioFields(section_name, inif, perf_config); + PopulateScenarioFields(section_name, instance_id, inif, perf_config); return std::shared_ptr(new PerfPublishTrackHandler(perf_config)); } @@ -233,8 +234,8 @@ namespace qperf { void PerfPublishTrackHandler::WriteThread() { - quicr::Bytes object_0_buffer(perf_config_.bytes_per_group_start); - quicr::Bytes object_not_0_buffer(perf_config_.bytes_per_group); + quicr::Bytes object_0_buffer(perf_config_.first_object_size); + quicr::Bytes object_not_0_buffer(perf_config_.object_size); for (std::size_t i = 0; i < object_0_buffer.size(); i++) { object_0_buffer[i] = i % 255; @@ -316,183 +317,4 @@ namespace qperf { write_thread_.join(); } } - - PerfPubClient::PerfPubClient(const quicr::ClientConfig& cfg, const std::string& configfile) - : quicr::Client(cfg) - , configfile_(configfile) - { - } - - void PerfPubClient::StatusChanged(Status status) - { - std::lock_guard _(track_handlers_mutex_); - switch (status) { - case Status::kReady: - SPDLOG_INFO("PerfPubClient - kReady"); - inif_.load(configfile_); - for (const auto& section_pair : inif_) { - const std::string& section_name = section_pair.first; - auto pub_handler = - track_handlers_.emplace_back(PerfPublishTrackHandler::Create(section_name, inif_)); - PublishTrack(pub_handler); - } - break; - - case Status::kNotReady: - SPDLOG_INFO("PerfPubClient - kNotReady"); - break; - case Status::kConnecting: - SPDLOG_INFO("PerfPubClient - kConnecting"); - break; - case Status::kDisconnecting: - SPDLOG_INFO("PerfPubClient - kDisconnecting"); - break; - case Status::kPendingServerSetup: - SPDLOG_INFO("PerfPubClient - kPendingSeverSetup"); - break; - - // All of the rest of these are 'errors' and will set terminate_. - case Status::kInternalError: - SPDLOG_INFO("PerfPubClient - kInternalError - terminate"); - terminate_ = true; - break; - case Status::kInvalidParams: - SPDLOG_INFO("PerfPubClient - kInvalidParams - terminate"); - terminate_ = true; - break; - case Status::kNotConnected: - SPDLOG_INFO("PerfPubClient - kNotConnected - terminate"); - terminate_ = true; - break; - case Status::kFailedToConnect: - SPDLOG_INFO("PerfPubClient - kFailedToConnect - terminate"); - terminate_ = true; - break; - default: - SPDLOG_INFO("PerfPubClient - UNKNOWN - Connection failed {0}", static_cast(status)); - terminate_ = true; - break; - } - } - - void PerfPubClient::MetricsSampled(const quicr::ConnectionMetrics&) {} - - bool PerfPubClient::GetTerminateStatus() - { - return terminate_; - } - - bool PerfPubClient::HandlersComplete() - { - std::lock_guard _(track_handlers_mutex_); - bool ret = true; - // Don't like this - should be dependent on a 'state' - if (track_handlers_.size() > 0) { - for (auto handler : track_handlers_) { - if (!handler->IsComplete()) { - ret = false; - break; - } - } - } else { - ret = false; - } - return ret; - } - - void PerfPubClient::Terminate() - { - std::lock_guard _(track_handlers_mutex_); - for (auto handler : track_handlers_) { - // Stop the handler writer thread... - handler->StopWriter(); - // Unpublish the track - UnpublishTrack(handler); - } - // we are done - terminate_ = true; - } } // namespace qperf - -bool terminate = false; - -void -HandleTerminateSignal(int) -{ - terminate = true; -} - -int -main(int argc, char** argv) -{ - // clang-format off - cxxopts::Options options("QPerf"); - options.add_options() - ("endpoint_id", "Name of the client", cxxopts::value()->default_value("perf@cisco.com")) - ("connect_uri", "Relay to connect to", cxxopts::value()->default_value("moq://localhost:1234")) - ("c,config", "Scenario config file", cxxopts::value()->default_value("./config.ini")) - ("h,help", "Print usage"); - // clang-format on - - cxxopts::ParseResult result; - - try { - result = options.parse(argc, argv); - } catch (const cxxopts::exceptions::exception& e) { - std::cerr << "Caught exception while parsing arguments: " << e.what() << std::endl; - return EXIT_FAILURE; - } - - if (result.count("help")) { - std::cerr << options.help() << std::endl; - return EXIT_SUCCESS; - } - - quicr::TransportConfig config; - config.tls_cert_filename = ""; - config.tls_key_filename = ""; - config.time_queue_max_duration = 5000; - config.use_reset_wait_strategy = false; - config.quic_qlog_path = ""; - - quicr::ClientConfig client_config; - client_config.endpoint_id = result["endpoint_id"].as(); - client_config.metrics_sample_ms = 5000; - client_config.transport_config = config; - client_config.connect_uri = result["connect_uri"].as(); - client_config.tick_service_sleep_delay_us = 50000; - - const auto logger = spdlog::stderr_color_mt("PERF"); - - auto config_file = result["config"].as(); - SPDLOG_INFO("--------------------------------------------"); - SPDLOG_INFO("Starting...pub"); - SPDLOG_INFO("\tconfig file {}", config_file); - SPDLOG_INFO("\tclient config:"); - SPDLOG_INFO("\t\tconnect_uri = {}", client_config.connect_uri); - SPDLOG_INFO("\t\tendpoint = {}", client_config.endpoint_id); - SPDLOG_INFO("--------------------------------------------"); - - std::signal(SIGINT, HandleTerminateSignal); - - auto client = std::make_shared(client_config, config_file); - - try { - client->Connect(); - } catch (const std::exception& e) { - SPDLOG_LOGGER_CRITICAL( - logger, "Failed to connect to relay '{0}' with exception: {1}", client_config.connect_uri, e.what()); - return EXIT_FAILURE; - } catch (...) { - SPDLOG_LOGGER_CRITICAL(logger, "Unexpected error connecting to relay"); - return EXIT_FAILURE; - } - - while (!terminate && !client->HandlersComplete()) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - client->Terminate(); - client->Disconnect(); - return EXIT_SUCCESS; -} diff --git a/src/qperf_meeting.cpp b/src/qperf_meeting.cpp new file mode 100644 index 0000000..abe1bb6 --- /dev/null +++ b/src/qperf_meeting.cpp @@ -0,0 +1,238 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025 Cisco Systems +// SPDX-License-Identifier: BSD-2-Clause + +#include "publisher_track_handler.hpp" +#include "subscriber_track_handler.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace qperf; + +class PerfClient : public quicr::Client +{ + public: + PerfClient(const quicr::ClientConfig& cfg, + const std::string& configfile, + std::uint32_t meeting_id, + std::uint32_t instances, + std::uint32_t instance_identifier) + : quicr::Client(cfg) + , configfile_(configfile) + , meeting_id_(meeting_id) + , instance_id_(instance_identifier) + , instances_(instances) + { + } + + void StatusChanged(Status status) + { + switch (status) { + case Status::kReady: + SPDLOG_INFO("Client status - kReady"); + inif_.load(configfile_); + + for (const auto& [section_name, _] : inif_) { + auto pub_handler = pub_track_handlers_.emplace_back( + PerfPublishTrackHandler::Create(section_name, inif_, instance_id_ + (meeting_id_ * 1000))); + PublishTrack(pub_handler); + } + + for (std::uint32_t i = 1; i <= instances_; ++i) { + if (i == instance_id_) { + continue; + } + + for (const auto& [section_name, _] : inif_) { + auto sub_handler = sub_track_handlers_.emplace_back( + PerfSubscribeTrackHandler::Create(section_name, inif_, i + (meeting_id_ * 1000))); + SubscribeTrack(sub_handler); + } + } + + break; + case Status::kNotReady: + SPDLOG_INFO("Client status - kNotReady"); + break; + case Status::kConnecting: + SPDLOG_INFO("Client status - kConnecting"); + break; + case Status::kNotConnected: + SPDLOG_INFO("Client status - kNotConnected"); + break; + case Status::kPendingServerSetup: + SPDLOG_INFO("Client status - kPendingSeverSetup"); + break; + + case Status::kFailedToConnect: + SPDLOG_ERROR("Client status - kFailedToConnect"); + terminate_ = true; + break; + case Status::kInternalError: + SPDLOG_ERROR("Client status - kInternalError"); + terminate_ = true; + break; + case Status::kInvalidParams: + SPDLOG_ERROR("Client status - kInvalidParams"); + terminate_ = true; + break; + default: + SPDLOG_ERROR("Connection failed {0}", static_cast(status)); + terminate_ = true; + break; + } + } + + bool HandlersComplete() + { + std::lock_guard _(mutex_); + defer(std::this_thread::sleep_for(std::chrono::milliseconds(100))); + + if (sub_track_handlers_.empty() || pub_track_handlers_.empty()) { + return false; + } + + for (auto handler : pub_track_handlers_) { + if (!handler->IsComplete()) { + return false; + } + } + + for (auto handler : sub_track_handlers_) { + if (!handler->IsComplete()) { + return false; + } + } + + return true; + } + + void Terminate() + { + std::lock_guard _(mutex_); + + for (auto handler : sub_track_handlers_) { + SPDLOG_INFO("unsubscribe track {}", handler->TestName()); + UnsubscribeTrack(handler); + } + + for (auto handler : pub_track_handlers_) { + handler->StopWriter(); + UnpublishTrack(handler); + } + + terminate_ = true; + } + + private: + bool terminate_; + std::string configfile_; + ini::IniFile inif_; + std::uint32_t meeting_id_; + std::uint32_t instance_id_; + std::uint32_t instances_; + + std::vector> sub_track_handlers_; + std::vector> pub_track_handlers_; + + std::mutex mutex_; +}; + +std::atomic_bool terminate = false; + +void +HandleTerminateSignal(int) +{ + terminate = true; +} + +int +main(int argc, char** argv) +{ + // clang-format off + cxxopts::Options options("QPerf"); + options.add_options() + ("endpoint_id", "Name of the client", cxxopts::value()->default_value("perf@cisco.com")) + ("connect_uri", "Relay to connect to", cxxopts::value()->default_value("moq://localhost:1234")) + ("meeting_id", "Meeting identifier", cxxopts::value()->default_value("1")) + ("n,instances", "Number of instances being run", cxxopts::value()) + ("i,instance_id", "Instance identifier number", cxxopts::value()) + ("c,config", "Scenario config file", cxxopts::value()) + ("h,help", "Print usage"); + // clang-format on + + cxxopts::ParseResult result; + + try { + result = options.parse(argc, argv); + } catch (const cxxopts::exceptions::exception& e) { + std::cerr << "Caught exception while parsing arguments: " << e.what() << std::endl; + return EXIT_FAILURE; + } + + if (result.count("help")) { + std::cerr << options.help() << std::endl; + return EXIT_SUCCESS; + } + + quicr::TransportConfig config; + config.tls_cert_filename = ""; + config.tls_key_filename = ""; + config.time_queue_max_duration = 5000; + config.use_reset_wait_strategy = false; + config.quic_qlog_path = ""; + + auto endpoint_instance_id = + result["endpoint_id"].as() + ":" + std::to_string(result["instance_id"].as()); + + quicr::ClientConfig client_config; + client_config.connect_uri = result["connect_uri"].as(); + client_config.endpoint_id = endpoint_instance_id; + client_config.metrics_sample_ms = 5000; + client_config.transport_config = config; + client_config.tick_service_sleep_delay_us = 50'000; + + auto log_id = endpoint_instance_id; + + const auto logger = spdlog::stderr_color_mt(log_id); + + const auto meeting_id = result["meeting_id"].as(); + const auto instance_id = result["instance_id"].as(); + const auto instances = result["instances"].as(); + + auto client = std::make_shared( + client_config, result["config"].as(), meeting_id, instances, instance_id); + + std::signal(SIGINT, HandleTerminateSignal); + + try { + client->Connect(); + } catch (const std::exception& e) { + SPDLOG_LOGGER_CRITICAL( + logger, "Failed to connect to relay '{0}' with exception: {1}", client_config.connect_uri, e.what()); + return EXIT_FAILURE; + } catch (...) { + SPDLOG_LOGGER_CRITICAL(logger, "Unexpected error connecting to relay"); + return EXIT_FAILURE; + } + + while (!terminate && !client->HandlersComplete()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + client->Terminate(); + client->Disconnect(); + + return EXIT_SUCCESS; +} diff --git a/src/qperf_pub.cpp b/src/qperf_pub.cpp new file mode 100644 index 0000000..6245004 --- /dev/null +++ b/src/qperf_pub.cpp @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025 Cisco Systems +// SPDX-License-Identifier: BSD-2-Clause + +#include "publisher_track_handler.hpp" +#include "qperf.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class PerfPubClient : public quicr::Client +{ + public: + PerfPubClient(const quicr::ClientConfig& cfg, const std::string& configfile) + : quicr::Client(cfg) + , configfile_(configfile) + { + } + + void StatusChanged(Status status) override + { + std::lock_guard _(track_handlers_mutex_); + switch (status) { + case Status::kReady: + SPDLOG_INFO("PerfPubClient - kReady"); + inif_.load(configfile_); + for (const auto& section_pair : inif_) { + const std::string& section_name = section_pair.first; + auto pub_handler = + track_handlers_.emplace_back(qperf::PerfPublishTrackHandler::Create(section_name, inif_, 0)); + PublishTrack(pub_handler); + } + break; + + case Status::kNotReady: + SPDLOG_INFO("PerfPubClient - kNotReady"); + break; + case Status::kConnecting: + SPDLOG_INFO("PerfPubClient - kConnecting"); + break; + case Status::kDisconnecting: + SPDLOG_INFO("PerfPubClient - kDisconnecting"); + break; + case Status::kPendingServerSetup: + SPDLOG_INFO("PerfPubClient - kPendingSeverSetup"); + break; + + // All of the rest of these are 'errors' and will set terminate_. + case Status::kInternalError: + SPDLOG_INFO("PerfPubClient - kInternalError - terminate"); + terminate_ = true; + break; + case Status::kInvalidParams: + SPDLOG_INFO("PerfPubClient - kInvalidParams - terminate"); + terminate_ = true; + break; + case Status::kNotConnected: + SPDLOG_INFO("PerfPubClient - kNotConnected - terminate"); + terminate_ = true; + break; + case Status::kFailedToConnect: + SPDLOG_INFO("PerfPubClient - kFailedToConnect - terminate"); + terminate_ = true; + break; + default: + SPDLOG_INFO("PerfPubClient - UNKNOWN - Connection failed {0}", static_cast(status)); + terminate_ = true; + break; + } + } + + void MetricsSampled(const quicr::ConnectionMetrics&) override {} + + bool GetTerminateStatus() { return terminate_; } + + bool HandlersComplete() + { + std::lock_guard _(track_handlers_mutex_); + bool ret = true; + // Don't like this - should be dependent on a 'state' + if (track_handlers_.size() > 0) { + for (auto handler : track_handlers_) { + if (!handler->IsComplete()) { + ret = false; + break; + } + } + } else { + ret = false; + } + return ret; + } + + void Terminate() + { + std::lock_guard _(track_handlers_mutex_); + for (auto handler : track_handlers_) { + // Stop the handler writer thread... + handler->StopWriter(); + // Unpublish the track + UnpublishTrack(handler); + } + // we are done + terminate_ = true; + } + + private: + bool terminate_; + std::string configfile_; + ini::IniFile inif_; + std::vector> track_handlers_; + std::mutex track_handlers_mutex_; +}; + +bool terminate = false; + +void +HandleTerminateSignal(int) +{ + terminate = true; +} + +int +main(int argc, char** argv) +{ + // clang-format off + cxxopts::Options options("QPerf"); + options.add_options() + ("endpoint_id", "Name of the client", cxxopts::value()->default_value("perf@cisco.com")) + ("connect_uri", "Relay to connect to", cxxopts::value()->default_value("moq://localhost:1234")) + ("c,config", "Scenario config file", cxxopts::value()->default_value("./config.ini")) + ("h,help", "Print usage"); + // clang-format on + + cxxopts::ParseResult result; + + try { + result = options.parse(argc, argv); + } catch (const cxxopts::exceptions::exception& e) { + std::cerr << "Caught exception while parsing arguments: " << e.what() << std::endl; + return EXIT_FAILURE; + } + + if (result.count("help")) { + std::cerr << options.help() << std::endl; + return EXIT_SUCCESS; + } + + quicr::TransportConfig config; + config.tls_cert_filename = ""; + config.tls_key_filename = ""; + config.time_queue_max_duration = 5000; + config.use_reset_wait_strategy = false; + config.quic_qlog_path = ""; + + quicr::ClientConfig client_config; + client_config.endpoint_id = result["endpoint_id"].as(); + client_config.metrics_sample_ms = 5000; + client_config.transport_config = config; + client_config.connect_uri = result["connect_uri"].as(); + client_config.tick_service_sleep_delay_us = 50000; + + const auto logger = spdlog::stderr_color_mt("PERF"); + + auto config_file = result["config"].as(); + SPDLOG_INFO("--------------------------------------------"); + SPDLOG_INFO("Starting...pub"); + SPDLOG_INFO("\tconfig file {}", config_file); + SPDLOG_INFO("\tclient config:"); + SPDLOG_INFO("\t\tconnect_uri = {}", client_config.connect_uri); + SPDLOG_INFO("\t\tendpoint = {}", client_config.endpoint_id); + SPDLOG_INFO("--------------------------------------------"); + + std::signal(SIGINT, HandleTerminateSignal); + + auto client = std::make_shared(client_config, config_file); + + try { + client->Connect(); + } catch (const std::exception& e) { + SPDLOG_LOGGER_CRITICAL( + logger, "Failed to connect to relay '{0}' with exception: {1}", client_config.connect_uri, e.what()); + return EXIT_FAILURE; + } catch (...) { + SPDLOG_LOGGER_CRITICAL(logger, "Unexpected error connecting to relay"); + return EXIT_FAILURE; + } + + while (!terminate && !client->HandlersComplete()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + client->Terminate(); + client->Disconnect(); + return EXIT_SUCCESS; +} diff --git a/src/qperf_sub.cpp b/src/qperf_sub.cpp new file mode 100644 index 0000000..8391698 --- /dev/null +++ b/src/qperf_sub.cpp @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025 Cisco Systems +// SPDX-License-Identifier: BSD-2-Clause + +#include "subscriber_track_handler.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class PerfSubClient : public quicr::Client +{ + public: + PerfSubClient(const quicr::ClientConfig& cfg, const std::string& configfile, std::uint32_t test_identifier) + : quicr::Client(cfg) + , configfile_(configfile) + , test_identifier_(test_identifier) + { + } + + void StatusChanged(Status status) override + { + switch (status) { + case Status::kReady: + SPDLOG_INFO("Client status - kReady"); + inif_.load(configfile_); + for (const auto& section_pair : inif_) { + const std::string& section_name = section_pair.first; + SPDLOG_INFO("Starting test - {}", section_name); + auto sub_handler = track_handlers_.emplace_back( + qperf::PerfSubscribeTrackHandler::Create(section_name, inif_, test_identifier_)); + SubscribeTrack(sub_handler); + } + break; + case Status::kNotReady: + SPDLOG_INFO("Client status - kNotReady"); + break; + case Status::kConnecting: + SPDLOG_INFO("Client status - kConnecting"); + break; + case Status::kNotConnected: + SPDLOG_INFO("Client status - kNotConnected"); + break; + case Status::kPendingServerSetup: + SPDLOG_INFO("Client status - kPendingSeverSetup"); + break; + + case Status::kFailedToConnect: + SPDLOG_ERROR("Client status - kFailedToConnect"); + terminate_ = true; + break; + case Status::kInternalError: + SPDLOG_ERROR("Client status - kInternalError"); + terminate_ = true; + break; + case Status::kInvalidParams: + SPDLOG_ERROR("Client status - kInvalidParams"); + terminate_ = true; + break; + default: + SPDLOG_ERROR("Connection failed {0}", static_cast(status)); + terminate_ = true; + break; + } + } + + void MetricsSampled(const quicr::ConnectionMetrics&) override {} + + bool HandlersComplete() + { + std::lock_guard _(track_handlers_mutex_); + bool ret = true; + // Don't like this - should be dependent on a 'state' + if (track_handlers_.size() > 0) { + for (auto handler : track_handlers_) { + if (!handler->IsComplete()) { + ret = false; + } + } + } else { + ret = false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return ret; + } + + void Terminate() + { + std::lock_guard _(track_handlers_mutex_); + for (auto handler : track_handlers_) { + // Unpublish the track + SPDLOG_INFO("unsubscribe track {}", handler->TestName()); + UnsubscribeTrack(handler); + } + // we are done + terminate_ = true; + } + + private: + bool terminate_; + std::string configfile_; + ini::IniFile inif_; + std::uint32_t test_identifier_; + + std::vector> track_handlers_; + + std::mutex track_handlers_mutex_; +}; + +bool terminate = false; + +void +HandleTerminateSignal(int) +{ + terminate = true; +} + +int +main(int argc, char** argv) +{ + // clang-format off + cxxopts::Options options("QPerf"); + options.add_options() + ("endpoint_id", "Name of the client", cxxopts::value()->default_value("perf@cisco.com")) + ("connect_uri", "Relay to connect to", cxxopts::value()->default_value("moq://localhost:1234")) + ("i,test_id", "Test idenfiter number", cxxopts::value()->default_value("1")) + ("c,config", "Scenario config file", cxxopts::value()) + ("h,help", "Print usage"); + // clang-format on + + cxxopts::ParseResult result; + + try { + result = options.parse(argc, argv); + } catch (const cxxopts::exceptions::exception& e) { + std::cerr << "Caught exception while parsing arguments: " << e.what() << std::endl; + return EXIT_FAILURE; + } + + if (result.count("help")) { + std::cerr << options.help() << std::endl; + return EXIT_SUCCESS; + } + + quicr::TransportConfig config; + config.tls_cert_filename = ""; + config.tls_key_filename = ""; + config.time_queue_max_duration = 5000; + config.use_reset_wait_strategy = false; + config.quic_qlog_path = ""; + + auto endpoint_test_id = + result["endpoint_id"].as() + ":" + std::to_string(result["test_id"].as()); + + quicr::ClientConfig client_config; + client_config.connect_uri = result["connect_uri"].as(); + client_config.endpoint_id = endpoint_test_id; + client_config.metrics_sample_ms = 5000; + client_config.transport_config = config; + client_config.tick_service_sleep_delay_us = 50'000; + + auto log_id = endpoint_test_id; + + const auto logger = spdlog::stderr_color_mt(log_id); + + auto test_identifier = result["test_id"].as(); + + auto client = std::make_shared(client_config, result["config"].as(), test_identifier); + + std::signal(SIGINT, HandleTerminateSignal); + + try { + client->Connect(); + } catch (const std::exception& e) { + SPDLOG_LOGGER_CRITICAL( + logger, "Failed to connect to relay '{0}' with exception: {1}", client_config.connect_uri, e.what()); + return EXIT_FAILURE; + } catch (...) { + SPDLOG_LOGGER_CRITICAL(logger, "Unexpected error connecting to relay"); + return EXIT_FAILURE; + } + + while (!terminate && !client->HandlersComplete()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + client->Terminate(); + client->Disconnect(); + + return EXIT_SUCCESS; +} diff --git a/src/qperf_sub.cc b/src/subscriber_track_handler.cpp similarity index 68% rename from src/qperf_sub.cc rename to src/subscriber_track_handler.cpp index a9c917c..ba45bcc 100644 --- a/src/qperf_sub.cc +++ b/src/subscriber_track_handler.cpp @@ -1,24 +1,23 @@ // SPDX-FileCopyrightText: Copyright (c) 2025 Cisco Systems // SPDX-License-Identifier: BSD-2-Clause +#include "subscriber_track_handler.hpp" +#include "qperf.hpp" + #include #include -#include #include #include #include #include #include -#include #include #include #include #include #include -#include "qperf_sub.hpp" - namespace qperf { std::atomic_bool terminate = false; @@ -58,13 +57,13 @@ namespace qperf { { } - auto PerfSubscribeTrackHandler::Create(const std::string& section_name, - ini::IniFile& inif, - std::uint32_t test_identifier) + std::shared_ptr PerfSubscribeTrackHandler::Create(const std::string& section_name, + ini::IniFile& inif, + std::uint32_t instance_id) { PerfConfig perf_config; - PopulateScenarioFields(section_name, inif, perf_config); - return std::shared_ptr(new PerfSubscribeTrackHandler(perf_config, test_identifier)); + PopulateScenarioFields(section_name, instance_id, inif, perf_config); + return std::shared_ptr(new PerfSubscribeTrackHandler(perf_config, instance_id)); } void PerfSubscribeTrackHandler::StatusChanged(Status status) @@ -287,7 +286,7 @@ namespace qperf { if (test_mode_ == qperf::TestMode::kRunning) { std::uint64_t delta_bytes = metrics_.bytes_received - last_bytes_; - std::uint64_t bitrate = ((delta_bytes) * 8) / diff.count(); + std::uint64_t bitrate = ((delta_bytes) * 8) / std::max(diff.count(), std::int64_t(1)); metric_samples_ += 1; bitrate_total_ += bitrate; if (min_bitrate_ == 0) { @@ -313,179 +312,4 @@ namespace qperf { last_metric_time_ = std::chrono::time_point_cast(std::chrono::system_clock::now()); last_bytes_ = metrics.bytes_received; } - - /** - * @brief MoQ client - * @details Implementation of the MoQ Client - */ - - PerfSubClient::PerfSubClient(const quicr::ClientConfig& cfg, - const std::string& configfile, - std::uint32_t test_identifier) - : quicr::Client(cfg) - , configfile_(configfile) - , test_identifier_(test_identifier) - { - } - - void PerfSubClient::StatusChanged(Status status) - { - switch (status) { - case Status::kReady: - SPDLOG_INFO("Client status - kReady"); - inif_.load(configfile_); - for (const auto& section_pair : inif_) { - const std::string& section_name = section_pair.first; - SPDLOG_INFO("Starting test - {}", section_name); - auto sub_handler = track_handlers_.emplace_back( - PerfSubscribeTrackHandler::Create(section_name, inif_, test_identifier_)); - SubscribeTrack(sub_handler); - } - break; - case Status::kNotReady: - SPDLOG_INFO("Client status - kNotReady"); - break; - case Status::kConnecting: - SPDLOG_INFO("Client status - kConnecting"); - break; - case Status::kNotConnected: - SPDLOG_INFO("Client status - kNotConnected"); - break; - case Status::kPendingServerSetup: - SPDLOG_INFO("Client status - kPendingSeverSetup"); - break; - - case Status::kFailedToConnect: - SPDLOG_ERROR("Client status - kFailedToConnect"); - terminate_ = true; - break; - case Status::kInternalError: - SPDLOG_ERROR("Client status - kInternalError"); - terminate_ = true; - break; - case Status::kInvalidParams: - SPDLOG_ERROR("Client status - kInvalidParams"); - terminate_ = true; - break; - default: - SPDLOG_ERROR("Connection failed {0}", static_cast(status)); - terminate_ = true; - break; - } - } - - bool PerfSubClient::HandlersComplete() - { - std::lock_guard _(track_handlers_mutex_); - bool ret = true; - // Don't like this - should be dependent on a 'state' - if (track_handlers_.size() > 0) { - for (auto handler : track_handlers_) { - if (!handler->IsComplete()) { - ret = false; - } - } - } else { - ret = false; - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - return ret; - } - - void PerfSubClient::Terminate() - { - std::lock_guard _(track_handlers_mutex_); - for (auto handler : track_handlers_) { - // Unpublish the track - SPDLOG_INFO("unsubscribe track {}", handler->TestName()); - UnsubscribeTrack(handler); - } - // we are done - terminate_ = true; - } - -} - -bool terminate = false; - -void -HandleTerminateSignal(int) -{ - terminate = true; -} - -int -main(int argc, char** argv) -{ - // clang-format off - cxxopts::Options options("QPerf"); - options.add_options() - ("endpoint_id", "Name of the client", cxxopts::value()->default_value("perf@cisco.com")) - ("connect_uri", "Relay to connect to", cxxopts::value()->default_value("moq://localhost:1234")) - ("i,test_id", "Test idenfiter number", cxxopts::value()->default_value("1")) - ("c,config", "Scenario config file", cxxopts::value()) - ("h,help", "Print usage"); - // clang-format on - - cxxopts::ParseResult result; - - try { - result = options.parse(argc, argv); - } catch (const cxxopts::exceptions::exception& e) { - std::cerr << "Caught exception while parsing arguments: " << e.what() << std::endl; - return EXIT_FAILURE; - } - - if (result.count("help")) { - std::cerr << options.help() << std::endl; - return EXIT_SUCCESS; - } - - quicr::TransportConfig config; - config.tls_cert_filename = ""; - config.tls_key_filename = ""; - config.time_queue_max_duration = 5000; - config.use_reset_wait_strategy = false; - config.quic_qlog_path = ""; - - auto endpoint_test_id = - result["endpoint_id"].as() + ":" + std::to_string(result["test_id"].as()); - - quicr::ClientConfig client_config; - client_config.connect_uri = result["connect_uri"].as(); - client_config.endpoint_id = endpoint_test_id; - client_config.metrics_sample_ms = 5000; - client_config.transport_config = config; - client_config.tick_service_sleep_delay_us = 50'000; - - auto log_id = endpoint_test_id; - - const auto logger = spdlog::stderr_color_mt(log_id); - - auto test_identifier = result["test_id"].as(); - - auto client = - std::make_shared(client_config, result["config"].as(), test_identifier); - - std::signal(SIGINT, HandleTerminateSignal); - - try { - client->Connect(); - } catch (const std::exception& e) { - SPDLOG_LOGGER_CRITICAL( - logger, "Failed to connect to relay '{0}' with exception: {1}", client_config.connect_uri, e.what()); - return EXIT_FAILURE; - } catch (...) { - SPDLOG_LOGGER_CRITICAL(logger, "Unexpected error connecting to relay"); - return EXIT_FAILURE; - } - - while (!terminate && !client->HandlersComplete()) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - client->Terminate(); - client->Disconnect(); - - return EXIT_SUCCESS; } diff --git a/templates/config-audio.ini b/templates/config-audio.ini deleted file mode 100644 index cc67f21..0000000 --- a/templates/config-audio.ini +++ /dev/null @@ -1,13 +0,0 @@ -[Audio] -namespace = perf/1 ; track namespace -name = 1 ; track name -track_mode = datagram ; track mode {datagram,stream} -priority = 2 ; priority (0-255) -ttl = 5000 ; ttl in ms -time_interval = 20 ; transmit interval in floating point ms -objs_per_group = 1 ; objects per group count >=1 -bytes_per_group_start = 120 ; size of a group 0 object -bytes_per_group = 120 ; size of a group <> 0 object -start_delay = 5000 ; start delay in ms - after subscribes -total_test_time = 40000 ; total transmit time in ms - including startdelay -; (not configured): total_transmit_time - is calculated total_transmit_time = total_test_time - start_delay diff --git a/templates/config-av.ini b/templates/config-av.ini deleted file mode 100644 index 2a22f4c..0000000 --- a/templates/config-av.ini +++ /dev/null @@ -1,26 +0,0 @@ -[Audio] -namespace = perf/1 ; track namespace -name = 1 ; track name -track_mode = datagram ; track mode {datagram,stream} -priority = 2 ; priority (0-255) -ttl = 5000 ; ttl in ms -time_interval = 20 ; transmit interval in floating point ms -objs_per_group = 1 ; objects per group count >=1 -bytes_per_group_start = 120 ; size of a group 0 object -bytes_per_group = 120 ; size of a group <> 0 object -start_delay = 5000 ; start delay in ms - after subscribes -total_test_time = 40000 ; total transmit time in ms - including start delay -; (not configured): total_transmit_time - is calculated total_transmit_time = total_test_time - start_delay - -[360p Video] -namespace = perf/2 ; track namespace -name = 1 ; track name -track_mode = stream ; track mode {datagram,stream} -priority = 3 ; priority (0-255) -ttl = 5000 ; ttl in ms -time_interval = 33.33 ; transmit interval in floating point ms -objs_per_group = 150 ; objects per group count >=1 -bytes_per_group_start = 21333 ; size of a group 0 object -bytes_per_group = 2666 ; size of a group <> 0 object -start_delay = 5000 ; start delay in ms - after subscribes -total_test_time = 40000 ; total transmit time in ms - including start delay diff --git a/templates/config.template.ini b/templates/config.template.ini index 36415cb..804f81f 100644 --- a/templates/config.template.ini +++ b/templates/config.template.ini @@ -1,25 +1,12 @@ -[TRACK 1] -namespace = perf/1 ; track namespace - can be the same or different than other section tracks -name = 1 ; track name - Should be different than other section tracks -track_mode = datagram ; track mode {datagram,stream} -priority = 1 ; priority (0-255) -ttl = 5000 ; ttl in ms -time_interval = 20.00 ; transmit interval in floating point ms -objs_per_group = 1 ; objects per group count >=1 -bytes_per_group_start = 60 ; size of a group 0 object -bytes_per_group = 60 ; size of a group <> 0 object -start_delay = 10000 ; start delay in ms - after subscribes -total_test_time = 35000 ; total transmit time in ms - including start delay - -[TRACK 2] -namespace = perf/1 ; track namespace - can be the same or different than other section tracks -name = 2 ; track name - Should be different than other section tracks -track_mode = stream ; track mode {datagram,stream} -priority = 2 ; priority (0-255) -ttl = 5000 ; ttl in ms -time_interval = 33.33 ; transmit interval in floating point ms -objs_per_group = 150 ; objects per group count >=1 -bytes_per_group_start = 22000 ; size of a group 0 object -bytes_per_group = 2500 ; size of a group <> 0 object -start_delay = 10000 ; start delay in ms - after subscribes -total_test_time = 35000 ; total transmit time in ms - including start delay +[TRACK] +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