Commit 960b1d51b for llama.cpp
commit 960b1d51b73176ac970be51ef8ef22c9e2b9113c
Author: Georgi Gerganov <ggerganov@gmail.com>
Date: Wed Nov 4 09:29:26 2026 +0200
ggml : write host-buffer tensor data directly in gguf writer (#31144)
The gguf writer staged each tensor into a temporary vector before
writing it. For host-buffer tensors the data pointer is directly
writable, so skip the staging copy and write it straight to the file.
This avoids the per-tensor allocation, zero-init and memcpy.
Assisted-by: dsh:llama.cpp/ggml-org/DeepSeek-V4-Flash-Vision-Exp-GGUF (Mac Studio, M2 Ultra, 192GB)
diff --git a/ggml/src/gguf.cpp b/ggml/src/gguf.cpp
index 75ceade7d..c80b68218 100644
--- a/ggml/src/gguf.cpp
+++ b/ggml/src/gguf.cpp
@@ -1463,6 +1463,7 @@ struct gguf_writer_base {
// we bet on devirtualization
virtual void write(int8_t val) = 0;
virtual void write(const std::vector<int8_t> & val) = 0;
+ virtual void write(const void * data, size_t size) = 0;
virtual void write_tensor_data(const struct gguf_tensor_info & info, size_t offset_data, size_t alignment) = 0;
template <typename T>
@@ -1579,21 +1580,31 @@ struct gguf_writer_buf final : public gguf_writer_base {
written_bytes += val.size();
}
+ void write(const void * data, const size_t size) override {
+ buf.insert(buf.end(), (const int8_t *) data, (const int8_t *) data + size);
+ written_bytes += size;
+ }
+
void write_tensor_data(const struct gguf_tensor_info & info, const size_t offset_data, const size_t alignment) override {
GGML_ASSERT(buf.size() - offset_data == info.offset);
GGML_ASSERT(ggml_is_contiguous(&info.t));
- const size_t offset = buf.size();
const size_t nbytes = ggml_nbytes(&info.t);
- buf.resize(offset + nbytes);
- if (info.t.buffer) {
- ggml_backend_tensor_get(&info.t, buf.data() + offset, 0, nbytes);
+ if (info.t.buffer && ggml_backend_buffer_is_host(info.t.buffer)) {
+ GGML_ASSERT(info.t.data != NULL && "tensor not allocated");
+ write(info.t.data, nbytes);
} else {
- GGML_ASSERT(info.t.data);
- memcpy(buf.data() + offset, info.t.data, nbytes);
+ const size_t offset = buf.size();
+ buf.resize(offset + nbytes);
+ if (info.t.buffer) {
+ ggml_backend_tensor_get(&info.t, buf.data() + offset, 0, nbytes);
+ } else {
+ GGML_ASSERT(info.t.data);
+ memcpy(buf.data() + offset, info.t.data, nbytes);
+ }
+ written_bytes += nbytes;
}
- written_bytes += nbytes;
pad(alignment);
}
@@ -1624,20 +1635,33 @@ struct gguf_writer_file final : public gguf_writer_base {
}
}
+ void write(const void * data, const size_t size) override {
+ const auto ret = fwrite(data, 1, size, file);
+ written_bytes += size;
+ if (ret != size) {
+ throw std::runtime_error("unexpected fwrite number of bytes written, '" + std::to_string(ret) + "' instead of '" + std::to_string(size) + "'");
+ }
+ }
+
void write_tensor_data(const struct gguf_tensor_info & info, const size_t offset_data, const size_t alignment) override {
GGML_ASSERT(written_bytes - offset_data == info.offset);
GGML_ASSERT(ggml_is_contiguous(&info.t));
const size_t nbytes = ggml_nbytes(&info.t);
- std::vector<int8_t> buf(nbytes);
- if (info.t.buffer) {
- ggml_backend_tensor_get(&info.t, buf.data(), 0, nbytes);
+ if (info.t.buffer && ggml_backend_buffer_is_host(info.t.buffer)) {
+ GGML_ASSERT(info.t.data != NULL && "tensor not allocated");
+ write(info.t.data, nbytes);
} else {
- GGML_ASSERT(info.t.data);
- memcpy(buf.data(), info.t.data, nbytes);
+ std::vector<int8_t> buf(nbytes);
+ if (info.t.buffer) {
+ ggml_backend_tensor_get(&info.t, buf.data(), 0, nbytes);
+ } else {
+ GGML_ASSERT(info.t.data);
+ memcpy(buf.data(), info.t.data, nbytes);
+ }
+ write(buf);
}
- write(buf);
pad(alignment);
}