@@ -1273,6 +1273,54 @@ static bool wp_is_routed_expert(const char * name) {
12731273 std::strstr (p, " ffn_down_exps." ) != nullptr ;
12741274}
12751275
1276+ static int wp_find_device_index_by_name (const std::vector<llama_device> & devices, const char * name) {
1277+ if (name == nullptr || name[0 ] == ' \0 ' ) {
1278+ return -1 ;
1279+ }
1280+ for (size_t i = 0 ; i < devices.size (); ++i) {
1281+ if (std::strcmp (ggml_backend_dev_name (devices[i].dev ), name) == 0 ) {
1282+ return (int ) i;
1283+ }
1284+ }
1285+ return -1 ;
1286+ }
1287+
1288+ static int wp_select_paging_device_index (const llama_model_params & params, const std::vector<llama_device> & devices) {
1289+ if (devices.empty ()) {
1290+ return -1 ;
1291+ }
1292+ if (params.main_gpu >= 0 && params.main_gpu < (int ) devices.size ()) {
1293+ return params.main_gpu ;
1294+ }
1295+ return 0 ;
1296+ }
1297+
1298+ static int wp_select_resident_device_index (const llama_model_params & params,
1299+ const std::vector<llama_device> & devices,
1300+ int paging_idx) {
1301+ if (devices.empty ()) {
1302+ return -1 ;
1303+ }
1304+ const char * requested = params.weight_paging_resident_device
1305+ ? params.weight_paging_resident_device
1306+ : " auto" ;
1307+ if (std::strcmp (requested, " auto" ) != 0 ) {
1308+ int idx = wp_find_device_index_by_name (devices, requested);
1309+ if (idx >= 0 ) {
1310+ return idx;
1311+ }
1312+ LLAMA_LOG_WARN (" %s: resident device '%s' not found; falling back to paging device\n " ,
1313+ __func__, requested);
1314+ return paging_idx;
1315+ }
1316+ for (size_t i = 0 ; i < devices.size (); ++i) {
1317+ if ((int ) i != paging_idx) {
1318+ return (int ) i;
1319+ }
1320+ }
1321+ return paging_idx;
1322+ }
1323+
12761324bool llama_model_base::load_tensors (llama_model_loader & ml) {
12771325 const auto & split_mode = params.split_mode ;
12781326 const auto & use_mlock = params.use_mlock ;
@@ -1308,6 +1356,43 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) {
13081356 pimpl->gpu_buft_list .emplace (dev.dev , std::move (buft_list));
13091357 }
13101358
1359+ std::string wp_expert_override_pattern;
1360+ std::string wp_dense_override_pattern;
1361+ std::vector<llama_model_tensor_buft_override> wp_tensor_buft_overrides;
1362+ ggml_backend_buffer_type_t wp_paging_buft = nullptr ;
1363+ ggml_backend_buffer_type_t wp_resident_buft = nullptr ;
1364+ bool wp_device_router_enabled = false ;
1365+
1366+ if (params.weight_paging_enabled && wp_resident_dense_enabled () && !devices.empty ()) {
1367+ const int paging_idx = wp_select_paging_device_index (params, devices);
1368+ const int resident_idx = wp_select_resident_device_index (params, devices, paging_idx);
1369+ if (paging_idx >= 0 && resident_idx >= 0 ) {
1370+ wp_paging_buft = ggml_backend_dev_buffer_type (devices[paging_idx].dev );
1371+ wp_resident_buft = ggml_backend_dev_buffer_type (devices[resident_idx].dev );
1372+ }
1373+ if (wp_paging_buft != nullptr && wp_resident_buft != nullptr ) {
1374+ wp_expert_override_pattern = " ffn_(up|gate|down)_exps\\ ." ;
1375+ wp_dense_override_pattern = " .*" ;
1376+ wp_tensor_buft_overrides.push_back ({ wp_expert_override_pattern.c_str (), wp_paging_buft });
1377+ wp_tensor_buft_overrides.push_back ({ wp_dense_override_pattern.c_str (), wp_resident_buft });
1378+ if (params.tensor_buft_overrides != nullptr ) {
1379+ for (const auto * o = params.tensor_buft_overrides ; o->pattern != nullptr ; ++o) {
1380+ wp_tensor_buft_overrides.push_back (*o);
1381+ }
1382+ }
1383+ wp_tensor_buft_overrides.push_back ({ nullptr , nullptr });
1384+ ml.tensor_buft_overrides = wp_tensor_buft_overrides.data ();
1385+ wp_device_router_enabled = true ;
1386+ LLAMA_LOG_INFO (" %s: WP_RESIDENT_DENSE router: paging=%s (%s), resident=%s (%s)\n " ,
1387+ __func__,
1388+ ggml_backend_dev_name (devices[paging_idx].dev ), ggml_backend_buft_name (wp_paging_buft),
1389+ ggml_backend_dev_name (devices[resident_idx].dev ), ggml_backend_buft_name (wp_resident_buft));
1390+ } else {
1391+ LLAMA_LOG_WARN (" %s: WP_RESIDENT_DENSE router disabled: could not resolve paging/resident bufts\n " ,
1392+ __func__);
1393+ }
1394+ }
1395+
13111396 ggml_backend_dev_t cpu_dev = ggml_backend_dev_by_type (GGML_BACKEND_DEVICE_TYPE_CPU );
13121397 if (cpu_dev == nullptr ) {
13131398 throw std::runtime_error (format (" %s: no CPU backend found" , __func__));
@@ -1760,10 +1845,21 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) {
17601845 }
17611846 }
17621847
1763- bool ctx_has_weights = false ;
1848+ bool ctx_has_paged_weights = false ;
17641849 for (ggml_tensor * t = ggml_get_first_tensor (ctx); t != nullptr ; t = ggml_get_next_tensor (ctx, t)) {
17651850 const auto * weight = ml.get_weight (ggml_get_name (t));
17661851 if (!weight) { continue ; }
1852+ if (wp_device_router_enabled && ctx_buft != nullptr ) {
1853+ const char * n = ggml_get_name (t);
1854+ ggml_backend_buffer_type_t expected_buft =
1855+ wp_is_routed_expert (n) ? wp_paging_buft : wp_resident_buft;
1856+ if (expected_buft != nullptr && ctx_buft != expected_buft) {
1857+ throw std::runtime_error (format (
1858+ " weight-paging: tensor %s routed to %s, expected %s" ,
1859+ n, ggml_backend_buft_name (ctx_buft),
1860+ ggml_backend_buft_name (expected_buft)));
1861+ }
1862+ }
17671863 // MAD-88: skip huge non-block tensors from paging. token_embd
17681864 // and output are needed every forward pass (vocab embed +
17691865 // logits), so paging them just adds SSD churn for no win.
@@ -1835,13 +1931,13 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) {
18351931 if (weight_pager) {
18361932 weight_pager->weight_tensor_ptrs .push_back (t);
18371933 }
1934+ ctx_has_paged_weights = true ;
18381935 }
1839- ctx_has_weights = true ;
18401936 }
18411937
1842- // Record the buft once per ctx that owns weight tensors.
1938+ // Record the buft once per ctx that owns paged weight tensors.
18431939 // De-dupe so the multi-device guard reads a clean set.
1844- if (ctx_has_weights && ctx_buft != nullptr && weight_pager) {
1940+ if (ctx_has_paged_weights && ctx_buft != nullptr && weight_pager) {
18451941 bool seen = false ;
18461942 for (auto * existing : weight_pager->weight_bufts ) {
18471943 if (existing == ctx_buft) { seen = true ; break ; }
@@ -2778,6 +2874,7 @@ llama_model_params llama_model_default_params() {
27782874 /* .weight_paging_enabled =*/ false ,
27792875 /* .weight_paging_slots =*/ -1 ,
27802876 /* .weight_paging_prefetch =*/ false ,
2877+ /* .weight_paging_resident_device =*/ " auto" ,
27812878 };
27822879
27832880 return result;
0 commit comments