Skip to content

[Core]acceleratorclass cheapest selection policy logic update#547

Merged
pallasathena92 merged 1 commit into
mainfrom
yifeliu/cheapest-policy
Mar 27, 2026
Merged

[Core]acceleratorclass cheapest selection policy logic update#547
pallasathena92 merged 1 commit into
mainfrom
yifeliu/cheapest-policy

Conversation

@pallasathena92
Copy link
Copy Markdown
Collaborator

@pallasathena92 pallasathena92 commented Mar 26, 2026

What this PR does

  • Fix cheapest accelerator selection to compare costs within the same unit group instead of mixing different pricing types (e.g., "$/hour" vs "$/million-tokens")

  • Costs are now compared in priority order: hourly (spot + on-demand) → per-million-tokens → tier

  • Compare candidates in unit groups, not individual cost types:

    • Hourly group (SpotPerHour + PerHour): For each candidate, use SpotPerHour if available, else PerHour. Compare all candidates that have either. Pick cheapest.
      • Key insight: SpotPerHour and PerHour are both $/hour, so they CAN be compared directly. Spot is just a discounted hourly rate — if B's PerHour < A's SpotPerHour, B is genuinely cheaper.
    • Token group: If no candidate has hourly pricing, compare by PerMillionTokens. Pick cheapest.
    • Tier fallback: If no numeric cost, compare tier strings (low=1, medium=2, high=3). Pick lowest.

Why we need it

The previous selectCheapest used getCandidateCost to extract a single cost per candidate with a fixed priority (spot > on-demand > token > tier), then compared all candidates against each other. This caused two issues:

  • Cross-unit comparison: A candidate's "$/hour" could be compared against another's "$/million-tokens" — different units that produce meaningless results.
  • Unfair candidate exclusion: If candidate A had SpotPerHour and candidate B only had PerHour, the old per-candidate priority meant they were compared on different cost types (spot vs on-demand) rather than being recognized as the same unit ($/hour).

Fixes #518

How to test

Checklist

  • Tests added/updated (if applicable)
  • Docs updated (if applicable)
  • make test passes locally

@github-actions github-actions Bot added api API/Types changes in pkg/apis model-agent Model agent changes accelerator Accelerator class changes tests Test changes labels Mar 26, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the selectCheapest logic to group and prioritize accelerator costs by type (hourly, token-based, and tier) rather than using a single global priority. It also cleans up associated helper functions and tests. A significant concern was raised regarding the removal of documentation for empty AcceleratorClasses lists; there appears to be a logic discrepancy where some parts of the codebase treat an empty list as 'supporting all accelerators' while others treat it as 'supporting none'. This inconsistency should be resolved to ensure predictable behavior.

Comment thread pkg/apis/ome/v1beta1/servingruntime_types.go
@pallasathena92 pallasathena92 force-pushed the yifeliu/cheapest-policy branch 3 times, most recently from 310351b to f759002 Compare March 27, 2026 17:58
@pallasathena92 pallasathena92 force-pushed the yifeliu/cheapest-policy branch from f759002 to 7e35190 Compare March 27, 2026 18:00
@pallasathena92
Copy link
Copy Markdown
Collaborator Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the selectCheapest logic to prioritize cost types in groups (hourly, token, and tier) and updates SupportsAcceleratorClass to return false when requirements are undefined. It also modifies candidatesFromNames to preserve the order of names during deduplication. Feedback suggests refactoring the selectCheapest loop into a single pass to improve efficiency and code structure.

Comment on lines +330 to +387
// Group 1: Hourly pricing (SpotPerHour or PerHour — same unit $/hour)
var cheapest *v1beta1.AcceleratorClass
var cheapestCost *resource.Quantity
for i := range withCost {
c := &withCost[i]
var cost *resource.Quantity
if c.Spec.Cost.SpotPerHour != nil {
cost = c.Spec.Cost.SpotPerHour
} else if c.Spec.Cost.PerHour != nil {
cost = c.Spec.Cost.PerHour
}
if cost == nil {
continue
}
if cheapest == nil || compareCosts(cost, cheapestCost) < 0 {
cheapest = c
cheapestCost = cost
}
}
if cheapest != nil {
logger.Info("Cheapest selected", "name", cheapest.Name, "costType", "hourly")
return &cheapest.Name
}

logger.Info("Cheapest selected", "name", cheapest.candidate.Name, "costType", cheapest.costValue)
return &cheapest.candidate.Name
// Group 2: Token-based pricing ($/million tokens)
for i := range withCost {
c := &withCost[i]
cost := c.Spec.Cost.PerMillionTokens
if cost == nil {
continue
}
if cheapest == nil || compareCosts(cost, cheapestCost) < 0 {
cheapest = c
cheapestCost = cost
}
}
if cheapest != nil {
logger.Info("Cheapest selected", "name", cheapest.Name, "costType", "per-million-tokens")
return &cheapest.Name
}

// Group 3: Tier fallback (low=1, medium=2, high=3)
var cheapestTier int64
for i := range withCost {
c := &withCost[i]
if c.Spec.Cost.Tier == "" {
continue
}
tierVal := tierToNumeric(c.Spec.Cost.Tier)
if cheapest == nil || tierVal < cheapestTier {
cheapest = c
cheapestTier = tierVal
}
}
if cheapest != nil {
logger.Info("Cheapest selected", "name", cheapest.Name, "costType", "tier")
return &cheapest.Name
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation iterates over the withCost slice three times, once for each cost group (hourly, token, tier). This is slightly inefficient and leads to some code repetition.

Consider refactoring this to first categorize candidates into their respective cost groups in a single pass, and then find the cheapest within each group according to priority. This would improve efficiency by reducing passes over the data and could make the logic more modular.

For example:

// 1. Single pass to group candidates
hourlyCandidates := []v1beta1.AcceleratorClass{}
tokenCandidates := []v1beta1.AcceleratorClass{}
tierCandidates := []v1beta1.AcceleratorClass{}
for _, c := range withCost {
    // categorize and append to appropriate list based on cost type
}

// 2. Process groups in order of priority
if len(hourlyCandidates) > 0 {
    // find and return cheapest from hourlyCandidates
}

if len(tokenCandidates) > 0 {
    // find and return cheapest from tokenCandidates
}

if len(tierCandidates) > 0 {
    // find and return cheapest from tierCandidates
}

@pallasathena92 pallasathena92 merged commit 957f541 into main Mar 27, 2026
29 checks passed
@pallasathena92 pallasathena92 deleted the yifeliu/cheapest-policy branch March 27, 2026 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accelerator Accelerator class changes api API/Types changes in pkg/apis model-agent Model agent changes tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants