@@ -322,6 +322,39 @@ bool llama_kv_cache_tiered::init() {
322322 if (!std::filesystem::exists (ssd_dir)) {
323323 std::filesystem::create_directories (ssd_dir);
324324 }
325+
326+ #ifdef GGML_USE_HIP
327+ if (config.warm_device >= 0 ) {
328+ // Allocate warm tier buffers on 6900XT (or whatever warm_device is)
329+ int prev_dev = 0 ;
330+ hipGetDevice (&prev_dev);
331+ hipSetDevice (config.warm_device );
332+
333+ // Each warm slot holds n_kv_heads * head_dim elements per position.
334+ // We allocate config.warm_ctx slots; each slot is warm_elem_bytes bytes
335+ // for K and the same for V.
336+ // warm_elem_bytes is set by the caller via set_warm_elem_bytes().
337+ if (warm_elem_bytes > 0 && config.warm_capacity () > 0 ) {
338+ warm_dev_capacity = warm_elem_bytes * config.warm_capacity ();
339+ hipError_t err_k = hipMalloc (&warm_k_dev, warm_dev_capacity);
340+ hipError_t err_v = hipMalloc (&warm_v_dev, warm_dev_capacity);
341+ if (err_k != hipSuccess || err_v != hipSuccess) {
342+ LLAMA_LOG_WARN (" %s: hipMalloc warm tier on device %d failed, falling back to SSD\n " ,
343+ __func__, config.warm_device );
344+ if (warm_k_dev) { hipFree (warm_k_dev); warm_k_dev = nullptr ; }
345+ if (warm_v_dev) { hipFree (warm_v_dev); warm_v_dev = nullptr ; }
346+ warm_dev_capacity = 0 ;
347+ } else {
348+ warm_slots.assign (config.warm_capacity (), WarmSlot{});
349+ LLAMA_LOG_INFO (" %s: warm tier on device %d: %.1f MB K + %.1f MB V\n " ,
350+ __func__, config.warm_device ,
351+ warm_dev_capacity / 1e6 , warm_dev_capacity / 1e6 );
352+ }
353+ }
354+ hipSetDevice (prev_dev);
355+ }
356+ #endif
357+
325358 return true ;
326359}
327360
@@ -361,6 +394,52 @@ bool llama_kv_cache_tiered::evict_tokens(uint32_t n_tokens_to_evict, llama_cache
361394 return true ;
362395}
363396
397+ #ifdef GGML_USE_HIP
398+ int llama_kv_cache_tiered::warm_alloc_slot () {
399+ for (int i = 0 ; i < (int )warm_slots.size (); i++) {
400+ if (!warm_slots[i].occupied ) {
401+ warm_slots[i].occupied = true ;
402+ return i;
403+ }
404+ }
405+ return -1 ;
406+ }
407+
408+ void llama_kv_cache_tiered::warm_free_slot (llama_pos pos) {
409+ auto it = warm_pos_to_slot.find (pos);
410+ if (it != warm_pos_to_slot.end ()) {
411+ int slot = it->second ;
412+ warm_slots[slot].occupied = false ;
413+ warm_slots[slot].pos = -1 ;
414+ warm_pos_to_slot.erase (it);
415+ }
416+ }
417+
418+ bool llama_kv_cache_tiered::warm_copy_to_device (int slot, const void * k_host, const void * v_host, size_t nbytes) {
419+ if (!warm_k_dev || !warm_v_dev || slot < 0 || (size_t )slot >= warm_slots.size ()) return false ;
420+ int prev_dev = 0 ;
421+ hipGetDevice (&prev_dev);
422+ hipSetDevice (config.warm_device );
423+ size_t offset = (size_t )slot * nbytes;
424+ bool ok = (hipMemcpy ((char *)warm_k_dev + offset, k_host, nbytes, hipMemcpyHostToDevice) == hipSuccess) &&
425+ (hipMemcpy ((char *)warm_v_dev + offset, v_host, nbytes, hipMemcpyHostToDevice) == hipSuccess);
426+ hipSetDevice (prev_dev);
427+ return ok;
428+ }
429+
430+ bool llama_kv_cache_tiered::warm_copy_from_device (int slot, void * k_host, void * v_host, size_t nbytes) {
431+ if (!warm_k_dev || !warm_v_dev || slot < 0 || (size_t )slot >= warm_slots.size ()) return false ;
432+ int prev_dev = 0 ;
433+ hipGetDevice (&prev_dev);
434+ hipSetDevice (config.warm_device );
435+ size_t offset = (size_t )slot * nbytes;
436+ bool ok = (hipMemcpy (k_host, (char *)warm_k_dev + offset, nbytes, hipMemcpyDeviceToHost) == hipSuccess) &&
437+ (hipMemcpy (v_host, (char *)warm_v_dev + offset, nbytes, hipMemcpyDeviceToHost) == hipSuccess);
438+ hipSetDevice (prev_dev);
439+ return ok;
440+ }
441+ #endif // GGML_USE_HIP
442+
364443bool llama_kv_cache_tiered::migrate_tokens (const std::vector<llama_pos>& positions,
365444 llama_cache_tier from_tier,
366445 llama_cache_tier to_tier,
@@ -388,15 +467,35 @@ bool llama_kv_cache_tiered::migrate_tokens(const std::vector<llama_pos>& positio
388467 bool is_cold_to_hot = (from_tier == TIER_COLD && to_tier == TIER_HOT );
389468
390469 if (is_warm_to_cold) {
391- // Warm→Cold: data is already in RAM, can serialize directly
392- save_to_ssd (positions, k_tensor, v_tensor, false ); // is_device_data = false
470+ // Warm→Cold: serialize to SSD
471+ save_to_ssd (positions, k_tensor, v_tensor, false );
393472 } else if (is_hot_to_warm) {
394- // Hot→Warm: data is in VRAM (device), need to copy to host first
395- save_to_ssd (positions, k_tensor, v_tensor, true ); // is_device_data = true
473+ #ifdef GGML_USE_HIP
474+ if (warm_k_dev && warm_elem_bytes > 0 ) {
475+ // R9700 VRAM → host staging → 6900XT VRAM
476+ size_t nbytes = warm_elem_bytes;
477+ std::vector<uint8_t > k_buf (nbytes), v_buf (nbytes);
478+ // copy device→host on current (hot) device
479+ hipMemcpy (k_buf.data (), k_tensor->data , nbytes, hipMemcpyDeviceToHost);
480+ hipMemcpy (v_buf.data (), v_tensor->data , nbytes, hipMemcpyDeviceToHost);
481+ for (auto pos : positions) {
482+ int slot = warm_alloc_slot ();
483+ if (slot < 0 ) {
484+ // warm tier full — spill to SSD
485+ save_to_ssd ({pos}, k_tensor, v_tensor, true );
486+ continue ;
487+ }
488+ warm_copy_to_device (slot, k_buf.data (), v_buf.data (), nbytes);
489+ warm_slots[slot].pos = pos;
490+ warm_pos_to_slot[pos] = slot;
491+ }
492+ } else
493+ #endif
494+ {
495+ save_to_ssd (positions, k_tensor, v_tensor, true );
496+ }
396497 } else if (is_cold_to_warm || is_cold_to_hot) {
397- // Loading from cold tier - load from SSD
398- // Note: requires mutable tensors for destination
399- load_from_ssd (positions, const_cast <ggml_tensor*>(k_tensor), const_cast <ggml_tensor*>(v_tensor), false ); // to_device = false
498+ load_from_ssd (positions, const_cast <ggml_tensor*>(k_tensor), const_cast <ggml_tensor*>(v_tensor), false );
400499 }
401500 }
402501
@@ -433,14 +532,31 @@ bool llama_kv_cache_tiered::batch_migrate_tokens(const std::vector<llama_pos>& p
433532 bool is_hot_to_warm = (from_tier == TIER_HOT && to_tier == TIER_WARM );
434533
435534 if (is_warm_to_cold) {
436- // Warm→Cold: data is already in RAM, can serialize directly
437- save_to_ssd (positions, k_tensor, v_tensor, false ); // is_device_data = false
535+ save_to_ssd (positions, k_tensor, v_tensor, false );
438536 } else if (is_hot_to_warm) {
439- // Hot→Warm: data is in VRAM (device), need to copy to host first
440- save_to_ssd (positions, k_tensor, v_tensor, true ); // is_device_data = true
537+ #ifdef GGML_USE_HIP
538+ if (warm_k_dev && warm_elem_bytes > 0 ) {
539+ size_t nbytes = warm_elem_bytes;
540+ std::vector<uint8_t > k_buf (nbytes), v_buf (nbytes);
541+ hipMemcpy (k_buf.data (), k_tensor->data , nbytes, hipMemcpyDeviceToHost);
542+ hipMemcpy (v_buf.data (), v_tensor->data , nbytes, hipMemcpyDeviceToHost);
543+ for (auto pos : positions) {
544+ int slot = warm_alloc_slot ();
545+ if (slot < 0 ) {
546+ save_to_ssd ({pos}, k_tensor, v_tensor, true );
547+ continue ;
548+ }
549+ warm_copy_to_device (slot, k_buf.data (), v_buf.data (), nbytes);
550+ warm_slots[slot].pos = pos;
551+ warm_pos_to_slot[pos] = slot;
552+ }
553+ } else
554+ #endif
555+ {
556+ save_to_ssd (positions, k_tensor, v_tensor, true );
557+ }
441558 } else {
442- // Load from SSD
443- load_from_ssd (positions, const_cast <ggml_tensor*>(k_tensor), const_cast <ggml_tensor*>(v_tensor), false ); // to_device = false
559+ load_from_ssd (positions, const_cast <ggml_tensor*>(k_tensor), const_cast <ggml_tensor*>(v_tensor), false );
444560 }
445561 }
446562
0 commit comments