mudler / mudler/vllm.cpp

fix(ENG-MM-INPUT-PIPELINE): an encoder-cache entry goes evictable on its first free while the same request still needs a later occurrence

Open
#2,734 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
423
Forks
53
Avg merge
20h 26m
Merged PRs (30d)
310

Description

Row: ENG-MM-INPUT-PIPELINE (.agents/engine-matrix.md:77, state READY, spec specs/multimodal-track.md §3), the row that owns EncoderCacheManager per include/vllm/v1/core/sched/scheduler.h:91 (#2379). Resolved by line number before filing.

Found by wave PORTQ-7 (#2717) re-deriving PORT-NOW entry [280], upstream 3958a420f0 vllm#54284. Nothing was executed.

The defect

An encoder-cache entry becomes evictable as soon as one occurrence of its mm_hash is freed, even while the same request still holds another cached occurrence of it.

EncoderCacheManager::FreeEncoderInput (src/vllm/v1/core/encoder_cache_manager.cpp:98-115):

auto cit = cached_.find(mm_hash);
if (cit == cached_.end() || cit->second.empty()) return;
cit->second.erase(request_id);
if (cit->second.empty()) {
  FreeablePush(mm_hash, num_embeds);
  num_freeable_slots_ += num_embeds;
}

The request's reference is dropped on the first free, with no check for another still-cached occurrence. Upstream now returns early in exactly that case.

This tree cannot express the check as written. The manager keys request_cached_ids_ as request_id -> set<int> of input_ids (include/vllm/v1/core/encoder_cache_manager.h:75) and holds no input_id -> mm_hash map; the caller supplies mm_hash per input_id. Both production call sites do have the request's features to hand — Scheduler::free_encoder_inputs (src/vllm/v1/core/sched/scheduler.cpp:589-590) and Scheduler::free_request_encoder_inputs (:607-609) both hold request.mm_features — so the fix is available either by giving the manager the map or by moving the predicate to the caller.

Reachability

The manager is constructed unconditionally at scheduler.cpp:292, and FreeEncoderInput is reached from update_from_output (:1242) and from the finish, preempt and admission-failure paths (:341, :360, :939, :1303).

Relation to [#2686]: independent, not a duplicate

#2686 is PORTQ-5's finding on GetFreedMmHashes (encoder_cache_manager.cpp:117-121), which returns freed_ unfiltered where upstream returns [h for h in self.freed if h not in self.cached].

Different function, different upstream commit, different mechanism. #2686 tells the worker to drop a tensor the manager re-allocated later in the same pass; this one makes an entry evictable while the same request still needs a later occurrence of it. Fixing either does not fix the other.

Checked upstream as well, to rule out one being the other's parent: at 5559679229:vllm/v1/core/encoder_cache_manager.py:255-267 get_freed_mm_hashes was still unfiltered (so #2686's fix is some other in-range commit), while free_encoder_input at :217-242 lacked this occurrence check. Two separate distances.

They share a row, so they can share a branch — but they need separate red-first tests, and a reviewer must not accept one test as covering both.

The commit is the whole distance

free_encoder_input existed at 5559679229:vllm/v1/core/encoder_cache_manager.py:217-242 in exactly the shape this tree ported. Not a pre-pin hole. git log 3958a420f0..e126687a9a -- vllm/v1/core/encoder_cache_manager.py is empty: no later in-range commit touches the file.

Size

~10-20 product lines across encoder_cache_manager.{h,cpp} and possibly the two scheduler call sites, plus one ported test — upstream's is 37 lines: one request, two input_ids, one shared mm_hash, asserting the entry is not freeable after the first free. Port its parameters and assertions rather than re-deriving them.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with EncoderCacheManager::FreeEncoderInput in src/vllm/v1/core/encoder_cache_manager.cpp and its state in include/vllm/v1/core/encoder_cache_manager.h. Then inspect Scheduler::free_encoder_inputs and Scheduler::free_request_encoder_inputs in scheduler.cpp, along with the upstream 37-line test. Done means a shared mm_hash is not freeable after the first occurrence is freed, with a separate red-first test for this behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend, machine-learning
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.