Skip to content

Commit 7e4e012

Browse files
Tyndalllllputaopi7
andcommitted
[optimizer] report fixed-target MRC metrics
Co-authored-by: putaopi <72243274+putaopi7@users.noreply.github.com>
1 parent 16ef05f commit 7e4e012

11 files changed

Lines changed: 326 additions & 72 deletions

File tree

kv_cache_manager/optimizer/manager/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ cc_library(
6262
"//kv_cache_manager/optimizer/liteHit:hit_curve",
6363
"//kv_cache_manager/optimizer/liteHit:lite_hit",
6464
"//kv_cache_manager/optimizer/liteHit:request_preprocess",
65+
"//kv_cache_manager/optimizer/metrics:mrc_window",
6566
],
6667
)
6768

kv_cache_manager/optimizer/manager/online_runtime/online_optimizer_manager.cc

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -62,41 +62,6 @@ int64_t SaturatingMultiplyToInt64(uint64_t lhs, uint64_t rhs) {
6262
return static_cast<int64_t>(lhs * rhs);
6363
}
6464

65-
void AccumulateMrc(const RequestFact &fact, InstanceState &state) {
66-
for (const auto &segment : fact.hit_curve) {
67-
if (segment.run_length == 0) {
68-
continue;
69-
}
70-
const uint64_t end_required_blocks = segment.start_required_blocks + segment.run_length - 1;
71-
if (end_required_blocks + 1 >= state.mrc_interval_hit_count_deltas.size()) {
72-
state.mrc_interval_hit_count_deltas.resize(static_cast<std::size_t>(end_required_blocks + 2), 0);
73-
}
74-
++state.mrc_interval_hit_count_deltas[segment.start_required_blocks];
75-
--state.mrc_interval_hit_count_deltas[end_required_blocks + 1];
76-
state.mrc_interval_total_hits += segment.run_length;
77-
}
78-
}
79-
80-
uint64_t ComputeMrcRequiredBlocks(const InstanceState &state) {
81-
if (state.mrc_interval_total_hits == 0) {
82-
return 0;
83-
}
84-
85-
// ceil(total * 0.95), written without multiplication to avoid overflow.
86-
const uint64_t target_hits = state.mrc_interval_total_hits - state.mrc_interval_total_hits / 20;
87-
uint64_t accumulated_hits = 0;
88-
int64_t hits_at_required_blocks = 0;
89-
for (std::size_t required_blocks = 0; required_blocks < state.mrc_interval_hit_count_deltas.size();
90-
++required_blocks) {
91-
hits_at_required_blocks += state.mrc_interval_hit_count_deltas[required_blocks];
92-
accumulated_hits += static_cast<uint64_t>(hits_at_required_blocks);
93-
if (accumulated_hits >= target_hits) {
94-
return required_blocks;
95-
}
96-
}
97-
return 0;
98-
}
99-
10065
} // namespace
10166

