Skip to content

V1 EngineCore: SIGTERM teardown runs two full-heap gen-2 GC passes (~4-7 s) to collect a few hundred objects #48620

Description

@arjmandi

Summary

On vLLM 0.24.0 (V1, default multiprocess), the EngineCore child performs two consecutive full-heap generation-2 GC passes during SIGTERM teardown, costing ~3.9–6.6 s to collect a few hundred objects in a process that is about to exit. Every offline LLM teardown pays this (benchmark iterations, test suites, repeated engine lifecycles).

Measurements

Observed via VLLM_GC_DEBUG='{"top_objects":20}' on two hosts, bare workload (Qwen/Qwen2.5-7B-Instruct, batch 32, greedy, no structured outputs), immediately after the shutdown sequence logs ([shutdown] EngineCore: trigger received signal=SIGTERMexiting busy loop):

1×A100-SXM4 40GB (Ubuntu 24.04, 30 vCPU):

(EngineCore pid=3684) GC took 4442.360ms to complete. Collected 377 objects (out of 1052645) in GC generation 2.
(EngineCore pid=3684) GC took 2164.036ms to complete. Collected 377 objects (out of 1035628) in GC generation 2.

1×H100 PCIe 80GB (Ubuntu 24.04, 26 vCPU):

GC took 2738.397ms to complete. Collected 377 objects (out of 1095452) in GC generation 2.
GC took 1391.236ms to complete. Collected 377 objects (out of 1078431) in GC generation 2.

Top collected-object classes are the startup graph, not request garbage: function (~150k), tuple, cell, dict, weakref.ReferenceType, vllm.v1.core.kv_cache_utils.KVCacheBlock (~23k).

During serving the same process's gen-2 tracked set is only ~24k–64k objects (freeze_gc_heap() working as intended); the ~1M-object heap becomes visible to the collector only on the shutdown path — consistent with gc.unfreeze() in EngineCore.shutdown() followed by full collection(s).

Why it may be worth changing

The process is exiting: OS teardown reclaims everything regardless, so 4–7 s of full-heap GC buys nothing on this path. Skipping the post-unfreeze full collections when the process is terminating (or leaving the heap frozen on exit paths) would make offline engine teardown ~seconds faster with no behavioral difference.

Repro

VLLM_GC_DEBUG='{"top_objects":20}' python -c "
from vllm import LLM, SamplingParams
llm = LLM(model='Qwen/Qwen2.5-7B-Instruct', max_model_len=2048)
llm.generate(['hello'], SamplingParams(max_tokens=16))
"
# watch the EngineCore GC lines after the SIGTERM shutdown sequence

Context: found while running the GC diagnostics for #48229 (multiprocess frozen-step investigation); split out per discussion there.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions