Files
wehub-resource-sync 770d92cb1f
Lint / lint (push) Waiting to run
Windows CI / Windows (push) Waiting to run
Build Docs / Deploy Docs (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:23:58 +08:00

186 lines
6.5 KiB
C++

/*!
* Copyright (c) 2023-2025 by Contributors
* \file serve/metrics.cc
*/
#include "metrics.h"
#include <tvm/runtime/logging.h>
#include <sstream>
namespace mlc {
namespace llm {
namespace serve {
tvm::ffi::json::Object TimeCost::AsJSON() const {
tvm::ffi::json::Object config;
config.Set("count", count);
if (count != 0) {
config.Set("mean", sum / count);
}
return config;
}
tvm::ffi::json::Object SpecDecodeMetrics::AsJSON() const {
tvm::ffi::json::Object metrics;
auto f_vector_to_array = [](const std::vector<int64_t>& vec) {
tvm::ffi::json::Array arr;
for (int64_t v : vec) {
arr.push_back(v);
}
return tvm::ffi::json::Value(arr);
};
metrics.Set("draft_count", f_vector_to_array(draft_count));
metrics.Set("accept_count", f_vector_to_array(accept_count));
TVM_FFI_ICHECK_EQ(draft_count.size(), accept_count.size());
// NOTE: label follows prometheus with full context
// so it can be flattened and used in metrics reoorting end point
tvm::ffi::json::Object accept_prob_metrics;
tvm::ffi::json::Object accept_rate_metrics;
tvm::ffi::json::Object accept_len_metrics;
double accept_len_value = 0;
for (size_t i = 0; i < draft_count.size(); ++i) {
std::ostringstream accept_prob_label;
accept_prob_label << "accept_prob{step=" << i << "}";
double accept_prob_value =
(static_cast<double>(accept_count[i]) / static_cast<double>(draft_count[i]));
accept_prob_metrics.Set(accept_prob_label.str(), accept_prob_value);
accept_len_value += accept_prob_value;
std::ostringstream accept_len_label;
accept_len_label << "accept_len{step=" << i << "}";
accept_len_metrics.Set(accept_len_label.str(), accept_len_value);
if (i != 0) {
std::ostringstream accept_rate_label;
accept_rate_label << "accept_rate{step=" << i << "}";
double accept_rate_value =
accept_count[i - 1] == 0
? 0.0f
: (static_cast<double>(accept_count[i]) / static_cast<double>(accept_count[i - 1]));
accept_rate_metrics.Set(accept_rate_label.str(), accept_rate_value);
}
}
metrics.Set("accept_prob", accept_prob_metrics);
metrics.Set("accept_rate", accept_rate_metrics);
metrics.Set("accept_len", accept_len_metrics);
return metrics;
}
tvm::ffi::json::Object RequestMetrics::AsJSON() const {
tvm::ffi::json::Object metrics;
metrics.Set("prompt_tokens", prompt_tokens);
metrics.Set("completion_tokens", completion_tokens);
metrics.Set("prefill_tokens", prefill_tokens);
metrics.Set("decode_tokens", decode_tokens);
metrics.Set("jump_forward_tokens", jump_forward_tokens);
if (prefill_tokens != 0) {
metrics.Set("prefill_tokens_per_s", prefill_tokens / this->GetPrefillTime());
}
if (decode_tokens != 0) {
metrics.Set("decode_tokens_per_s", decode_tokens / this->GetDecodeTime());
}
metrics.Set("end_to_end_latency_s", this->GetTotalTime());
metrics.Set("ttft_s", this->GetTTFT());
metrics.Set("inter_token_latency_s", this->GetInterTokenLatency());
return metrics;
}
std::string RequestMetrics::AsUsageJSONStr(bool include_extra) const {
tvm::ffi::json::Object usage;
usage.Set("prompt_tokens", prompt_tokens);
usage.Set("completion_tokens", completion_tokens);
usage.Set("total_tokens", prompt_tokens + completion_tokens);
if (include_extra) {
usage.Set("extra", this->AsJSON());
}
return tvm::ffi::json::Stringify(usage);
}
tvm::ffi::json::Object EngineMetrics::AsJSON() const {
tvm::ffi::json::Object metrics;
metrics.Set("engine_prefill_time_sum", engine_prefill_time_sum);
metrics.Set("engine_decode_time_sum", engine_decode_time_sum);
metrics.Set("engine_jump_forward_time_sum", engine_jump_forward_time_sum);
metrics.Set("prompt_tokens_sum", prompt_tokens_sum);
metrics.Set("completion_tokens_sum", completion_tokens_sum);
metrics.Set("prefill_tokens_sum", prefill_tokens_sum);
metrics.Set("decode_tokens_sum", decode_tokens_sum);
metrics.Set("jump_forward_tokens_sum", jump_forward_tokens_sum);
if (prefill_tokens_sum != 0) {
metrics.Set("prefill_tokens_per_s", prefill_tokens_sum / engine_prefill_time_sum);
}
if (engine_decode_time_sum != 0) {
metrics.Set("decode_tokens_per_s", decode_tokens_sum / engine_decode_time_sum);
}
metrics.Set("last_finished_request", last_finished_request.AsJSON());
if (!spec_decode.IsEmpty()) {
metrics.Set("spec_decode", spec_decode.AsJSON());
}
auto f_create_time_list = [](const std::vector<TimeCost>& time_list) {
tvm::ffi::json::Object result;
for (size_t i = 1; i < time_list.size(); ++i) {
const TimeCost& item = time_list[i];
if (item.count == 0) continue;
std::ostringstream label_mean;
label_mean << "mean{batch_size=" << i << "}";
double mean = item.sum / item.count;
result.Set(label_mean.str(), mean);
std::ostringstream label_count;
label_count << "count{batch_size=" << i << "}";
result.Set(label_count.str(), item.count);
}
return tvm::ffi::json::Value(result);
};
metrics.Set("decode_time_by_batch_size", f_create_time_list(decode_time_by_batch_size));
metrics.Set("draft_time_by_batch_size", f_create_time_list(draft_time_by_batch_size));
metrics.Set("verify_time_by_batch_size", f_create_time_list(verify_time_by_batch_size));
return metrics;
}
std::string EngineMetrics::AsUsageJSONStr() const {
tvm::ffi::json::Object usage;
// We return engine usage as a usage field according to the OpenAI API.
// To comply with the API, just set prompt_tokens, completion_tokens, and total_tokens to 0.
// And store the information in the extra field.
usage.Set("prompt_tokens", static_cast<int64_t>(0));
usage.Set("completion_tokens", static_cast<int64_t>(0));
usage.Set("total_tokens", static_cast<int64_t>(0));
usage.Set("extra", this->AsJSON());
return tvm::ffi::json::Stringify(usage);
}
void EngineMetrics::Reset() {
engine_prefill_time_sum = 0.0;
engine_decode_time_sum = 0.0;
engine_jump_forward_time_sum = 0;
prompt_tokens_sum = 0;
completion_tokens_sum = 0;
prefill_tokens_sum = 0;
decode_tokens_sum = 0;
jump_forward_tokens_sum = 0;
last_finished_request.Reset();
spec_decode.Reset();
decode_time_by_batch_size.clear();
draft_time_by_batch_size.clear();
verify_time_by_batch_size.clear();
decode_time_by_batch_size.resize(kEndFineGrainedTrackingBatchSize);
draft_time_by_batch_size.resize(kEndFineGrainedTrackingBatchSize);
verify_time_by_batch_size.resize(kEndFineGrainedTrackingBatchSize);
}
} // namespace serve
} // namespace llm
} // namespace mlc