ggml : reduce CPU overhead in meta backend (#22041)

* cache subgraph splits when cgraph is unchanged

Skip per-call subgraph construction in ggml_backend_meta_graph_compute when the same ggml_cgraph is used consecutively.

Assign uid to every sub-graph so that CUDA's fast uid check path hits too.

* Address review comments

* Keep the scope as is

* Rename last_uid and last_n_subgraphs field. Remove last_max_tmp_size field. Refactor code.

* Address review comments

* Update ggml/src/ggml-backend-meta.cpp

Co-authored-by: Johannes Gäßler <johannesg@5d6.de>

* Update ggml/src/ggml-backend-meta.cpp

Co-authored-by: Johannes Gäßler <johannesg@5d6.de>

---------

Co-authored-by: Johannes Gäßler <johannesg@5d6.de>
This commit is contained in:
Gaurav Garg
2026-04-19 12:48:35 +03:00
committed by GitHub
co-authored by Johannes Gäßler
parent 037bfe38d0
commit bcdcc1044f
+18 -5
View File
@@ -1456,6 +1456,8 @@ struct ggml_backend_meta_context {
int max_nnodes = 0; int max_nnodes = 0;
size_t max_tmp_size = 0; size_t max_tmp_size = 0;
size_t max_subgraphs = 0; size_t max_subgraphs = 0;
size_t n_subgraphs = 0;
uint64_t uid = 0;
void * comm_ctx = nullptr; void * comm_ctx = nullptr;
ggml_backend_comm_allreduce_tensor_t comm_allreduce = nullptr; ggml_backend_comm_allreduce_tensor_t comm_allreduce = nullptr;
@@ -1616,6 +1618,9 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
const size_t n_backends = ggml_backend_meta_n_backends(backend); const size_t n_backends = ggml_backend_meta_n_backends(backend);
ggml_backend_meta_context * backend_ctx = (ggml_backend_meta_context *) backend->context; ggml_backend_meta_context * backend_ctx = (ggml_backend_meta_context *) backend->context;
// If the previous cgraph had a defined UID it can be used to skip rebuilding the subgraphs per simple backend.
const bool needs_rebuild = (cgraph->uid == 0) || (cgraph->uid != backend_ctx->uid);
bool max_nnodes_raised = false; bool max_nnodes_raised = false;
if (cgraph->n_nodes > backend_ctx->max_nnodes) { if (cgraph->n_nodes > backend_ctx->max_nnodes) {
for (size_t j = 0; j < n_backends; j++) { for (size_t j = 0; j < n_backends; j++) {
@@ -1625,7 +1630,13 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
} }
backend_ctx->max_nnodes = cgraph->n_nodes; backend_ctx->max_nnodes = cgraph->n_nodes;
max_nnodes_raised = true; max_nnodes_raised = true;
assert(needs_rebuild);
} }
if (needs_rebuild) {
size_t n_subgraphs = 0;
size_t max_tmp_size = 0;
for (size_t j = 0; j < n_backends; j++) { for (size_t j = 0; j < n_backends; j++) {
auto & bcj = backend_ctx->backend_configs[j]; auto & bcj = backend_ctx->backend_configs[j];
@@ -1642,8 +1653,6 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
} }
} }
size_t n_subgraphs = 0;
size_t max_tmp_size = 0;
{ {
// For MoE models it may make sense to delay the AllReduce in order to reduce I/O: // For MoE models it may make sense to delay the AllReduce in order to reduce I/O:
auto get_i_delayed = [&](const int i) -> int { auto get_i_delayed = [&](const int i) -> int {
@@ -1739,6 +1748,9 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
GGML_ASSERT(i_start == cgraph->n_nodes); GGML_ASSERT(i_start == cgraph->n_nodes);
} }
backend_ctx->uid = cgraph->uid;
backend_ctx->n_subgraphs = n_subgraphs;
if (max_tmp_size > backend_ctx->max_tmp_size) { if (max_tmp_size > backend_ctx->max_tmp_size) {
for (size_t j = 0; j < n_backends; j++) { for (size_t j = 0; j < n_backends; j++) {
auto & bcj = backend_ctx->backend_configs[j]; auto & bcj = backend_ctx->backend_configs[j];
@@ -1747,7 +1759,6 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
backend_ctx->max_tmp_size = max_tmp_size; backend_ctx->max_tmp_size = max_tmp_size;
} }
if (max_nnodes_raised || n_subgraphs > backend_ctx->max_subgraphs) { if (max_nnodes_raised || n_subgraphs > backend_ctx->max_subgraphs) {
backend_ctx->max_subgraphs = std::max(backend_ctx->max_subgraphs, n_subgraphs); backend_ctx->max_subgraphs = std::max(backend_ctx->max_subgraphs, n_subgraphs);
const size_t n_reduce_steps = backend_ctx->n_reduce_steps(); const size_t n_reduce_steps = backend_ctx->n_reduce_steps();
@@ -1793,6 +1804,8 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
const size_t hash_pos_ij = ggml_hash_insert(&cgraph_ij->visited_hash_set, node_ij); const size_t hash_pos_ij = ggml_hash_insert(&cgraph_ij->visited_hash_set, node_ij);
cgraph_ij->use_counts[hash_pos_ij] = cgraph->use_counts[hash_pos_orig]; cgraph_ij->use_counts[hash_pos_ij] = cgraph->use_counts[hash_pos_orig];
} }
cgraph_ij->uid = ggml_graph_next_uid();
}
} }
} }
@@ -1898,7 +1911,7 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
}; };
for (size_t i = 0; i < n_subgraphs; i++) { for (size_t i = 0; i < backend_ctx->n_subgraphs; i++) {
for (size_t j = 0; j < n_backends; j++) { for (size_t j = 0; j < n_backends; j++) {
auto & bcj = backend_ctx->backend_configs[j]; auto & bcj = backend_ctx->backend_configs[j];
const ggml_status status = ggml_backend_graph_compute_async(bcj.backend, bcj.cgraphs[i].cgraph_main); const ggml_status status = ggml_backend_graph_compute_async(bcj.backend, bcj.cgraphs[i].cgraph_main);
@@ -1907,7 +1920,7 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend,
} }
} }
if (n_backends > 1 && i < n_subgraphs - 1) { if (n_backends > 1 && i < backend_ctx->n_subgraphs - 1) {
bool backend_allreduce_success = false; bool backend_allreduce_success = false;
if (backend_ctx->comm_ctx) { if (backend_ctx->comm_ctx) {
std::vector<ggml_tensor *> nodes; std::vector<ggml_tensor *> nodes;