Skip to content

Commit 69e0363

Browse files
committed
Replace custom GPU filter/scorer with generic endpoint-attribute plugins
The DCGM extractor now emits ScalarMetricValue (instead of a custom GPUUtilization type), making it compatible with the generic endpoint-attribute-filter and endpoint-attribute-scorer already in main. Signed-off-by: noalimoy <nlimoy@redhat.com>
1 parent 5b3cdd7 commit 69e0363

14 files changed

Lines changed: 169 additions & 644 deletions

File tree

cmd/epp/runner/runner.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ import (
108108
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/requesthandling/parsers/vllmhttp"
109109
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/filter/bylabel"
110110
endpointattributefilter "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/filter/endpointattribute"
111-
filtergpuutil "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/filter/gpuutilization"
112111
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/filter/prefixcacheaffinity"
113112
sessionaffinityfilter "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/filter/sessionaffinity"
114113
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/filter/sloheadroomtier"
@@ -121,7 +120,6 @@ import (
121120
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/scorer/activerequest"
122121
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/scorer/contextlengthaware"
123122
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/scorer/endpointattribute"
124-
scorergpuutil "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/scorer/gpuutilization"
125123
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/scorer/kvcacheutilization"
126124
latencyscorer "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/scorer/latency"
127125
"github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/scheduling/scorer/loadaware"
@@ -577,10 +575,6 @@ func (r *Runner) registerInTreePlugins() {
577575
fwkplugin.Register(srcdcgm.DCGMDataSourceType, srcdcgm.DCGMDataSourceFactory)
578576
fwkplugin.Register(attrgpu.DCGMExtractorType, extdcgm.DCGMExtractorFactory)
579577

580-
// GPU utilization filter/scorer
581-
fwkplugin.Register(filtergpuutil.PluginType, filtergpuutil.Factory)
582-
fwkplugin.Register(scorergpuutil.PluginType, scorergpuutil.Factory)
583-
584578
fwkplugin.Register(prefix.PrefixCacheScorerPluginType, prefix.PrefixCachePluginFactory)
585579
fwkplugin.Register(maxscore.MaxScorePickerType, maxscore.MaxScorePickerFactory)
586580
fwkplugin.Register(random.RandomPickerType, random.RandomPickerFactory)

deploy/config/sim-epp-gpu-config.yaml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,27 @@ plugins:
99
port: 9400
1010
- type: dcgm-extractor
1111
name: dcgm-extractor
12-
- type: gpu-utilization-filter
12+
- type: endpoint-attribute-filter
13+
name: gpu-utilization-filter
1314
parameters:
14-
threshold: 0.90
15-
- type: gpu-utilization-scorer
15+
attribute: "GPUUtilization/dcgm-extractor"
16+
onMissing: "Pass"
17+
fallbackOnEmpty: true
18+
algorithm:
19+
type: "threshold"
20+
threshold:
21+
operator: "LessThanOrEqual"
22+
value: 0.90
23+
- type: endpoint-attribute-scorer
24+
name: gpu-utilization-scorer
25+
parameters:
26+
attributeKey: "GPUUtilization/dcgm-extractor"
27+
algorithm:
28+
type: "linear_lower_is_better"
29+
normalization:
30+
fixedRange:
31+
min: 0.0
32+
max: 1.0
1633
- type: kv-cache-utilization-scorer
1734
- type: max-score-picker
1835
- type: single-profile-handler
Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
# GPU Attributes
22

3-
This package defines the data structures for GPU hardware metrics collected from NVIDIA DCGM Exporter.
3+
This package defines the data key for GPU hardware metrics collected from NVIDIA DCGM Exporter.
44

5-
## `GPUUtilization`
5+
## `GPUUtilizationDataKey`
66

7-
A normalized GPU compute utilization value in [0.0, 1.0], derived from `DCGM_FI_DEV_GPU_UTIL` (which reports 0-100). For multi-GPU pods the extractor aggregates across visible devices using `max`.
7+
- **Key string**: `GPUUtilization/dcgm-extractor`
8+
- **Value type**: `ScalarMetricValue` (from `attribute/metrics`)
89

9-
- **Key**: `GPUUtilizationDataKey` (`GPUUtilization/dcgm-extractor`)
10-
11-
## Producers
12-
13-
The following plugins produce this attribute:
14-
15-
- **`dcgm-extractor`** (Data Layer): Extracts GPU utilization from the DCGM Exporter Prometheus endpoint.
10+
For usage instructions, cluster prerequisites, and EPP config examples see
11+
[`extractor/dcgm/README.md`](../../../extractor/dcgm/README.md).

pkg/epp/framework/plugins/datalayer/attribute/gpu/data_types.go

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package gpu
1818

1919
import (
20-
fwkdl "github.com/llm-d/llm-d-router/pkg/epp/framework/interface/datalayer"
2120
"github.com/llm-d/llm-d-router/pkg/epp/framework/interface/plugin"
2221
)
2322

@@ -27,19 +26,6 @@ const (
2726
)
2827

2928
// GPUUtilizationDataKey identifies per-endpoint GPU utilization published
30-
// by the DCGM extractor and consumed by GPU-aware filters and scorers.
29+
// by the DCGM extractor and consumed by the generic endpoint-attribute
30+
// filter and scorer.
3131
var GPUUtilizationDataKey = plugin.NewDataKey("GPUUtilization", DCGMExtractorType)
32-
33-
// GPUUtilization is a normalized GPU compute utilization value in [0.0, 1.0],
34-
// derived from DCGM_FI_DEV_GPU_UTIL (which reports 0-100).
35-
// For multi-GPU pods the extractor aggregates across visible devices.
36-
type GPUUtilization float64
37-
38-
func (v GPUUtilization) Clone() fwkdl.Cloneable {
39-
return v
40-
}
41-
42-
// ReadGPUUtilization retrieves GPU utilization from an endpoint's AttributeMap.
43-
func ReadGPUUtilization(attrs fwkdl.AttributeMap, key string) (GPUUtilization, bool) {
44-
return fwkdl.ReadAttribute[GPUUtilization](attrs, key)
45-
}

pkg/epp/framework/plugins/datalayer/extractor/dcgm/README.md

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
The DCGM Extractor converts the Prometheus metrics response from a
66
`dcgm-data-source` into a per-endpoint GPU utilization attribute consumed by
7-
GPU-aware filters and scorers.
7+
the generic `endpoint-attribute-filter` and `endpoint-attribute-scorer`
8+
(configurable for any scalar attribute, not hardwired to GPU).
89

910
## What it does
1011

@@ -16,18 +17,77 @@ GPU-aware filters and scorers.
1617
- If the sample has no `pod` label, it is kept (sidecar / unlabeled payloads).
1718
4. Aggregates matching samples using `max` (highest-utilized GPU for the pod).
1819
5. Normalizes the value from 0-100 to [0.0, 1.0].
19-
6. Stores the result as a `GPUUtilization` attribute on the corresponding endpoint.
20+
6. Stores the result as `ScalarMetricValue` on the corresponding endpoint.
2021

2122
## Attributes produced
2223

23-
- `GPUUtilization` stored at attribute key `GPUUtilizationDataKey`
24-
(`GPUUtilization/dcgm-extractor`) on each endpoint.
25-
26-
```go
27-
key := attrgpu.GPUUtilizationDataKey.String()
28-
util, ok := attrgpu.ReadGPUUtilization(endpoint.GetAttributes(), key)
29-
```
24+
- **Type:** `ScalarMetricValue` (from `attribute/metrics`)
25+
- **Key string:** `GPUUtilization/dcgm-extractor`
3026

3127
## Configuration
3228

3329
No configuration parameters.
30+
31+
## Cluster prerequisites
32+
33+
- **DCGM Exporter** must be running on the cluster and accessible from the
34+
EPP. See [`source/dcgm/README.md`](../../source/dcgm/README.md) for
35+
data source configuration (port, DaemonSet vs sidecar, TLS).
36+
37+
## EPP config example
38+
39+
Use the generic `endpoint-attribute-filter` and `endpoint-attribute-scorer`
40+
to consume the produced attribute. The `attribute`/`attributeKey` parameter
41+
must match the key string above.
42+
43+
```yaml
44+
apiVersion: llm-d.ai/v1alpha1
45+
kind: EndpointPickerConfig
46+
plugins:
47+
- type: dcgm-data-source
48+
name: dcgm-source
49+
parameters:
50+
port: 9400
51+
- type: dcgm-extractor
52+
name: dcgm-extractor
53+
- type: endpoint-attribute-filter
54+
name: gpu-utilization-filter
55+
parameters:
56+
attribute: "GPUUtilization/dcgm-extractor"
57+
onMissing: "Pass"
58+
fallbackOnEmpty: true
59+
algorithm:
60+
type: "threshold"
61+
threshold:
62+
operator: "LessThanOrEqual"
63+
value: 0.90
64+
- type: endpoint-attribute-scorer
65+
name: gpu-utilization-scorer
66+
parameters:
67+
attributeKey: "GPUUtilization/dcgm-extractor"
68+
algorithm:
69+
type: "linear_lower_is_better"
70+
normalization:
71+
fixedRange:
72+
min: 0.0
73+
max: 1.0
74+
- type: kv-cache-utilization-scorer
75+
- type: max-score-picker
76+
- type: single-profile-handler
77+
- type: decode-filter
78+
dataLayer:
79+
sources:
80+
- pluginRef: dcgm-source
81+
extractors:
82+
- pluginRef: dcgm-extractor
83+
schedulingProfiles:
84+
- name: default
85+
plugins:
86+
- pluginRef: decode-filter
87+
- pluginRef: gpu-utilization-filter
88+
- pluginRef: gpu-utilization-scorer
89+
weight: 2.0
90+
- pluginRef: kv-cache-utilization-scorer
91+
weight: 1.0
92+
- pluginRef: max-score-picker
93+
```

pkg/epp/framework/plugins/datalayer/extractor/dcgm/extractor.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
fwkdl "github.com/llm-d/llm-d-router/pkg/epp/framework/interface/datalayer"
2727
fwkplugin "github.com/llm-d/llm-d-router/pkg/epp/framework/interface/plugin"
2828
attrgpu "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/gpu"
29+
attrmetrics "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/metrics"
2930
sourcemetrics "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/source/metrics"
3031
)
3132

@@ -101,14 +102,14 @@ func (e *Extractor) Extract(_ context.Context, in fwkdl.PollInput[sourcemetrics.
101102
return fmt.Errorf("metric %q present but has no samples for pod %q", gpuUtilMetricName, podName)
102103
}
103104

104-
normalized := attrgpu.GPUUtilization(maxUtil / 100.0)
105+
normalized := attrmetrics.ScalarMetricValue(maxUtil / 100.0)
105106
in.Endpoint.GetAttributes().Put(e.dk.String(), normalized)
106107
return nil
107108
}
108109

109110
// Produces declares the data key this extractor publishes.
110111
func (e *Extractor) Produces() map[fwkplugin.DataKey]any {
111-
return map[fwkplugin.DataKey]any{e.dk: attrgpu.GPUUtilization(0)}
112+
return map[fwkplugin.DataKey]any{e.dk: attrmetrics.ScalarMetricValue(0)}
112113
}
113114

114115
// sampleBelongsToPod reports whether m should contribute to this endpoint's

pkg/epp/framework/plugins/datalayer/extractor/dcgm/extractor_test.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828

2929
fwkdl "github.com/llm-d/llm-d-router/pkg/epp/framework/interface/datalayer"
3030
attrgpu "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/gpu"
31+
attrmetrics "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/attribute/metrics"
3132
sourcemetrics "github.com/llm-d/llm-d-router/pkg/epp/framework/plugins/datalayer/source/metrics"
3233
)
3334

@@ -215,7 +216,7 @@ func TestExtractorExtract(t *testing.T) {
215216
if diff := cmp.Diff(before, after); diff == "" {
216217
t.Error("expected attribute to be updated, but no change detected")
217218
}
218-
val, ok := attrgpu.ReadGPUUtilization(attr, key)
219+
val, ok := attrmetrics.ReadScalarMetricValue(attr, key)
219220
if !ok {
220221
t.Fatal("GPU utilization not found in attributes after extract")
221222
}
@@ -247,7 +248,23 @@ func TestFactory_SetsName(t *testing.T) {
247248
func TestProduces_DeclaresGPUUtilization(t *testing.T) {
248249
ext := NewDCGMExtractor()
249250
produced := ext.Produces()
250-
if _, ok := produced[attrgpu.GPUUtilizationDataKey]; !ok {
251+
val, ok := produced[attrgpu.GPUUtilizationDataKey]
252+
if !ok {
251253
t.Error("Produces must declare GPUUtilizationDataKey")
252254
}
255+
if _, isScalar := val.(attrmetrics.ScalarMetricValue); !isScalar {
256+
t.Errorf("Produces value type = %T, want ScalarMetricValue", val)
257+
}
258+
}
259+
260+
// TestAttributeKeyContract verifies that the attribute key string stored by
261+
// the extractor matches the value that generic endpoint-attribute-filter and
262+
// endpoint-attribute-scorer expect in their YAML config. A mismatch means
263+
// the filter/scorer silently skip all endpoints (attribute not found).
264+
func TestAttributeKeyContract(t *testing.T) {
265+
const expectedKey = "GPUUtilization/dcgm-extractor"
266+
got := attrgpu.GPUUtilizationDataKey.String()
267+
if got != expectedKey {
268+
t.Errorf("GPUUtilizationDataKey.String() = %q, want %q (must match YAML attribute config)", got, expectedKey)
269+
}
253270
}

pkg/epp/framework/plugins/scheduling/filter/gpuutilization/README.md

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)