diff --git a/src/llama-model-loader.cpp b/src/llama-model-loader.cpp index 5071556..6b659e2 100644 --- a/src/llama-model-loader.cpp +++ b/src/llama-model-loader.cpp @@ -1463,12 +1463,12 @@ bool llama_model_loader::load_all_data( std::vector host_ptrs; size_t buffer_idx = 0; // buffer to use for async loads ggml_backend_t upload_backend = [&](const char * func) -> ggml_backend_t { - if (use_mmap || check_tensors) { + if (check_tensors) { return nullptr; } // When not using mmaped io use async uploads from pinned memory to GPU memory. // First determine if the backend supports the necessary features for async uploads. - auto * buf = bufs.count(0) ? bufs.at(0) : nullptr; + auto * buf = bufs.count(0) ? bufs.at(0).buffer : nullptr; if (!buf) { LLAMA_LOG_DEBUG("%s: no buffer found for async uploads\n", func); return nullptr; @@ -1539,7 +1539,7 @@ bool llama_model_loader::load_all_data( if (upload_backend) { LLAMA_LOG_DEBUG("%s: using async uploads for device %s, buffer type %s, backend %s\n", __func__, ggml_backend_dev_name(ggml_backend_get_device(upload_backend)), - ggml_backend_buft_name(ggml_backend_buffer_get_type(bufs.at(0))), + ggml_backend_buft_name(ggml_backend_buffer_get_type(bufs.at(0).buffer)), ggml_backend_name(upload_backend)); } @@ -1558,11 +1558,15 @@ bool llama_model_loader::load_all_data( size_t n_size = ggml_nbytes(cur); - if (use_mmap) { + const auto buf_it = bufs.find(weight->idx); + const bool tensor_uses_mmap = + use_mmap && buf_it != bufs.end() && buf_it->second.is_mmap; + + if (tensor_uses_mmap) { const auto & mapping = mappings.at(weight->idx); ggml_backend_buffer_t buf_mmap = nullptr; - if (bufs.count(weight->idx)) { - buf_mmap = bufs.at(weight->idx); + if (buf_it != bufs.end()) { + buf_mmap = buf_it->second.buffer; } uint8_t * data = (uint8_t *) mapping->addr() + weight->offs; diff --git a/src/llama-model-loader.h b/src/llama-model-loader.h index e9fe359..4b1215d 100644 --- a/src/llama-model-loader.h +++ b/src/llama-model-loader.h @@ -15,7 +15,12 @@ #include #include -using llama_buf_map = std::unordered_map; +struct llama_buf_info { + ggml_backend_buffer_t buffer; + bool is_mmap; +}; + +using llama_buf_map = std::unordered_map; // lists of buffer types used for each layer using buft_list_t = std::vector>; diff --git a/src/llama-model.cpp b/src/llama-model.cpp index a71b1aa..388e9dc 100644 --- a/src/llama-model.cpp +++ b/src/llama-model.cpp @@ -1664,7 +1664,10 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { } } - ml.init_mappings(true, use_mlock ? &pimpl->mlock_mmaps : nullptr); + // Do not prefetch every byte of every shard. Qwen3.8 Flash Next can keep its + // sparse PLE tensor mmap-backed on CPU while uploading dense weights to GPU; + // whole-file prefetch defeats that arrangement and thrashes memory on UMA. + ml.init_mappings(false, use_mlock ? &pimpl->mlock_mmaps : nullptr); pimpl->mappings.reserve(ml.mappings.size()); // create the backend buffers @@ -1720,7 +1723,7 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { throw std::runtime_error(format("unable to allocate %s buffer", ggml_backend_buft_name(buft))); } bufs.emplace_back(buf); - buf_map.emplace(idx, buf); + buf_map.emplace(idx, llama_buf_info { buf, true }); } } else { ggml_backend_buffer_t buf; @@ -1743,7 +1746,7 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { } bufs.emplace_back(buf); for (uint32_t idx = 0; idx < ml.files.size(); idx++) { - buf_map.emplace(idx, buf); + buf_map.emplace(idx, llama_buf_info { buf, false }); } }