diff --git a/ggml/src/ggml-alloc.c b/ggml/src/ggml-alloc.c index a71838eafc6..96f4c463c1e 100644 --- a/ggml/src/ggml-alloc.c +++ b/ggml/src/ggml-alloc.c @@ -1124,6 +1124,20 @@ static void free_buffers(ggml_backend_buffer_t ** buffers, const size_t * n_buff free(*buffers); } +static bool alloc_ctx_tensors_finalize_views(struct ggml_context * ctx) { + for (struct ggml_tensor * t = ggml_get_first_tensor(ctx); t != NULL; t = ggml_get_next_tensor(ctx, t)) { + if (t->view_src != NULL && t->buffer == NULL) { + enum ggml_status status = ggml_backend_view_init(t); + if (status != GGML_STATUS_SUCCESS) { + GGML_LOG_ERROR("%s: failed to initialize view tensor %s\n", __func__, t->name); + return false; + } + } + } + + return true; +} + static bool alloc_tensor_range(struct ggml_context * ctx, struct ggml_tensor * first, struct ggml_tensor * last, ggml_backend_buffer_type_t buft, size_t size, @@ -1142,24 +1156,14 @@ static bool alloc_tensor_range(struct ggml_context * ctx, struct ggml_tallocr tallocr = ggml_tallocr_new(buffer); for (struct ggml_tensor * t = first; t != last; t = ggml_get_next_tensor(ctx, t)) { - enum ggml_status status = GGML_STATUS_SUCCESS; - if (t->data == NULL) { - if (t->view_src == NULL) { - status = ggml_tallocr_alloc(&tallocr, t); - } else if (t->buffer == NULL) { - status = ggml_backend_view_init(t); - } - } else { - if (t->view_src != NULL && t->buffer == NULL) { - // view of a pre-allocated tensor - status = ggml_backend_view_init(t); + if (t->view_src == NULL && t->data == NULL) { + enum ggml_status status = ggml_tallocr_alloc(&tallocr, t); + if (status != GGML_STATUS_SUCCESS) { + GGML_LOG_ERROR("%s: failed to allocate tensor %s\n", __func__, t->name); + free_buffers(buffers, n_buffers); + return false; } } - if (status != GGML_STATUS_SUCCESS) { - GGML_LOG_ERROR("%s: failed to initialize tensor %s\n", __func__, t->name); - free_buffers(buffers, n_buffers); - return false; - } } return true; @@ -1217,6 +1221,11 @@ static ggml_backend_buffer_t ggml_backend_alloc_ctx_tensors_from_buft_impl( return NULL; } + if (!alloc_ctx_tensors_finalize_views(ctx)) { + free_buffers(&buffers, &n_buffers); + return NULL; + } + ggml_backend_buffer_t buffer; if (n_buffers == 1) { buffer = buffers[0]; diff --git a/tests/test-alloc.cpp b/tests/test-alloc.cpp index 6d5428493e7..65ae1cd51f1 100644 --- a/tests/test-alloc.cpp +++ b/tests/test-alloc.cpp @@ -583,6 +583,31 @@ static void test_reallocation() { } } +// Parent nbytes > max_size leaves cur_buf_size > max_size. A following view +// then triggers a split with this_size == 0, so the view-only residual range +// skips alloc_tensor_range unless views are finalized after all splits. +static void test_view_init_after_max_size_split() { + const size_t max_size = 16; + dummy_backend backend = dummy_backend_init(max_size); + auto [ctx, graph, ctx_ptr] = make_context(); + (void) graph; + + ggml_tensor * parent = make_input_with_size(ctx, 24); // 6 x f32, > max_size + ggml_tensor * view0 = ggml_view_1d(ctx, parent, 2, 0); + ggml_tensor * view1 = ggml_view_1d(ctx, parent, 2, 2 * sizeof(float)); + assign_names(ctx); + + ggml_backend_buffer_ptr buf(ggml_backend_alloc_ctx_tensors_from_buft(ctx, &backend.buffer_type)); + GGML_ASSERT(buf); + + for (ggml_tensor * t = ggml_get_first_tensor(ctx); t; t = ggml_get_next_tensor(ctx, t)) { + GGML_ASSERT(t->buffer != nullptr); + GGML_ASSERT(t->data != nullptr); + } + GGML_ASSERT(view0->view_src == parent); + GGML_ASSERT(view1->view_src == parent); +} + static void run(const char * name, void (*f)()) { printf("%s ", name); fflush(stdout); @@ -604,5 +629,6 @@ int main() { run("test_multiple_buffer_types", test_multiple_buffer_types); run("test_buffer_size_zero", test_buffer_size_zero); run("test_reallocation", test_reallocation); + run("test_view_init_after_max_size_split", test_view_init_after_max_size_split); return 0; }