llama: free backend compute scratch on cold; guard memory_breakdown

On device release with KV eviction, also call ggml_backend_free_scratch()
on each backend so a cold model drops its Vulkan compute preallocations
(reallocated lazily on the next compute via restore).

Also guard llama_context::memory_breakdown() against a freed scheduler:
evict_kv release resets sched to null, so a cold model that is then torn
down (e.g. terminated by the process manager) hit GGML_ASSERT(sched) in
ggml_backend_sched_get_buffer_type. Skip the compute-buffer accounting
when sched is null.

Assisted-by: Claude
This commit is contained in:
2026-07-26 21:58:43 +02:00
parent a8dc3ebddb
commit 6d476d6ccc
+20 -11
View File
@@ -742,6 +742,11 @@ void llama_context::release_device(bool evict_kv) {
// model holds essentially no VRAM. Rebuilt lazily by sched_reserve() on restore. // model holds essentially no VRAM. Rebuilt lazily by sched_reserve() on restore.
sched.reset(); sched.reset();
sched_need_reserve = true; sched_need_reserve = true;
// finally, free each backend's own compute-scratch (Vulkan prealloc/staging buffers, which
// are owned by the backend and survive sched.reset()). Reallocated lazily on next compute.
for (auto & backend : backends) {
ggml_backend_free_scratch(backend.get());
}
} }
} }
@@ -3266,17 +3271,21 @@ llama_memory_breakdown llama_context::memory_breakdown() const {
ret[buft].context += size; ret[buft].context += size;
} }
} }
if (model.hparams.no_alloc) { // the scheduler (and its compute buffers) may have been freed while the model is cold
for (size_t i = 0; i < backends.size(); ++i) { // (on-demand VRAM eviction, see release_device); it contributes no compute VRAM then.
ggml_backend_t backend = backends[i].get(); if (sched) {
ggml_backend_buffer_type_t buft = ggml_backend_sched_get_buffer_type(sched.get(), backend); if (model.hparams.no_alloc) {
ret[buft].compute += backend_buf_exp_size[i]; for (size_t i = 0; i < backends.size(); ++i) {
} ggml_backend_t backend = backends[i].get();
} else { ggml_backend_buffer_type_t buft = ggml_backend_sched_get_buffer_type(sched.get(), backend);
for (const auto & backend_ptr : backends) { ret[buft].compute += backend_buf_exp_size[i];
ggml_backend_t backend = backend_ptr.get(); }
ggml_backend_buffer_type_t buft = ggml_backend_sched_get_buffer_type(sched.get(), backend); } else {
ret[buft].compute += ggml_backend_sched_get_buffer_size(sched.get(), backend); for (const auto & backend_ptr : backends) {
ggml_backend_t backend = backend_ptr.get();
ggml_backend_buffer_type_t buft = ggml_backend_sched_get_buffer_type(sched.get(), backend);
ret[buft].compute += ggml_backend_sched_get_buffer_size(sched.get(), backend);
}
} }
} }
return ret; return ret;