Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions ggml/src/ggml-cuda/common.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -1459,11 +1459,12 @@ struct ggml_backend_cuda_context {
// Map from graph key to cuda_graph - allows multiple graphs per context when the
// computation is split across CPU/GPU (e.g., with --n-cpu-moe), and when the same
// split is called with different tensor shapes (e.g. a speculative verify batch)
std::unordered_map<uint64_t, std::unique_ptr<ggml_cuda_graph>> cuda_graphs;

// a cuda graph instance is only valid for the shapes it captured, so a caller that
// alternates shapes needs one instance per shape to stay on the graph path
static const size_t max_cuda_graphs = 64;
// alternates shapes needs one instance per shape to stay on the graph path. the map is
// bounded only by the time based sweep below, as upstream does: tensor split mode alone
// needs about 2 * n_layers + 1 graphs per device and shape, so any count cap small enough
// to matter evicts graphs that are still in use and re-captures them on every token
std::unordered_map<uint64_t, std::unique_ptr<ggml_cuda_graph>> cuda_graphs;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep a resource bound on the shape cache

When CUDA or HIP graphs are enabled in a server and clients generate many distinct prompt or batch shapes within the 10-second retention window, each shape and tensor-split segment can now add a cache entry, including a full node_props vector and potentially a graph executable after warmup. The time-based sweep does not impose a count or byte limit, so a sufficiently fast burst can exhaust host or device memory before any entry becomes old enough to evict; it also cannot release stale entries while the server is idle because it only runs from cuda_graph(). Keep a bound sized for tensor-split workloads, or avoid shape-keying one-off prefill graphs.

Useful? React with 👍 / 👎.


int64_t last_graph_eviction_sweep = 0;

Expand All @@ -1484,16 +1485,6 @@ struct ggml_backend_cuda_context {

auto it = cuda_graphs.find(graph_key);
if (it == cuda_graphs.end()) {
// a workload with many distinct shapes must not grow this without bound
while (cuda_graphs.size() >= max_cuda_graphs) {
auto lru = cuda_graphs.begin();
for (auto c = cuda_graphs.begin(); c != cuda_graphs.end(); ++c) {
if (c->second->last_used_time < lru->second->last_used_time) {
lru = c;
}
}
cuda_graphs.erase(lru);
}
it = cuda_graphs.emplace(graph_key, std::make_unique<ggml_cuda_graph>()).first;
}
it->second->last_used_time = time_now;
Expand Down