Milestone 2
This commit is contained in:
parent
14e3afdd7b
commit
866f0419ad
19 changed files with 2006 additions and 23 deletions
33
perf/README.md
Normal file
33
perf/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Performance tests
|
||||
|
||||
Milestone 0 perf test instructions are tracked in docs/milestone-0.md.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
Create/destroy microbenchmark (milestone 0/2):
|
||||
```
|
||||
./build/warppipe_perf --mode create-destroy --count 200 --type sink
|
||||
./build/warppipe_perf --mode create-destroy --count 100 --type both
|
||||
```
|
||||
|
||||
Registry snapshot + add/remove events (milestone 1):
|
||||
```
|
||||
./build/warppipe_perf --mode registry --count 1000 --events 100
|
||||
```
|
||||
|
||||
Optional format and loopback:
|
||||
```
|
||||
./build/warppipe_perf --mode create-destroy --count 200 --type sink --rate 48000 --channels 2
|
||||
./build/warppipe_perf --mode create-destroy --count 200 --type sink --target "some-node-name"
|
||||
```
|
||||
|
||||
Planned coverage:
|
||||
- Microbenchmark: connect -> create N -> destroy N
|
||||
- Target: subsecond for N=200 on warm PipeWire connection
|
||||
231
perf/warppipe_perf.cpp
Normal file
231
perf/warppipe_perf.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <warppipe/warppipe.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
struct Options {
|
||||
std::string mode = "create-destroy";
|
||||
std::string type = "sink";
|
||||
std::string target;
|
||||
uint32_t count = 200;
|
||||
uint32_t events = 100;
|
||||
uint32_t rate = 48000;
|
||||
uint32_t channels = 2;
|
||||
};
|
||||
|
||||
bool ParseUInt(const char* value, uint32_t* out) {
|
||||
if (!value || !out) {
|
||||
return false;
|
||||
}
|
||||
char* end = nullptr;
|
||||
unsigned long parsed = std::strtoul(value, &end, 10);
|
||||
if (!end || end == value) {
|
||||
return false;
|
||||
}
|
||||
*out = static_cast<uint32_t>(parsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintUsage() {
|
||||
std::cout << "warppipe_perf usage:\n"
|
||||
<< " --mode create-destroy|registry\n"
|
||||
<< " --type sink|source|both\n"
|
||||
<< " --count N (default 200, per-type when --type both)\n"
|
||||
<< " --events N (registry mode, default 100)\n"
|
||||
<< " --rate N (default 48000)\n"
|
||||
<< " --channels N (default 2)\n"
|
||||
<< " --target <node-name> (loopback target, optional)\n";
|
||||
}
|
||||
|
||||
bool ParseArgs(int argc, char* argv[], Options* options) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "--mode" && i + 1 < argc) {
|
||||
options->mode = argv[++i];
|
||||
} else if (arg == "--type" && i + 1 < argc) {
|
||||
options->type = argv[++i];
|
||||
} else if (arg == "--count" && i + 1 < argc) {
|
||||
if (!ParseUInt(argv[++i], &options->count)) {
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--events" && i + 1 < argc) {
|
||||
if (!ParseUInt(argv[++i], &options->events)) {
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--rate" && i + 1 < argc) {
|
||||
if (!ParseUInt(argv[++i], &options->rate)) {
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--channels" && i + 1 < argc) {
|
||||
if (!ParseUInt(argv[++i], &options->channels)) {
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--target" && i + 1 < argc) {
|
||||
options->target = argv[++i];
|
||||
} else if (arg == "--help" || arg == "-h") {
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (options->type != "sink" && options->type != "source" && options->type != "both") {
|
||||
return false;
|
||||
}
|
||||
if (options->mode != "create-destroy" && options->mode != "registry") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Prefix() {
|
||||
return "warppipe-perf-" + std::to_string(static_cast<long>(getpid()));
|
||||
}
|
||||
|
||||
double ToMillis(std::chrono::steady_clock::duration duration) {
|
||||
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(duration).count();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Options options;
|
||||
if (!ParseArgs(argc, argv, &options)) {
|
||||
PrintUsage();
|
||||
return 2;
|
||||
}
|
||||
|
||||
warppipe::ConnectionOptions connection;
|
||||
connection.application_name = "warppipe-perf";
|
||||
auto client = warppipe::Client::Create(connection);
|
||||
if (!client.ok()) {
|
||||
std::cerr << "warppipe_perf: failed to connect: " << client.status.message << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
warppipe::VirtualNodeOptions node_options;
|
||||
node_options.format.rate = options.rate;
|
||||
node_options.format.channels = options.channels;
|
||||
node_options.display_name = "warppipe-perf";
|
||||
node_options.group = "warppipe-perf";
|
||||
if (!options.target.empty()) {
|
||||
node_options.behavior = warppipe::VirtualBehavior::kLoopback;
|
||||
node_options.target_node = options.target;
|
||||
}
|
||||
|
||||
const bool is_source = options.type == "source";
|
||||
const bool is_both = options.type == "both";
|
||||
const std::string prefix = Prefix();
|
||||
|
||||
if (options.mode == "create-destroy") {
|
||||
std::vector<warppipe::NodeId> sink_nodes;
|
||||
std::vector<warppipe::NodeId> source_nodes;
|
||||
sink_nodes.reserve(options.count);
|
||||
if (is_both) {
|
||||
source_nodes.reserve(options.count);
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
if (!is_source || is_both) {
|
||||
for (uint32_t i = 0; i < options.count; ++i) {
|
||||
std::string name = prefix + "-sink-" + std::to_string(i);
|
||||
warppipe::Result<warppipe::VirtualSink> sink_result =
|
||||
client.value->CreateVirtualSink(name, node_options);
|
||||
if (!sink_result.ok()) {
|
||||
std::cerr << "create failed at " << i << ": " << sink_result.status.message << "\n";
|
||||
break;
|
||||
}
|
||||
sink_nodes.push_back(sink_result.value.node);
|
||||
}
|
||||
}
|
||||
if (is_source || is_both) {
|
||||
for (uint32_t i = 0; i < options.count; ++i) {
|
||||
std::string name = prefix + "-source-" + std::to_string(i);
|
||||
warppipe::Result<warppipe::VirtualSource> source_result =
|
||||
client.value->CreateVirtualSource(name, node_options);
|
||||
if (!source_result.ok()) {
|
||||
std::cerr << "create failed at " << i << ": " << source_result.status.message << "\n";
|
||||
break;
|
||||
}
|
||||
source_nodes.push_back(source_result.value.node);
|
||||
}
|
||||
}
|
||||
auto created = std::chrono::steady_clock::now();
|
||||
|
||||
for (const auto& node : sink_nodes) {
|
||||
client.value->RemoveNode(node);
|
||||
}
|
||||
for (const auto& node : source_nodes) {
|
||||
client.value->RemoveNode(node);
|
||||
}
|
||||
auto destroyed = std::chrono::steady_clock::now();
|
||||
|
||||
const double create_ms = ToMillis(created - start);
|
||||
const double destroy_ms = ToMillis(destroyed - created);
|
||||
const double total_ms = ToMillis(destroyed - start);
|
||||
const double ops = static_cast<double>(sink_nodes.size() + source_nodes.size());
|
||||
std::cout << "create_count=" << static_cast<size_t>(ops) << "\n"
|
||||
<< "create_ms=" << std::fixed << std::setprecision(2) << create_ms << "\n"
|
||||
<< "destroy_ms=" << destroy_ms << "\n"
|
||||
<< "total_ms=" << total_ms << "\n";
|
||||
if (total_ms > 0.0) {
|
||||
std::cout << "ops_per_sec=" << (ops / (total_ms / 1000.0)) << "\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (options.mode == "registry") {
|
||||
std::vector<warppipe::NodeId> nodes;
|
||||
nodes.reserve(options.count);
|
||||
for (uint32_t i = 0; i < options.count; ++i) {
|
||||
std::string name = prefix + "-node-" + std::to_string(i);
|
||||
auto result = client.value->CreateVirtualSink(name, node_options);
|
||||
if (!result.ok()) {
|
||||
std::cerr << "create failed at " << i << ": " << result.status.message << "\n";
|
||||
break;
|
||||
}
|
||||
nodes.push_back(result.value.node);
|
||||
}
|
||||
|
||||
auto list_start = std::chrono::steady_clock::now();
|
||||
auto listed = client.value->ListNodes();
|
||||
auto list_end = std::chrono::steady_clock::now();
|
||||
if (!listed.ok()) {
|
||||
std::cerr << "ListNodes failed: " << listed.status.message << "\n";
|
||||
}
|
||||
|
||||
auto events_start = std::chrono::steady_clock::now();
|
||||
for (uint32_t i = 0; i < options.events; ++i) {
|
||||
std::string name = prefix + "-event-" + std::to_string(i);
|
||||
auto result = client.value->CreateVirtualSink(name, node_options);
|
||||
if (!result.ok()) {
|
||||
break;
|
||||
}
|
||||
client.value->RemoveNode(result.value.node);
|
||||
}
|
||||
auto events_end = std::chrono::steady_clock::now();
|
||||
|
||||
for (const auto& node : nodes) {
|
||||
client.value->RemoveNode(node);
|
||||
}
|
||||
|
||||
const double list_ms = ToMillis(list_end - list_start);
|
||||
const double events_ms = ToMillis(events_end - events_start);
|
||||
std::cout << "registry_nodes=" << nodes.size() << "\n"
|
||||
<< "list_ms=" << std::fixed << std::setprecision(2) << list_ms << "\n"
|
||||
<< "event_ops=" << options.events << "\n"
|
||||
<< "event_ms=" << events_ms << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
PrintUsage();
|
||||
return 2;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue