Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
diff --git a/src/ggml-metal/ggml-metal-context.m b/src/ggml-metal/ggml-metal-context.m
index e9064666..b4bac890 100644
--- a/src/ggml-metal/ggml-metal-context.m
+++ b/src/ggml-metal/ggml-metal-context.m
@@ -53,6 +53,7 @@ struct ggml_metal {

// command buffer state
int n_cb; // number of extra threads used to submit the command buffers
Comment on lines +5 to +8

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 Regenerate the patch against the pinned ggml revision

This patch's context does not match the pinned ggml commit 135ce1e0e3f2b0693d3de93d4f6b5fa0a1947820: for example, that revision has no blank line between capture_scope and // command buffer state, or between the fusion-statistics block and GGML_LOG_INFO. The earlier patches do not modify this file, so they cannot resolve these mismatches. The source-preparation step in .github/workflows/unsloth-sd-prebuilt.yml:231–233 applies every patch unconditionally, which means this addition stops prebuilt release preparation for every platform, not just Metal. Regenerate the hunks against the pinned source and verify the complete patch sequence with git apply --check (documented by local git apply -h as “see if the patch is applicable”).

Useful? React with 👍 / 👎.

+ int n_cb_user; // n_cb as requested by ggml_metal_set_n_cb; graph_compute may raise n_cb for big graphs
int n_nodes_0; // number of nodes submitted by the main thread
int n_nodes_1; // remaining number of nodes submitted by the n_cb threads
int n_nodes_per_cb;
@@ -156,6 +157,7 @@ ggml_metal_t ggml_metal_init(ggml_metal_device_t dev) {
if (ggml_metal_fusion_info_stats(res->finfo)) {
ggml_metal_fusion_info_labels_init(res->finfo);
res->n_cb = 0;
+ res->n_cb_user = 0;
}

GGML_LOG_INFO("%s: use fusion = %s\n", __func__, ggml_metal_fusion_info_enabled(res->finfo) ? "true" : "false");
@@ -484,6 +486,22 @@ enum ggml_status ggml_metal_graph_compute(ggml_metal_t ctx, struct ggml_cgraph *
// number of nodes encoded by the main thread (empirically determined)
const int n_main = MAX(64, 0.1*gf->n_nodes);

+ // a command buffer that runs for too long is killed by the macOS GPU watchdog (status 5,
+ // kIOGPUCommandBufferCallbackErrorTimeout): one Z-Image 512 DiT step in one buffer does that on a slow GPU.
+ // Spread big graphs over more command buffers (GGML_METAL_NODES_PER_CB, default 256; 0 = off).
+ {
+ static int nodes_per_cb = -1;
+ if (nodes_per_cb < 0) {
+ const char * v = getenv("GGML_METAL_NODES_PER_CB");
+ nodes_per_cb = v ? MAX(0, atoi(v)) : 256;
+ }
+ int n_cb_eff = ctx->n_cb_user;
+ if (nodes_per_cb > 0 && n_cb_eff > 0) {
+ n_cb_eff = MAX(n_cb_eff, MIN(GGML_METAL_MAX_COMMAND_BUFFERS, (gf->n_nodes - n_main + nodes_per_cb - 1)/nodes_per_cb));
+ }
+ ctx->n_cb = n_cb_eff;
+ }
+
// number of threads in addition to the main thread
const int n_cb = ctx->n_cb;

@@ -712,6 +730,8 @@ void ggml_metal_set_n_cb(ggml_metal_t ctx, int n_cb) {
n_cb = 0;
}

+ ctx->n_cb_user = MIN(n_cb, GGML_METAL_MAX_COMMAND_BUFFERS);
+
if (ctx->n_cb != n_cb) {
ctx->n_cb = MIN(n_cb, GGML_METAL_MAX_COMMAND_BUFFERS);