10267
int64_t OnlineOptimizerManager::ComputeSizeForGroup(const std::vector<LocationSpecInfo> &specs,
@@ -529,7 +494,6 @@ ErrorCode OnlineOptimizerManager::TraceQuery(const std::string &instance_id,
529494
}
530495

531496
const RequestFact fact = state->lite_hit->ProcessRequest(normalized.block_keys, replay_timestamp_ns);
532-
AccumulateMrc(fact, *state);
533497
result.input_token_len = ClampToInt64(normalized.input_token_len);
534498

535499
const uint64_t block_size = static_cast<uint64_t>(state->instance_info->block_size());
@@ -554,6 +518,7 @@ ErrorCode OnlineOptimizerManager::TraceQuery(const std::string &instance_id,
554518

555519
if (state->instance_group->enable_theoretical_max_cache()) {
556520
const uint64_t max_hits = HitCurveProjector::ProjectInfinite(fact);
521+
state->mrc_window.Record(fact);
557522
result.max_hit_count = ClampToInt64(max_hits);
558523
result.max_hit_rate =
559524
normalized.input_token_len == 0 ? 0.0 : static_cast<double>(max_hits * block_size) / token_denominator;
@@ -737,21 +702,21 @@ ErrorCode OnlineOptimizerManager::ListInstances(const std::string &instance_grou
737702
ErrorCode OnlineOptimizerManager::TakeMrcMetrics(std::vector<MrcMetricInfo> &metrics) {
738703
std::shared_lock lock(instances_mutex_);
739704
metrics.clear();
740-
metrics.reserve(instances_.size());
741705

742706
for (const auto &[id, state] : instances_) {
743707
std::lock_guard<std::mutex> guard(state->mutex);
744-
if (state->linear_step != 0 || !state->lite_hit) {
708+
if (state->linear_step != 0 || !state->lite_hit || !state->instance_group->enable_theoretical_max_cache()) {
745709
continue;
746710
}
747711

748-
MrcMetricInfo metric;
749-
metric.instance_id = id;
750-
metric.capacity_bytes =
751-
SaturatingMultiplyToInt64(ComputeMrcRequiredBlocks(*state), static_cast<uint64_t>(state->size_full));
752-
metrics.push_back(std::move(metric));
753-
state->mrc_interval_hit_count_deltas.clear();
754-
state->mrc_interval_total_hits = 0;
712+
for (const auto &point : state->mrc_window.Take()) {
713+
MrcMetricInfo metric;
714+
metric.instance_id = id;
715+
metric.target_basis_points = point.target_basis_points;
716+
metric.capacity_bytes =
717+
SaturatingMultiplyToInt64(point.required_blocks, static_cast<uint64_t>(state->size_full));
718+
metrics.push_back(std::move(metric));
719+
}
755720
}
756721
return EC_OK;
757722
}
@@ -795,8 +760,7 @@ ErrorCode OnlineOptimizerManager::ResetStats(const std::string &instance_id) {
795760
state->total_input_tokens = 0;
796761
std::fill(state->total_hits_per_capacity.begin(), state->total_hits_per_capacity.end(), 0);
797762
state->total_max_hits = 0;
798-
state->mrc_interval_hit_count_deltas.clear();
799-
state->mrc_interval_total_hits = 0;
763+
state->mrc_window.Reset();
800764
KVCM_LOG_INFO("ResetStats OK: instance[%s]", instance_id.c_str());
801765
return EC_OK;
802766
}

kv_cache_manager/optimizer/manager/online_runtime/online_optimizer_manager.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "kv_cache_manager/optimizer/config/optimizer_instance_info.h"
1515
#include "kv_cache_manager/optimizer/index/online/cache_indexer.h"
1616
#include "kv_cache_manager/optimizer/liteHit/lite_hit.h"
17+
#include "kv_cache_manager/optimizer/metrics/mrc_window.h"
1718

1819
namespace kv_cache_manager {
1920

@@ -45,11 +46,7 @@ struct InstanceState {
4546
std::vector<int64_t> total_hits_per_capacity;
4647
int64_t total_max_hits = 0;
4748

48-
// Range-add difference array for the current reporting interval's
49-
// distribution of minimum LRU capacities required by theoretically
50-
// hittable blocks.
51-
std::vector<int64_t> mrc_interval_hit_count_deltas;
52-
uint64_t mrc_interval_total_hits = 0;
49+
MrcWindow mrc_window;
5350
};
5451

5552
struct TraceQueryResult {
@@ -84,6 +81,7 @@ struct HitAgeBucketRatio {
8481

8582
struct MrcMetricInfo {
8683
std::string instance_id;
84+
uint32_t target_basis_points = 0;
8785
int64_t capacity_bytes = 0;
8886
};
8987

kv_cache_manager/optimizer/metrics/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
package(default_visibility = ["//visibility:public"])
22

3+
cc_library(
4+
name = "mrc_window",
5+
srcs = ["mrc_window.cc"],
6+
hdrs = ["mrc_window.h"],
7+
deps = [
8+
"//kv_cache_manager/optimizer/liteHit:hit_curve",
9+
],
10+
)
11+
312
cc_library(
413
name = "optimizer_metrics_collector",
514
srcs = ["optimizer_metrics_collector.cc"],
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include "kv_cache_manager/optimizer/metrics/mrc_window.h"
2+
3+
#include <array>
4+
#include <iterator>
5+
#include <limits>
6+
7+
#include "kv_cache_manager/optimizer/liteHit/hit_curve.h"
8+
9+
namespace kv_cache_manager {
10+
11+
namespace {
12+
13+
constexpr uint32_t kBasisPointScale = 10000;
14+
constexpr std::array<uint32_t, 6> kTargetBasisPoints = {6000, 8000, 9000, 9500, 9900, 9950};
15+
16+
uint64_t SaturatingAdd(uint64_t lhs, uint64_t rhs) {
17+
return rhs > std::numeric_limits<uint64_t>::max() - lhs ? std::numeric_limits<uint64_t>::max() : lhs + rhs;
18+
}
19+
20+
uint64_t SaturatingMultiply(uint64_t lhs, uint64_t rhs) {
21+
if (lhs != 0 && rhs > std::numeric_limits<uint64_t>::max() / lhs) {
22+
return std::numeric_limits<uint64_t>::max();
23+
}
24+
return lhs * rhs;
25+
}
26+
27+
} // namespace
28+
29+
void MrcWindow::Record(const RequestFact &fact) {
30+
uint64_t request_hits = 0;
31+
for (const auto &segment : fact.hit_curve) {
32+
if (segment.run_length == 0) {
33+
continue;
34+
}
35+
36+
const uint64_t end_required_blocks = segment.start_required_blocks + segment.run_length - 1;
37+
++hit_count_deltas_[segment.start_required_blocks];
38+
--hit_count_deltas_[end_required_blocks + 1];
39+
request_hits = SaturatingAdd(request_hits, segment.run_length);
40+
}
41+
42+
total_hits_ = SaturatingAdd(total_hits_, request_hits);
43+
}
44+
45+
std::vector<MrcWindowPoint> MrcWindow::Take() {
46+
std::vector<MrcWindowPoint> curve;
47+
curve.reserve(kTargetBasisPoints.size());
48+
for (uint32_t target_basis_points : kTargetBasisPoints) {
49+
curve.push_back({target_basis_points, ComputeRequiredBlocks(target_basis_points)});
50+
}
51+
Reset();
52+
return curve;
53+
}
54+
55+
void MrcWindow::Reset() {
56+
hit_count_deltas_.clear();
57+
total_hits_ = 0;
58+
}
59+
60+
uint64_t MrcWindow::ComputeRequiredBlocks(uint32_t target_basis_points) const {
61+
if (total_hits_ == 0) {
62+
return 0;
63+
}
64+
65+
// ceil(total * target / 10000), split into quotient/remainder to avoid overflow.
66+
const uint64_t quotient = total_hits_ / kBasisPointScale;
67+
const uint64_t remainder = total_hits_ % kBasisPointScale;
68+
const uint64_t target_hits =
69+
quotient * target_basis_points + (remainder * target_basis_points + kBasisPointScale - 1) / kBasisPointScale;
70+
uint64_t accumulated_hits = 0;
71+
int64_t hits_at_required_blocks = 0;
72+
73+
for (auto it = hit_count_deltas_.begin(); it != hit_count_deltas_.end(); ++it) {
74+
hits_at_required_blocks += it->second;
75+
const auto next = std::next(it);
76+
if (hits_at_required_blocks <= 0 || next == hit_count_deltas_.end()) {
77+
continue;
78+
}
79+
80+
const uint64_t span = next->first - it->first;
81+
const uint64_t span_hits = SaturatingMultiply(static_cast<uint64_t>(hits_at_required_blocks), span);
82+
if (span_hits >= target_hits - accumulated_hits) {
83+
const uint64_t remaining_hits = target_hits - accumulated_hits;
84+
const uint64_t offset = (remaining_hits - 1) / static_cast<uint64_t>(hits_at_required_blocks);
85+
return it->first + offset;
86+
}
87+
accumulated_hits = SaturatingAdd(accumulated_hits, span_hits);
88+
}
89+
return 0;
90+
}
91+
92+
} // namespace kv_cache_manager
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <map>
5+
#include <vector>
6+
7+
namespace kv_cache_manager {
8+
9+
struct RequestFact;
10+
11+
struct MrcWindowPoint {
12+
uint32_t target_basis_points = 0;
13+
uint64_t required_blocks = 0;
14+
};
15+
16+
// Accumulates one reporting window of full-attention theoretical hits.
17+
// Synchronization is provided by the owning InstanceState mutex.
18+
class MrcWindow {
19+
public:
20+
void Record(const RequestFact &fact);
21+
std::vector<MrcWindowPoint> Take();
22+
void Reset();
23+
24+
private:
25+
uint64_t ComputeRequiredBlocks(uint32_t target_basis_points) const;
26+
27+
// Sparse difference points of required capacity -> theoretical hit count.
28+
std::map<uint64_t, int64_t> hit_count_deltas_;
29+
uint64_t total_hits_ = 0;
30+
};
31+
32+
} // namespace kv_cache_manager

kv_cache_manager/optimizer/metrics/optimizer_metrics_reporter.cc

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@
1515

1616
namespace kv_cache_manager {
1717

18+
namespace {
19+
20+
std::string FormatTargetHitRatePercent(uint32_t target_basis_points) {
21+
const uint32_t whole = target_basis_points / 100;
22+
const uint32_t fraction = target_basis_points % 100;
23+
if (fraction == 0) {
24+
return std::to_string(whole);
25+
}
26+
if (fraction % 10 == 0) {
27+
return std::to_string(whole) + "." + std::to_string(fraction / 10);
28+
}
29+
return std::to_string(whole) + "." + (fraction < 10 ? "0" : "") + std::to_string(fraction);
30+
}
31+
32+
} // namespace
33+
1834
#define DECLARE_METRICS(group, name) std::unique_ptr<kmonitor::MutableMetric> group##_##name##_metrics;
1935

2036
struct OptimizerMetricsReporter::KmonContext {
@@ -221,7 +237,6 @@ bool OptimizerMetricsReporter::InitMetrics() {
221237
REGISTER_GAUGE_METRIC(query, capacity_efficiency);
222238

223239
REGISTER_GAUGE_METRIC(trace, query_hit_age_bucket_ratio);
224-
225240
kmon_ctx_->mrc_metrics.reset(reporter->RegisterMetric("mrc", kmonitor::GAUGE, kmonitor::FATAL));
226241
if (!kmon_ctx_->mrc_metrics) {
227242
KVCM_LOG_ERROR("failed to register metric:[mrc]");
@@ -332,7 +347,8 @@ void OptimizerMetricsReporter::ReportInterval() {
332347
}
333348

334349
for (const auto &metric : mrc_metrics) {
335-
MetricsTags prom_tags = {{"instance_id", metric.instance_id}};
350+
MetricsTags prom_tags = {{"instance_id", metric.instance_id},
351+
{"target_hit_rate_percent", FormatTargetHitRatePercent(metric.target_basis_points)}};
336352
Gauge mrc = metrics_registry_->GetGauge("mrc", prom_tags);
337353
mrc = static_cast<double>(metric.capacity_bytes);
338354
}
@@ -379,7 +395,8 @@ void OptimizerMetricsReporter::ReportInterval() {
379395
}
380396

381397
for (const auto &metric : mrc_metrics) {
382-
MetricsTags base_tags = {{"instance_id", metric.instance_id}};
398+
MetricsTags base_tags = {{"instance_id", metric.instance_id},
399+
{"target_hit_rate_percent", FormatTargetHitRatePercent(metric.target_basis_points)}};
383400
kmonitor::MetricsTags tags = kmon_ctx_->GetKmonitorTags(base_tags);
384401
kmon_ctx_->mrc_metrics->Report(&tags, static_cast<double>(metric.capacity_bytes));
385402
}

kv_cache_manager/optimizer/test/BUILD

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,16 @@ cc_test(
273273
],
274274
)
275275

276+
cc_test(
277+
name = "MrcWindowTest",
278+
srcs = ["mrc_window_test.cc"],
279+
deps = [
280+
"//kv_cache_manager/common:unittest",
281+
"//kv_cache_manager/optimizer/liteHit:hit_curve",
282+
"//kv_cache_manager/optimizer/metrics:mrc_window",
283+
],
284+
)
285+
276286
cc_test(
277287
name = "OnlineOptimizerServerConfigTest",
278288
srcs = [

0 commit comments

Comments
 (0)