Skip to content

Commit e6fdbce

Browse files
committed
Merge remote-tracking branch 'upstream/master' into codex/harden-vulkan-turbo4-v-cache
2 parents aeb7ce5 + 83eebe9 commit e6fdbce

6 files changed

Lines changed: 158 additions & 12 deletions

File tree

common/fit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class common_params_fit_exception : public std::runtime_error {
2626
using std::runtime_error::runtime_error;
2727
};
2828

29-
static std::vector<llama_device_memory_data> common_get_device_memory_data(
29+
std::vector<llama_device_memory_data> common_get_device_memory_data(
3030
const char * path_model,
3131
const llama_model_params * mparams,
3232
const llama_context_params * cparams,

common/fit.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#pragma once
22

33
#include "ggml.h"
4+
#include "ggml-backend.h"
5+
#include "llama.h"
6+
#include "../src/llama-ext.h"
7+
8+
#include <vector>
49

510
enum common_params_fit_status {
611
COMMON_PARAMS_FIT_STATUS_SUCCESS = 0, // found allocations that are projected to fit
@@ -30,3 +35,14 @@ void common_fit_print(
3035
struct llama_context_params * cparams);
3136

3237
void common_memory_breakdown_print(const struct llama_context * ctx);
38+
39+
// Load a model + context with no_alloc and return the per-device memory breakdown.
40+
std::vector<llama_device_memory_data> common_get_device_memory_data(
41+
const char * path_model,
42+
const struct llama_model_params * mparams,
43+
const struct llama_context_params * cparams,
44+
std::vector<ggml_backend_dev_t> & devs,
45+
uint32_t & hp_ngl,
46+
uint32_t & hp_n_ctx_train,
47+
uint32_t & hp_n_expert,
48+
enum ggml_log_level log_level);

ggml/include/ggml-alloc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ GGML_API size_t ggml_gallocr_get_buffer_size(ggml_gallocr_t galloc, int buffer_i
7676
// Utils
7777
// Create a buffer and allocate all the tensors in a ggml_context
7878
// ggml_backend_alloc_ctx_tensors_from_buft_size returns the size of the buffer that would be allocated by ggml_backend_alloc_ctx_tensors_from_buft
79+
// ggml_backend_alloc_ctx_tensors_from_buft returns NULL on failure or if all tensors in ctx are already allocated or zero-sized
7980
GGML_API size_t ggml_backend_alloc_ctx_tensors_from_buft_size(struct ggml_context * ctx, ggml_backend_buffer_type_t buft);
8081
GGML_API struct ggml_backend_buffer * ggml_backend_alloc_ctx_tensors_from_buft(struct ggml_context * ctx, ggml_backend_buffer_type_t buft);
8182
GGML_API struct ggml_backend_buffer * ggml_backend_alloc_ctx_tensors(struct ggml_context * ctx, ggml_backend_t backend);

ggml/src/ggml-backend-meta.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,9 @@ static void ggml_backend_meta_buffer_set_tensor(ggml_backend_buffer_t buffer, gg
12751275
for (size_t j = 0; j < n_bufs; j++) {
12761276
ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
12771277
const size_t chunk_size_j = simple_tensor->nb[split_state.axis + 1];
1278+
if (chunk_size_j == 0) {
1279+
continue;
1280+
}
12781281
const size_t simple_offset = i_start * chunk_size_j;
12791282
ggml_backend_tensor_set_2d(simple_tensor, (const char *) data + offset_j, simple_offset, chunk_size_j, i_stop - i_start, chunk_size_j, chunk_size_full);
12801283
offset_j += chunk_size_j;
@@ -1382,6 +1385,9 @@ static void ggml_backend_meta_buffer_get_tensor(ggml_backend_buffer_t buffer, co
13821385
for (size_t j = 0; j < n_bufs; j++){
13831386
const ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
13841387
const size_t chunk_size_j = simple_tensor->nb[split_state.axis + 1];
1388+
if (chunk_size_j == 0) {
1389+
continue;
1390+
}
13851391
const size_t simple_offset = i_start * chunk_size_j;
13861392
ggml_backend_tensor_get_2d(simple_tensor, (char *) data + offset_j, simple_offset, chunk_size_j, i_stop - i_start, chunk_size_j, chunk_size_full);
13871393
offset_j += chunk_size_j;
@@ -1445,6 +1451,7 @@ static ggml_backend_buffer_t ggml_backend_meta_buffer_type_alloc_buffer(ggml_bac
14451451
buf_ctx->buf_configs.reserve(n_simple_bufts);
14461452
for (size_t i = 0; i < n_simple_bufts; i++) {
14471453
ggml_backend_buffer_t simple_buf = ggml_backend_buft_alloc_buffer(ggml_backend_meta_buft_simple_buft(buft, i), size);
1454+
GGML_ASSERT(simple_buf != nullptr);
14481455
max_size = std::max(max_size, ggml_backend_buffer_get_size(simple_buf));
14491456
buf_ctx->buf_configs.emplace_back(ggml_init(params), simple_buf);
14501457
}
@@ -1474,8 +1481,27 @@ struct ggml_backend_buffer * ggml_backend_meta_alloc_ctx_tensors_from_buft(struc
14741481
t->data = (void *) 0x2000000000000000; // FIXME
14751482
}
14761483
for (size_t i = 0; i < n_simple_bufts; i++) {
1477-
meta_buf_ctx->buf_configs[i].buf = ggml_backend_alloc_ctx_tensors_from_buft(
1478-
meta_buf_ctx->buf_configs[i].ctx, ggml_backend_meta_buft_simple_buft(buft, i));
1484+
ggml_context * ctx = meta_buf_ctx->buf_configs[i].ctx;
1485+
ggml_backend_buffer_type_t simple_buft = ggml_backend_meta_buft_simple_buft(buft, i);
1486+
1487+
// If a ggml_context only has zero-sized tensors, ggml_backend_alloc_ctx_tensors_from_buft returns NULL.
1488+
// For those edge cases, allocate a dummy buffer instead.
1489+
bool any_nonzero_slice = false;
1490+
for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != nullptr; t = ggml_get_next_tensor(ctx, t)) {
1491+
if (ggml_nelements(t) != 0) {
1492+
any_nonzero_slice = true;
1493+
break;
1494+
}
1495+
}
1496+
if (any_nonzero_slice) {
1497+
meta_buf_ctx->buf_configs[i].buf = ggml_backend_alloc_ctx_tensors_from_buft(ctx, simple_buft);
1498+
} else {
1499+
meta_buf_ctx->buf_configs[i].buf = ggml_backend_buft_alloc_buffer(simple_buft, 0);
1500+
for (ggml_tensor * t = ggml_get_first_tensor(ctx); t != nullptr; t = ggml_get_next_tensor(ctx, t)) {
1501+
t->buffer = meta_buf_ctx->buf_configs[i].buf;
1502+
}
1503+
}
1504+
GGML_ASSERT(meta_buf_ctx->buf_configs[i].buf != nullptr);
14791505
meta_buf->size = std::max(meta_buf->size, ggml_backend_buffer_get_size(meta_buf_ctx->buf_configs[i].buf));
14801506
}
14811507
return meta_buf;
@@ -1605,6 +1631,9 @@ static void ggml_backend_meta_set_tensor_async(ggml_backend_t backend, ggml_tens
16051631
ggml_backend_t simple_backend = ggml_backend_meta_simple_backend(backend, j);
16061632
ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
16071633
const size_t chunk_size_j = simple_tensor->nb[split_state.axis + 1];
1634+
if (chunk_size_j == 0) {
1635+
continue;
1636+
}
16081637
ggml_backend_tensor_set_2d_async(simple_backend, simple_tensor, (const char *) data + offset_j, offset, chunk_size_j,
16091638
i_stop - i_start, chunk_size_j, chunk_size_full);
16101639
offset_j += chunk_size_j;
@@ -1646,6 +1675,9 @@ static void ggml_backend_meta_get_tensor_async(ggml_backend_t backend, const ggm
16461675
ggml_backend_t simple_backend = ggml_backend_meta_simple_backend(backend, j);
16471676
const ggml_tensor * simple_tensor = ggml_backend_meta_buffer_simple_tensor(tensor, j);
16481677
const size_t chunk_size_j = simple_tensor->nb[split_state.axis + 1];
1678+
if (chunk_size_j == 0) {
1679+
continue;
1680+
}
16491681
ggml_backend_tensor_get_2d_async(simple_backend, simple_tensor, (char *) data + offset_j, offset, chunk_size_j,
16501682
i_stop - i_start, chunk_size_j, chunk_size_full);
16511683
offset_j += chunk_size_j;

ggml/src/ggml-opencl/ggml-opencl.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -661,11 +661,10 @@ struct ggml_backend_opencl_context {
661661
cl_kernel kernel_mul_mm_iq4_nl_f32_l4_lm;
662662

663663
std::vector<ProfilingInfo> profiling_info;
664+
std::vector<ProfilingInfo> profiling_results;
664665

665-
void write_profiling_info() {
666-
FILE * fperf = fopen("cl_profiling.csv", "w");
667-
if (!fperf) {
668-
GGML_LOG_ERROR("Failed to open cl_profiling.csv\n");
666+
void flush_profiling_batch() {
667+
if (profiling_info.empty()) {
669668
return;
670669
}
671670

@@ -689,6 +688,7 @@ struct ggml_backend_opencl_context {
689688
CL_CHECK(clGetEventProfilingInfo(
690689
info.evt, CL_PROFILING_COMMAND_COMPLETE, sizeof(cl_ulong), &cmd_complete, NULL));
691690
CL_CHECK(clReleaseEvent(info.evt));
691+
info.evt = nullptr;
692692

693693
char kernel_name[512];
694694
CL_CHECK(clGetKernelInfo(info.kernel, CL_KERNEL_FUNCTION_NAME,
@@ -706,10 +706,26 @@ struct ggml_backend_opencl_context {
706706
info.cmd_complete_duration_ns = cmd_complete - cmd_end;
707707
info.cmd_total_duration_ns = cmd_complete - cmd_queued;
708708
}
709+
profiling_results.insert(profiling_results.end(),
710+
std::make_move_iterator(profiling_info.begin()),
711+
std::make_move_iterator(profiling_info.end()));
712+
profiling_info.clear();
713+
}
714+
715+
void write_profiling_info() {
716+
if (profiling_results.empty()) {
717+
return;
718+
}
709719

710720
// Dump a csv
721+
FILE * fperf = fopen("cl_profiling.csv", "w");
722+
if (!fperf) {
723+
GGML_LOG_ERROR("Failed to open cl_profiling.csv\n");
724+
return;
725+
}
726+
711727
fprintf(fperf, "op name, kernel name, exec duration (ms), global size, local size, output size\n");
712-
for (const ProfilingInfo & info : profiling_info) {
728+
for (const ProfilingInfo & info : profiling_results) {
713729
fprintf(fperf, "%s,%s,%f,%zux%zux%zu,%zux%zux%zu,%zux%zux%zux%zu\n",
714730
info.op_name.c_str(), info.kernel_name.c_str(),
715731
info.cmd_duration_ns/1.e6f,
@@ -720,14 +736,14 @@ struct ggml_backend_opencl_context {
720736
fclose(fperf);
721737

722738
// Dump a simple chrome trace
723-
FILE* ftrace = fopen("cl_trace.json", "w");
739+
FILE * ftrace = fopen("cl_trace.json", "w");
724740
if (!ftrace) {
725741
GGML_LOG_ERROR("Failed to open cl_trace.json\n");
726742
return;
727743
}
728744

729745
fprintf(ftrace, "[\n");
730-
for (const ProfilingInfo & info : profiling_info) {
746+
for (const ProfilingInfo & info : profiling_results) {
731747
fprintf(ftrace, "{\"name\": \"%s\", \"cat\": \"OpenCL\", \"ph\": \"B\", \"ts\": %" PRIu64 ", \"pid\": \"\", \"tid\": \"Host\"},\n",
732748
info.kernel_name.c_str(), info.cmd_queued/1000);
733749
fprintf(ftrace, "{\"name\": \"%s\", \"cat\": \"OpenCL\", \"ph\": \"E\", \"ts\": %" PRIu64 ", \"pid\": \"\", \"tid\": \"Host\"},\n",
@@ -738,6 +754,7 @@ struct ggml_backend_opencl_context {
738754
fprintf(ftrace, "{\"name\": \"%s\", \"cat\": \"OpenCL\", \"ph\": \"E\", \"ts\": %" PRIu64 ", \"pid\": \"\", \"tid\": \"Device\"},\n",
739755
info.kernel_name.c_str(), info.cmd_end/1000);
740756
}
757+
fprintf(ftrace, "]\n");
741758
fclose(ftrace);
742759
}
743760

@@ -758,6 +775,9 @@ struct ggml_backend_opencl_context {
758775

759776
profiling_info.emplace_back();
760777
populateProfilingInfo(profiling_info.back(), evt, kernel, work_dim, global_work_size, local_work_size, tensor);
778+
if (profiling_info.size() >= 2048) {
779+
flush_profiling_batch();
780+
}
761781
#else
762782
GGML_UNUSED(tensor);
763783
CL_CHECK(clEnqueueNDRangeKernel(queue, kernel, work_dim, NULL, global_work_size, local_work_size, 0, NULL, NULL));
@@ -804,7 +824,7 @@ struct ggml_backend_opencl_context {
804824
if (ref_count == 0) {
805825
#ifdef GGML_OPENCL_PROFILING
806826
write_profiling_info();
807-
profiling_info.clear();
827+
profiling_results.clear();
808828
#endif
809829
}
810830
}

tools/server/server-context.cpp

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "build-info.h"
1010
#include "common.h"
11+
#include "fit.h"
1112
#include "llama.h"
1213
#include "log.h"
1314
#include "sampling.h"
@@ -775,7 +776,7 @@ struct server_context_impl {
775776
for (auto & [dev, size] : mmproj_mem) {
776777
total += size;
777778
}
778-
SRV_INF("[mtmd] estimated memory usage of mmproj is %.2f MiB\n", total / (1024.0 * 1024.0));
779+
SRV_INF("[mtmd] estimated worst-case memory usage of mmproj is %.2f MiB\n", total / (1024.0 * 1024.0));
779780
GGML_ASSERT(!params_base.fit_params_target.empty());
780781
for (auto & [dev, size] : mmproj_mem) {
781782
for (size_t i = 0; i < ggml_backend_dev_count(); i++) {
@@ -793,6 +794,82 @@ struct server_context_impl {
793794
}
794795
}
795796

797+
// optionally reserve VRAM for the draft / MTP context before fitting the target model
798+
if (params_base.fit_params) {
799+
const bool spec_mtp = std::find(params_base.speculative.types.begin(),
800+
params_base.speculative.types.end(),
801+
COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params_base.speculative.types.end();
802+
const bool has_draft = params_base.speculative.has_dft();
803+
804+
if (has_draft || spec_mtp) {
805+
common_params params_dft = params_base;
806+
bool measure_model_bytes = true;
807+
808+
if (has_draft) {
809+
const auto & params_spec = params_base.speculative.draft;
810+
params_dft.devices = params_spec.devices;
811+
params_dft.model = params_spec.mparams;
812+
params_dft.n_gpu_layers = params_spec.n_gpu_layers;
813+
params_dft.cache_type_k = params_spec.cache_type_k;
814+
params_dft.cache_type_v = params_spec.cache_type_v;
815+
params_dft.tensor_buft_overrides = params_spec.tensor_buft_overrides;
816+
} else {
817+
// MTP draft context lives on the target model, only context+compute are new
818+
measure_model_bytes = false;
819+
}
820+
821+
auto mparams_dft = common_model_params_to_llama(params_dft);
822+
auto cparams_dft = common_context_params_to_llama(params_dft);
823+
if (spec_mtp) {
824+
cparams_dft.ctx_type = LLAMA_CONTEXT_TYPE_MTP;
825+
}
826+
cparams_dft.n_rs_seq = 0;
827+
828+
std::vector<ggml_backend_dev_t> devs;
829+
uint32_t hp_ngl = 0;
830+
uint32_t hp_nct = 0;
831+
uint32_t hp_nex = 0;
832+
try {
833+
auto dmd = common_get_device_memory_data(
834+
params_dft.model.path.c_str(), &mparams_dft, &cparams_dft,
835+
devs, hp_ngl, hp_nct, hp_nex, GGML_LOG_LEVEL_ERROR);
836+
837+
GGML_ASSERT(!params_base.fit_params_target.empty());
838+
size_t total = 0;
839+
840+
std::vector<ggml_backend_dev_t> tgt_devices = params.devices;
841+
842+
if (tgt_devices.empty()) {
843+
for(size_t i = 0; i < ggml_backend_dev_count(); ++i) {
844+
tgt_devices.push_back(ggml_backend_dev_get(i));
845+
}
846+
}
847+
848+
for (size_t j = 0; j < devs.size(); ++j) {
849+
const size_t bytes =
850+
(measure_model_bytes ? dmd[j].mb.model : 0) +
851+
dmd[j].mb.context +
852+
dmd[j].mb.compute;
853+
total += bytes;
854+
for (size_t i = 0; i < tgt_devices.size(); i++) {
855+
if (tgt_devices[i] == devs[j]) {
856+
SRV_DBG("[spec] adding %.2f MiB to fit_params_target for device %s\n",
857+
bytes / (1024.0 * 1024.0), ggml_backend_dev_name(devs[j]));
858+
params_base.fit_params_target[i] += bytes;
859+
break;
860+
}
861+
}
862+
}
863+
SRV_INF("[spec] estimated memory usage of %s is %.2f MiB\n",
864+
has_draft ? "draft model" : "MTP context",
865+
total / (1024.0 * 1024.0));
866+
} catch (const std::exception & e) {
867+
SRV_ERR("[spec] failed to measure %s memory: %s\n",
868+
has_draft ? "draft model" : "MTP context", e.what());
869+
}
870+
}
871+
}
872+
796873
llama_init = common_init_from_params(params_base);
797874

798875
model_tgt = llama_init->model();

0 commit comments

Comments
 (0)