Recover from a ggml unsupported-op abort by restarting on the CPU backend
ggml checks every node against the device's supports_op and calls GGML_ABORT
when one is not implemented, because a single-backend graph has nowhere else to
put it: there is no per-op CPU fallback. The whole sd-server dies with SIGABRT
mid-generation and the user gets "the native image renderer stopped
unexpectedly" with no way forward.
Seen on macos-14 arm64 with FLUX.2-klein-4B Q2_K through the cross-platform CI:
the text encoder is already pinned to CPU, and the abort moved into the denoise
loop instead.
ggml_metal_op_encode_impl: error: unsupported op 'MUL_MAT' -> ggml_abort
StableDiffusionGGML::sample -> sample_k_diffusion
A retry on the same backend would abort identically, so the load is restarted
once with --backend cpu (the only flag that changes which backend executes the
graph; --offload-to-cpu moves parameters, not compute) and the generation is
re-submitted. The same checkpoint then renders slower rather than not at all.
Strictly bounded: the signature must carry both the unsupported-op line and
ggml_abort, the device must not already be CPU, and it happens once per load,
so an OOM kill or a genuine crash still surfaces as itself.
This commit is contained in:
parent
110b7a5a40
commit
9a9999f8cd
5 changed files with 244 additions and 9 deletions
|
|
@ -18,6 +18,7 @@ from core.inference.diffusion_memory import (
|
|||
OFFLOAD_SEQUENTIAL,
|
||||
)
|
||||
from core.inference.sd_cpp_args import (
|
||||
CPU_BACKEND_FLAGS,
|
||||
SdCppGenParams,
|
||||
SdCppModelFiles,
|
||||
SdCppUpscaleParams,
|
||||
|
|
@ -25,6 +26,7 @@ from core.inference.sd_cpp_args import (
|
|||
build_sd_cpp_command,
|
||||
build_sd_cpp_server_command,
|
||||
build_sd_cpp_upscale_command,
|
||||
is_ggml_unsupported_op_abort,
|
||||
metal_text_encoder_flags,
|
||||
native_speed_flags,
|
||||
offload_flags,
|
||||
|
|
@ -526,3 +528,39 @@ def test_img_gen_request_flux_uses_distilled_guidance():
|
|||
def test_img_gen_request_requires_prompt():
|
||||
with pytest.raises(ValueError):
|
||||
build_img_gen_request(prompt = " ", steps = 4)
|
||||
|
||||
|
||||
def test_ggml_unsupported_op_abort_is_recognised_only_with_both_markers():
|
||||
# The CPU-backend rescue must fire for the deterministic "this backend cannot run this graph"
|
||||
# abort and for nothing else: an OOM kill or a plain crash has to surface as itself.
|
||||
abort = (
|
||||
"sd-server connection lost during img_gen poll (process exited, code -6)\n"
|
||||
"Last output:\n"
|
||||
"[ERROR] ggml_extend.hpp:70 - ggml_metal_op_encode_impl: error: unsupported op 'MUL_MAT'\n"
|
||||
"1 sd-server 0x00000001044f8df4 ggml_abort + 156"
|
||||
)
|
||||
assert is_ggml_unsupported_op_abort(abort) is True
|
||||
# The RMS_NORM shape of the same abort (text encoder) counts too.
|
||||
assert is_ggml_unsupported_op_abort(
|
||||
"error: unsupported op 'RMS_NORM'\nggml_abort + 156"
|
||||
) is True
|
||||
# Neither marker alone is enough, and an unrelated death is never a match.
|
||||
assert is_ggml_unsupported_op_abort("unsupported op 'MUL_MAT'") is False
|
||||
assert is_ggml_unsupported_op_abort("ggml_abort + 156") is False
|
||||
assert is_ggml_unsupported_op_abort("process exited, code -9") is False
|
||||
assert is_ggml_unsupported_op_abort("") is False
|
||||
|
||||
|
||||
def test_server_command_appends_extra_args_last():
|
||||
# --backend cpu is passed as extra_args by the abort rescue, and sd.cpp is last-wins, so it has
|
||||
# to land after every flag the normal build emits.
|
||||
cmd = build_sd_cpp_server_command(
|
||||
"/x/sd-server",
|
||||
SdCppModelFiles(diffusion_model = "/m/z.gguf", vae = "/m/vae.sft"),
|
||||
host = "127.0.0.1",
|
||||
port = 1234,
|
||||
native_speed = "default",
|
||||
extra_args = list(CPU_BACKEND_FLAGS),
|
||||
)
|
||||
assert cmd[-2:] == ["--backend", "cpu"]
|
||||
assert cmd[0] == "/x/sd-server"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue