Skip to content

Commit 4dcea28

Browse files
committed
feat: initial support for memory-mapping model weights
1 parent 072d564 commit 4dcea28

3 files changed

Lines changed: 117 additions & 0 deletions

File tree

src/model.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,99 @@ void ModelLoader::process_model_files(bool enable_mmap) {
806806
LOG_INFO("model files processing completed in %.2fs", process_time_ms / 1000.f);
807807
}
808808

809+
std::vector<MmapTensorStore> ModelLoader::mmap_tensors(std::map<std::string, ggml_tensor*>& tensors,
810+
std::set<std::string> ignore_tensors)
811+
{
812+
process_model_files(true);
813+
814+
std::vector<MmapTensorStore> result;
815+
uint64_t mapped_bytes = 0;
816+
size_t mapped_tensors = 0;
817+
818+
LOG_DEBUG("memory-mapping tensors...");
819+
820+
int64_t t_start = ggml_time_ms();
821+
822+
for (const auto& fdata : file_data) {
823+
if (!fdata.mmapped) continue;
824+
825+
const std::vector<TensorStorage>& file_tensors = fdata.tensors;
826+
std::shared_ptr<MmapWrapper> mmapped = fdata.mmapped;
827+
828+
uint8_t * mmap_data = const_cast<uint8_t*>(mmapped->data());
829+
830+
ggml_backend_buffer_t buf_mmap = ggml_backend_cpu_buffer_from_ptr(mmap_data, mmapped->size());
831+
if (!buf_mmap) {
832+
LOG_WARN("mmap: failed to create backend buffer for file %s", fdata.path.c_str());
833+
continue;
834+
}
835+
ggml_backend_buffer_set_usage(buf_mmap, GGML_BACKEND_BUFFER_USAGE_WEIGHTS);
836+
837+
size_t file_mapped_bytes = 0;
838+
size_t file_mapped_tensors = 0;
839+
840+
for (const auto& tensor_storage : file_tensors) {
841+
const std::string& name = tensor_storage.name;
842+
843+
bool is_ignored = false;
844+
for (const auto& ignore_prefix : ignore_tensors) {
845+
if (starts_with(name, ignore_prefix)) {
846+
is_ignored = true;
847+
break;
848+
}
849+
}
850+
if (is_ignored)
851+
continue;
852+
853+
auto it = tensors.find(name);
854+
if (it == tensors.end())
855+
continue;
856+
857+
ggml_tensor* dst_tensor = it->second;
858+
if (dst_tensor == nullptr)
859+
continue;
860+
861+
if (tensor_storage.type != dst_tensor->type)
862+
continue;
863+
864+
size_t tensor_size = tensor_storage.nbytes();
865+
size_t tensor_offset = tensor_storage.offset;
866+
867+
if (tensor_storage.ne[0] != dst_tensor->ne[0] ||
868+
tensor_storage.ne[1] != dst_tensor->ne[1] ||
869+
tensor_storage.ne[2] != dst_tensor->ne[2] ||
870+
tensor_storage.ne[3] != dst_tensor->ne[3] ||
871+
tensor_size != ggml_nbytes(dst_tensor)) {
872+
// let load_tensors worry about this
873+
continue;
874+
}
875+
876+
dst_tensor->buffer = buf_mmap;
877+
dst_tensor->data = mmap_data + tensor_offset;
878+
879+
file_mapped_bytes += tensor_size;
880+
file_mapped_tensors++;
881+
}
882+
883+
if (file_mapped_bytes > 0) {
884+
mapped_tensors += file_mapped_tensors;
885+
mapped_bytes += file_mapped_bytes;
886+
result.push_back({mmapped, buf_mmap});
887+
}
888+
}
889+
890+
int64_t t_end = ggml_time_ms();
891+
int64_t duration_ms = t_end - t_start;
892+
893+
LOG_INFO("memory-mapped %zu tensors in %zu files (%.2f MB), taking %.2fs",
894+
mapped_tensors,
895+
result.size(),
896+
mapped_bytes / (1024.0 * 1024.0),
897+
duration_ms / 1000.0);
898+
899+
return result;
900+
}
901+
809902
bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads_p, bool enable_mmap) {
810903

811904
process_model_files(enable_mmap);
@@ -898,6 +991,12 @@ bool ModelLoader::load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_thread
898991
continue;
899992
}
900993

994+
// skip mmapped tensors
995+
if (dst_tensor->buffer != nullptr
996+
&& ggml_backend_buffer_get_usage(dst_tensor->buffer) == GGML_BACKEND_BUFFER_USAGE_WEIGHTS) {
997+
continue;
998+
}
999+
9011000
size_t nbytes_to_read = tensor_storage.nbytes_to_read();
9021001

9031002
auto read_data = [&](char* buf, size_t n) {

src/model.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ struct ModelFileData {
202202
bool is_zip;
203203
};
204204

205+
struct MmapTensorStore {
206+
std::shared_ptr<MmapWrapper> mmapped;
207+
ggml_backend_buffer_t buffer;
208+
};
209+
205210
class ModelLoader {
206211
protected:
207212
SDVersion version_ = VERSION_COUNT;
@@ -232,6 +237,8 @@ class ModelLoader {
232237
String2TensorStorage& get_tensor_storage_map() { return tensor_storage_map; }
233238
void set_wtype_override(ggml_type wtype, std::string tensor_type_rules = "");
234239
void process_model_files(bool enable_mmap = false);
240+
std::vector<MmapTensorStore> mmap_tensors(std::map<std::string, ggml_tensor*>& tensors,
241+
std::set<std::string> ignore_tensors = {});
235242
bool load_tensors(on_new_tensor_cb_t on_new_tensor_cb, int n_threads = 0, bool use_mmap = false);
236243
bool load_tensors(std::map<std::string, ggml_tensor*>& tensors,
237244
std::set<std::string> ignore_tensors = {},

src/stable-diffusion.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ static float get_cache_reuse_threshold(const sd_cache_params_t& params) {
107107

108108
class StableDiffusionGGML {
109109
public:
110+
std::vector<MmapTensorStore> mmap_tensor_store;
110111
ggml_backend_t backend = nullptr; // general backend
111112
ggml_backend_t clip_backend = nullptr;
112113
ggml_backend_t control_net_backend = nullptr;
@@ -825,6 +826,16 @@ class StableDiffusionGGML {
825826
ignore_tensors.insert("text_encoders.llm.multi_modal_projector.");
826827
}
827828

829+
if (sd_ctx_params->enable_mmap) {
830+
if (!(offload_params_to_cpu || ggml_backend_is_cpu(backend))) {
831+
LOG_DEBUG("cannot memory-map model weights: only supported with CPU or --offload-to-cpu");
832+
} else if (apply_lora_immediately) {
833+
LOG_DEBUG("cannot memory-map model weights: only supported with --lora-apply-mode at_runtime");
834+
} else {
835+
mmap_tensor_store = model_loader.mmap_tensors(tensors, ignore_tensors);
836+
}
837+
}
838+
828839
if (clip_vision) {
829840
clip_vision->alloc_params_buffer();
830841
}

0 commit comments

Comments
 (0)