Mooncake/mooncake-transfer-engine/benchmark/utils.cpp

166 lines
6.4 KiB
C++

// Copyright 2025 KVCache.AI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "utils.h"
#include <gflags/gflags.h>
#include <iostream>
DEFINE_string(seg_name, "", "Memory segment name for the local side");
DEFINE_string(seg_type, "DRAM",
"Memory segment type for the target side: DRAM|VRAM");
DEFINE_string(target_seg_name, "", "Memory segment name for the target side");
DEFINE_string(op_type, "read", "Operation type to benchmark: read|write|mix");
DEFINE_bool(check_consistency, false,
"Enable data consistency check after transfer.");
DEFINE_uint64(total_buffer_size, 1UL << 30,
"Total buffer size for testing (in bytes).");
DEFINE_uint64(start_block_size, 4096, "Start block size (in bytes).");
DEFINE_uint64(max_block_size, 1UL << 26, "Maximum block size (in bytes).");
DEFINE_uint64(start_batch_size, 1, "Start batch size (number of requests).");
DEFINE_uint64(max_batch_size, 1, "Maximum batch size (number of requests).");
DEFINE_int32(duration, 5, "Number of duration per test case.");
DEFINE_int32(start_num_threads, 1,
"Start number of concurrent worker threads.");
DEFINE_int32(max_num_threads, 1,
"Maximum number of concurrent worker threads.");
DEFINE_int32(local_gpu_id, 0, "Local GPU ID to be used, -1 for all GPUs");
DEFINE_int32(target_gpu_id, 0, "Target GPU ID to be used, -1 for all GPUs");
DEFINE_string(metadata_type, "p2p",
"Type of metadata service: p2p|etcd|redis|http");
DEFINE_string(metadata_url_list, "",
"List of metadata service URLs, comma-separated.");
DEFINE_int32(
rpc_server_port, 0,
"RPC server port used for p2p metadata service (0 = auto-select).");
DEFINE_string(xport_type, "", "Transport type: rdma|shm|mnnvl|gds|iouring");
DEFINE_string(backend, "tent", "Transport backend: classic|tent");
DEFINE_bool(notifi, false,
"Enable RDMA notification for performance measurement.");
namespace mooncake {
namespace tent {
std::string XferBenchConfig::seg_name;
std::string XferBenchConfig::seg_type;
std::string XferBenchConfig::target_seg_name;
std::string XferBenchConfig::op_type;
bool XferBenchConfig::check_consistency = false;
size_t XferBenchConfig::total_buffer_size = 0;
size_t XferBenchConfig::start_block_size = 0;
size_t XferBenchConfig::max_block_size = 0;
size_t XferBenchConfig::start_batch_size = 0;
size_t XferBenchConfig::max_batch_size = 0;
int XferBenchConfig::duration = 0;
int XferBenchConfig::max_num_threads = 0;
int XferBenchConfig::start_num_threads = 0;
std::string XferBenchConfig::metadata_type;
std::string XferBenchConfig::metadata_url_list;
int XferBenchConfig::rpc_server_port = 0;
std::string XferBenchConfig::xport_type;
std::string XferBenchConfig::backend;
bool XferBenchConfig::notifi = false;
int XferBenchConfig::local_gpu_id = 0;
int XferBenchConfig::target_gpu_id = 0;
void XferBenchConfig::loadFromFlags() {
seg_type = FLAGS_seg_type;
seg_name = FLAGS_seg_name;
target_seg_name = FLAGS_target_seg_name;
op_type = FLAGS_op_type;
check_consistency = FLAGS_check_consistency;
total_buffer_size = FLAGS_total_buffer_size;
start_block_size = FLAGS_start_block_size;
max_block_size = FLAGS_max_block_size;
start_batch_size = FLAGS_start_batch_size;
max_batch_size = FLAGS_max_batch_size;
start_num_threads = FLAGS_start_num_threads;
max_num_threads = FLAGS_max_num_threads;
duration = FLAGS_duration;
metadata_type = FLAGS_metadata_type;
metadata_url_list = FLAGS_metadata_url_list;
rpc_server_port = FLAGS_rpc_server_port;
xport_type = FLAGS_xport_type;
backend = FLAGS_backend;
notifi = FLAGS_notifi;
local_gpu_id = FLAGS_local_gpu_id;
target_gpu_id = FLAGS_target_gpu_id;
}
double XferMetricStats::percentile(double p) {
if (samples.empty()) return 0.0;
if (p <= 0) return min();
if (p >= 100) return max();
std::vector<double> sorted = samples;
std::sort(sorted.begin(), sorted.end());
double rank = (p / 100.0) * (sorted.size() - 1);
size_t idx = static_cast<size_t>(rank);
double frac = rank - idx;
if (idx + 1 < sorted.size()) {
return sorted[idx] * (1.0 - frac) + sorted[idx + 1] * frac;
} else {
return sorted[idx];
}
}
void printStatsHeader() {
// clang-format off
std::cout << std::left
<< std::setw(14) << "BlkSize (B)"
<< std::setw(8) << "Batch"
<< std::setw(14) << "BW (GB/S)"
<< std::setw(14) << "Avg Lat (us)"
<< std::setw(14) << "Avg Tx (us)"
<< std::setw(14) << "P99 Tx (us)"
<< std::setw(14) << "P999 Tx (us)"
<< std::endl;
std::cout << std::string(160, '-') << std::endl;
// clang-format on
}
void printStats(size_t block_size, size_t batch_size, XferBenchStats& stats,
int num_threads) {
size_t total_data_transferred = 0;
double avg_latency = 0, throughput_gb = 0;
auto num_ops = stats.transfer_duration.count();
double total_duration = stats.total_duration.avg();
total_data_transferred = ((block_size * batch_size) * num_ops);
avg_latency = (total_duration * num_threads / num_ops);
throughput_gb = (((double)total_data_transferred / (1000 * 1000 * 1000)) /
(total_duration / 1e6)); // In GB/Sec
// Tabulate print with fixed width for each string
// clang-format off
std::cout << std::left << std::fixed << std::setprecision(6)
<< std::setw(14) << block_size
<< std::setw(8) << batch_size
<< std::setw(14) << throughput_gb
<< std::setprecision(1)
<< std::setw(14) << avg_latency
<< std::setw(14) << stats.transfer_duration.avg()
<< std::setw(14) << stats.transfer_duration.p99()
<< std::setw(14) << stats.transfer_duration.p999()
<< std::endl;
// clang-format on
}
} // namespace tent
} // namespace mooncake