[HiSparse]Fix DeepSeek V4 HiSparse PD Transfers with Separate Host and Device KV Indices (#31901)
Co-authored-by: jackyYang6 <82102811+jackyYang6@users.noreply.github.com>
This commit is contained in:
co-authored by
jackyYang6
parent
d48ab2d386
commit
3953788596
@@ -1249,6 +1249,32 @@ class DecodePreallocQueue(DecodeHiCachePreallocMixin):
|
|||||||
page_indices = kv_to_page_indices(kv_indices, kv_transfer_page_size).astype(
|
page_indices = kv_to_page_indices(kv_indices, kv_transfer_page_size).astype(
|
||||||
np.int32
|
np.int32
|
||||||
)
|
)
|
||||||
|
device_page_indices = None
|
||||||
|
if (
|
||||||
|
self.scheduler.enable_hisparse
|
||||||
|
and isinstance(self.token_to_kv_pool, DeepSeekV4TokenToKVPool)
|
||||||
|
and not _is_fake_transfer(decode_req.req, self.scheduler.server_args)
|
||||||
|
):
|
||||||
|
# alloc_logical_only() already allocated the shared logical pages
|
||||||
|
# used by C4 indexer and C128 KV. These device buffers do not use
|
||||||
|
# the C4 sparse physical-slot mapping; carry their logical page IDs
|
||||||
|
# alongside the independently allocated C4 host page IDs.
|
||||||
|
full_kv_indices = self.req_to_token_pool.req_to_token[
|
||||||
|
decode_req.req.req_pool_idx,
|
||||||
|
prefix_len:origin_input_len,
|
||||||
|
]
|
||||||
|
device_page_indices = kv_to_page_indices(
|
||||||
|
full_kv_indices,
|
||||||
|
page_size,
|
||||||
|
).astype(np.int32)
|
||||||
|
if self.transfer_backend != TransferBackend.MOONCAKE:
|
||||||
|
raise NotImplementedError(
|
||||||
|
"DSV4 HiSparse direct PD transfer currently requires "
|
||||||
|
"the Mooncake backend"
|
||||||
|
)
|
||||||
|
metadata_kwargs = {"decode_prefix_len": total_prefix_len}
|
||||||
|
if device_page_indices is not None:
|
||||||
|
metadata_kwargs["device_kv_indices"] = device_page_indices
|
||||||
if (
|
if (
|
||||||
self.transfer_queue.enable_staging
|
self.transfer_queue.enable_staging
|
||||||
and hasattr(decode_req.kv_receiver, "require_staging")
|
and hasattr(decode_req.kv_receiver, "require_staging")
|
||||||
@@ -1263,7 +1289,7 @@ class DecodePreallocQueue(DecodeHiCachePreallocMixin):
|
|||||||
page_indices,
|
page_indices,
|
||||||
decode_req.metadata_buffer_index,
|
decode_req.metadata_buffer_index,
|
||||||
state_indices,
|
state_indices,
|
||||||
decode_prefix_len=total_prefix_len,
|
**metadata_kwargs,
|
||||||
)
|
)
|
||||||
if decode_req.is_rebootstrap:
|
if decode_req.is_rebootstrap:
|
||||||
self.kv_manager.submit_prefill_recompute(
|
self.kv_manager.submit_prefill_recompute(
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class TransferInfo:
|
|||||||
required_dst_info_num: int
|
required_dst_info_num: int
|
||||||
is_dummy: bool
|
is_dummy: bool
|
||||||
decode_prefix_len: Optional[int] = None
|
decode_prefix_len: Optional[int] = None
|
||||||
|
dst_device_kv_indices: Optional[npt.NDArray[np.int32]] = None
|
||||||
# Note: always put the optional staging field at the final (it will be set through 'STAGING_RSP' pkg when needed)
|
# Note: always put the optional staging field at the final (it will be set through 'STAGING_RSP' pkg when needed)
|
||||||
staging: Optional[StagingTransferInfo] = None
|
staging: Optional[StagingTransferInfo] = None
|
||||||
|
|
||||||
@@ -113,6 +114,11 @@ class TransferInfo:
|
|||||||
decode_prefix_len=(
|
decode_prefix_len=(
|
||||||
int(msg[8].decode("ascii")) if len(msg) > 8 and msg[8] != b"" else None
|
int(msg[8].decode("ascii")) if len(msg) > 8 and msg[8] != b"" else None
|
||||||
),
|
),
|
||||||
|
dst_device_kv_indices=(
|
||||||
|
np.frombuffer(msg[9], dtype=np.int32)
|
||||||
|
if len(msg) > 9 and msg[9] != b""
|
||||||
|
else None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -623,6 +629,8 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
force_flat: bool = False,
|
force_flat: bool = False,
|
||||||
src_layer_ids: Optional[List[int]] = None,
|
src_layer_ids: Optional[List[int]] = None,
|
||||||
dst_layer_ids: Optional[List[int]] = None,
|
dst_layer_ids: Optional[List[int]] = None,
|
||||||
|
dst_device_data_indices: Optional[npt.NDArray[np.int32]] = None,
|
||||||
|
dst_device_data_ptrs: Optional[set[int]] = None,
|
||||||
) -> int:
|
) -> int:
|
||||||
"""
|
"""
|
||||||
Generic KV cache transfer supporting both MHA and MLA architectures.
|
Generic KV cache transfer supporting both MHA and MLA architectures.
|
||||||
@@ -632,10 +640,18 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
even on a non-MLA backend, for K-only state buffers (e.g. MiniMax sparse
|
even on a non-MLA backend, for K-only state buffers (e.g. MiniMax sparse
|
||||||
index) whose per-layer list must not be half-split into K/V.
|
index) whose per-layer list must not be half-split into K/V.
|
||||||
"""
|
"""
|
||||||
# Group by indices for optimization
|
# Host and device buffers may use different destination page spaces.
|
||||||
|
# Build both transfer plans once, then select per destination buffer.
|
||||||
prefill_kv_blocks, dst_kv_blocks = group_concurrent_contiguous(
|
prefill_kv_blocks, dst_kv_blocks = group_concurrent_contiguous(
|
||||||
prefill_data_indices, dst_data_indices
|
prefill_data_indices, dst_data_indices
|
||||||
)
|
)
|
||||||
|
device_prefill_kv_blocks = device_dst_kv_blocks = None
|
||||||
|
if dst_device_data_indices is not None:
|
||||||
|
device_prefill_kv_blocks, device_dst_kv_blocks = (
|
||||||
|
group_concurrent_contiguous(
|
||||||
|
prefill_data_indices, dst_device_data_indices
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
layers_params = None
|
layers_params = None
|
||||||
|
|
||||||
@@ -701,7 +717,18 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
src_ptr: int, dst_ptr: int, item_len: int
|
src_ptr: int, dst_ptr: int, item_len: int
|
||||||
) -> List[Tuple[int, int, int]]:
|
) -> List[Tuple[int, int, int]]:
|
||||||
transfer_blocks = []
|
transfer_blocks = []
|
||||||
for prefill_index, decode_index in zip(prefill_kv_blocks, dst_kv_blocks):
|
if dst_device_data_ptrs and int(dst_ptr) in dst_device_data_ptrs:
|
||||||
|
assert (
|
||||||
|
device_prefill_kv_blocks is not None
|
||||||
|
and device_dst_kv_blocks is not None
|
||||||
|
)
|
||||||
|
src_blocks, dst_blocks = (
|
||||||
|
device_prefill_kv_blocks,
|
||||||
|
device_dst_kv_blocks,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
src_blocks, dst_blocks = prefill_kv_blocks, dst_kv_blocks
|
||||||
|
for prefill_index, decode_index in zip(src_blocks, dst_blocks):
|
||||||
src_addr = src_ptr + int(prefill_index[0]) * item_len
|
src_addr = src_ptr + int(prefill_index[0]) * item_len
|
||||||
dst_addr = dst_ptr + int(decode_index[0]) * item_len
|
dst_addr = dst_ptr + int(decode_index[0]) * item_len
|
||||||
length = item_len * len(prefill_index)
|
length = item_len * len(prefill_index)
|
||||||
@@ -750,7 +777,19 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
dst_kv_indices: npt.NDArray[np.int32],
|
dst_kv_indices: npt.NDArray[np.int32],
|
||||||
executor: concurrent.futures.ThreadPoolExecutor,
|
executor: concurrent.futures.ThreadPoolExecutor,
|
||||||
dst_layer_ids: Optional[List[int]] = None,
|
dst_layer_ids: Optional[List[int]] = None,
|
||||||
|
dst_device_kv_indices: Optional[npt.NDArray[np.int32]] = None,
|
||||||
):
|
):
|
||||||
|
dst_device_kv_ptrs = None
|
||||||
|
if dst_device_kv_indices is not None:
|
||||||
|
compression_ratios = self.kv_args.mla_compression_ratios
|
||||||
|
assert compression_ratios is not None
|
||||||
|
if len(dst_kv_ptrs) == len(self.kv_args.kv_data_ptrs):
|
||||||
|
start = self.kv_args.prefill_start_layer
|
||||||
|
end = self.kv_args.prefill_end_layer
|
||||||
|
assert end is not None
|
||||||
|
compression_ratios = compression_ratios[start:end]
|
||||||
|
c4_layer_num = sum(ratio == 4 for ratio in compression_ratios)
|
||||||
|
dst_device_kv_ptrs = set(dst_kv_ptrs[c4_layer_num:])
|
||||||
return self._send_kvcache_generic(
|
return self._send_kvcache_generic(
|
||||||
mooncake_session_id=mooncake_session_id,
|
mooncake_session_id=mooncake_session_id,
|
||||||
src_data_ptrs=self.kv_args.kv_data_ptrs,
|
src_data_ptrs=self.kv_args.kv_data_ptrs,
|
||||||
@@ -761,6 +800,8 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
executor=executor,
|
executor=executor,
|
||||||
src_layer_ids=self.kv_args.kv_layer_ids,
|
src_layer_ids=self.kv_args.kv_layer_ids,
|
||||||
dst_layer_ids=dst_layer_ids,
|
dst_layer_ids=dst_layer_ids,
|
||||||
|
dst_device_data_indices=dst_device_kv_indices,
|
||||||
|
dst_device_data_ptrs=dst_device_kv_ptrs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def send_kvcache_dcp(
|
def send_kvcache_dcp(
|
||||||
@@ -1557,12 +1598,22 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
is_dcp_transfer = (
|
is_dcp_transfer = (
|
||||||
target_rank_registration_info.requires_dcp_relayout
|
target_rank_registration_info.requires_dcp_relayout
|
||||||
)
|
)
|
||||||
|
chunked_dst_device_kv_indice = None
|
||||||
if is_dcp_transfer:
|
if is_dcp_transfer:
|
||||||
|
if req.dst_device_kv_indices is not None:
|
||||||
|
raise RuntimeError(
|
||||||
|
"HiSparse destination device indices are not "
|
||||||
|
"supported by PD DCP relayout"
|
||||||
|
)
|
||||||
chunked_dst_kv_indice = req.dst_kv_indices
|
chunked_dst_kv_indice = req.dst_kv_indices
|
||||||
else:
|
else:
|
||||||
chunked_dst_kv_indice = req.dst_kv_indices[
|
chunked_dst_kv_indice = req.dst_kv_indices[
|
||||||
kv_chunk.index_slice
|
kv_chunk.index_slice
|
||||||
]
|
]
|
||||||
|
if req.dst_device_kv_indices is not None:
|
||||||
|
chunked_dst_device_kv_indice = (
|
||||||
|
req.dst_device_kv_indices[kv_chunk.index_slice]
|
||||||
|
)
|
||||||
|
|
||||||
# NOTE: This is temporarily a workaround to deal with the case where the prefill_kv_indices
|
# NOTE: This is temporarily a workaround to deal with the case where the prefill_kv_indices
|
||||||
# is mismatched with the dst_kv_indices when page size > 1, this should never happen.
|
# is mismatched with the dst_kv_indices when page size > 1, this should never happen.
|
||||||
@@ -1577,6 +1628,12 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
: len(chunked_dst_kv_indice)
|
: len(chunked_dst_kv_indice)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
if chunked_dst_device_kv_indice is not None:
|
||||||
|
chunked_dst_device_kv_indice = (
|
||||||
|
chunked_dst_device_kv_indice[
|
||||||
|
: len(kv_chunk.prefill_kv_indices)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
skip_kv, skip_state = self._get_dsa_cache_transfer_skip_flags(
|
skip_kv, skip_state = self._get_dsa_cache_transfer_skip_flags(
|
||||||
target_rank_registration_info
|
target_rank_registration_info
|
||||||
@@ -1620,7 +1677,8 @@ class MooncakeKVManager(CommonKVManager):
|
|||||||
target_rank_registration_info.dst_kv_ptrs,
|
target_rank_registration_info.dst_kv_ptrs,
|
||||||
chunked_dst_kv_indice,
|
chunked_dst_kv_indice,
|
||||||
executor,
|
executor,
|
||||||
target_rank_registration_info.dst_kv_layer_ids,
|
dst_layer_ids=target_rank_registration_info.dst_kv_layer_ids,
|
||||||
|
dst_device_kv_indices=chunked_dst_device_kv_indice,
|
||||||
)
|
)
|
||||||
elif (
|
elif (
|
||||||
self.enable_staging
|
self.enable_staging
|
||||||
@@ -2244,6 +2302,7 @@ class MooncakeKVReceiver(CommonKVReceiver):
|
|||||||
aux_index: Optional[int] = None,
|
aux_index: Optional[int] = None,
|
||||||
state_indices: Optional[List] = None,
|
state_indices: Optional[List] = None,
|
||||||
decode_prefix_len: Optional[int] = None,
|
decode_prefix_len: Optional[int] = None,
|
||||||
|
device_kv_indices: Optional[npt.NDArray[np.int32]] = None,
|
||||||
):
|
):
|
||||||
if self.bootstrap_infos is None:
|
if self.bootstrap_infos is None:
|
||||||
self.kv_mgr.record_failure(
|
self.kv_mgr.record_failure(
|
||||||
@@ -2282,6 +2341,11 @@ class MooncakeKVReceiver(CommonKVReceiver):
|
|||||||
),
|
),
|
||||||
str(self.required_dst_info_num).encode("ascii"),
|
str(self.required_dst_info_num).encode("ascii"),
|
||||||
str(decode_prefix_len or 0).encode("ascii"),
|
str(decode_prefix_len or 0).encode("ascii"),
|
||||||
|
(
|
||||||
|
np.asarray(device_kv_indices, dtype=np.int32).tobytes()
|
||||||
|
if not is_dummy and device_kv_indices is not None
|
||||||
|
else b""
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
except zmq.ZMQError:
|
except zmq.ZMQError:
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
from sglang.srt.mem_cache.allocator.hisparse import (
|
from sglang.srt.mem_cache.allocator.hisparse import (
|
||||||
@@ -78,8 +80,8 @@ class TestDeepSeekV4HiSparseAllocator(CustomTestCase):
|
|||||||
|
|
||||||
fill_len = 512
|
fill_len = 512
|
||||||
swa_tail_len = 128
|
swa_tail_len = 128
|
||||||
kv_loc = torch.arange(fill_len, dtype=torch.int64)
|
kv_loc = torch.arange(512, 512 + fill_len, dtype=torch.int64)
|
||||||
host_indices = torch.arange(1000, 1000 + fill_len, dtype=torch.int64)
|
host_indices = torch.arange(1000, 1128, dtype=torch.int64)
|
||||||
|
|
||||||
req = SimpleNamespace(
|
req = SimpleNamespace(
|
||||||
rid="req-0",
|
rid="req-0",
|
||||||
@@ -108,18 +110,17 @@ class TestDeepSeekV4HiSparseAllocator(CustomTestCase):
|
|||||||
req_to_token_pool = ReqToTokenPool()
|
req_to_token_pool = ReqToTokenPool()
|
||||||
allocator = SimpleNamespace(
|
allocator = SimpleNamespace(
|
||||||
device=torch.device("cpu"),
|
device=torch.device("cpu"),
|
||||||
page_size=64,
|
page_size=256,
|
||||||
available_size=MagicMock(return_value=fill_len),
|
available_size=MagicMock(return_value=fill_len),
|
||||||
alloc_extend_swa_tail=MagicMock(return_value=kv_loc),
|
alloc_extend_swa_tail=MagicMock(return_value=kv_loc),
|
||||||
alloc_logical_only=MagicMock(return_value=kv_loc),
|
alloc_logical_only=MagicMock(return_value=kv_loc),
|
||||||
)
|
)
|
||||||
|
regular_host_alloc = MagicMock(return_value=host_indices)
|
||||||
coordinator = SimpleNamespace(
|
coordinator = SimpleNamespace(
|
||||||
mem_pool_host=SimpleNamespace(
|
mem_pool_host=SimpleNamespace(alloc_paged_token_slots=regular_host_alloc),
|
||||||
alloc_paged_token_slots=MagicMock(return_value=host_indices)
|
|
||||||
),
|
|
||||||
req_to_host_pool=object(),
|
req_to_host_pool=object(),
|
||||||
req_to_host_pool_allocated_len=object(),
|
req_to_host_pool_allocated_len=object(),
|
||||||
host_token_len=MagicMock(side_effect=lambda length: length),
|
host_token_len=MagicMock(side_effect=lambda token_len: token_len // 4),
|
||||||
)
|
)
|
||||||
queue = DecodePreallocQueue.__new__(DecodePreallocQueue)
|
queue = DecodePreallocQueue.__new__(DecodePreallocQueue)
|
||||||
queue.req_to_token_pool = req_to_token_pool
|
queue.req_to_token_pool = req_to_token_pool
|
||||||
@@ -138,7 +139,7 @@ class TestDeepSeekV4HiSparseAllocator(CustomTestCase):
|
|||||||
|
|
||||||
result = queue._pre_alloc(req)
|
result = queue._pre_alloc(req)
|
||||||
|
|
||||||
self.assertIs(result, host_indices)
|
self.assertTrue(torch.equal(result, host_indices))
|
||||||
allocator.alloc_extend_swa_tail.assert_called_once()
|
allocator.alloc_extend_swa_tail.assert_called_once()
|
||||||
allocator.alloc_logical_only.assert_not_called()
|
allocator.alloc_logical_only.assert_not_called()
|
||||||
_, kwargs = allocator.alloc_extend_swa_tail.call_args
|
_, kwargs = allocator.alloc_extend_swa_tail.call_args
|
||||||
@@ -149,7 +150,104 @@ class TestDeepSeekV4HiSparseAllocator(CustomTestCase):
|
|||||||
self.assertEqual(req.kv_committed_len, fill_len)
|
self.assertEqual(req.kv_committed_len, fill_len)
|
||||||
self.assertEqual(req.extend_range.length, fill_len)
|
self.assertEqual(req.extend_range.length, fill_len)
|
||||||
self.assertEqual(len(req_to_token_pool.writes), 1)
|
self.assertEqual(len(req_to_token_pool.writes), 1)
|
||||||
coordinator.mem_pool_host.alloc_paged_token_slots.assert_called_once()
|
coordinator.host_token_len.assert_called_once_with(fill_len)
|
||||||
|
regular_host_alloc.assert_called_once_with(
|
||||||
|
coordinator.req_to_host_pool,
|
||||||
|
coordinator.req_to_host_pool_allocated_len,
|
||||||
|
req.req_pool_idx,
|
||||||
|
0,
|
||||||
|
len(host_indices),
|
||||||
|
)
|
||||||
|
self.assertTrue(torch.equal(req_to_token_pool.writes[0][1], kv_loc))
|
||||||
|
|
||||||
|
# C4 indexer/C128 use the logical allocator's full-page IDs. They do not
|
||||||
|
# use either the independently allocated host pages or C4 sparse slots.
|
||||||
|
np.testing.assert_array_equal(
|
||||||
|
np.unique(kv_loc.numpy() // allocator.page_size),
|
||||||
|
np.array([2, 3]),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_mooncake_uses_separate_host_and_device_page_indices(self):
|
||||||
|
from sglang.srt.disaggregation.mooncake.conn import MooncakeKVManager
|
||||||
|
|
||||||
|
manager = object.__new__(MooncakeKVManager)
|
||||||
|
manager.is_mla_backend = True
|
||||||
|
manager.is_hybrid_mla_backend = False
|
||||||
|
manager.enable_custom_mem_pool = False
|
||||||
|
manager._transfer_data = MagicMock(return_value=0)
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=1) as executor:
|
||||||
|
ret = manager._send_kvcache_generic(
|
||||||
|
mooncake_session_id="session",
|
||||||
|
src_data_ptrs=[1000, 2000, 3000],
|
||||||
|
dst_data_ptrs=[10000, 20000, 30000],
|
||||||
|
item_lens=[100, 100, 100],
|
||||||
|
prefill_data_indices=np.array([1, 2], dtype=np.int32),
|
||||||
|
dst_data_indices=np.array([7, 8], dtype=np.int32),
|
||||||
|
executor=executor,
|
||||||
|
dst_device_data_indices=np.array([21, 22], dtype=np.int32),
|
||||||
|
dst_device_data_ptrs={20000, 30000},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(ret, 0)
|
||||||
|
manager._transfer_data.assert_called_once_with(
|
||||||
|
"session",
|
||||||
|
[
|
||||||
|
(1100, 10700, 200),
|
||||||
|
(2100, 22100, 200),
|
||||||
|
(3100, 32100, 200),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_mooncake_derives_device_buffers_from_local_pp_layout(self):
|
||||||
|
from sglang.srt.disaggregation.mooncake.conn import MooncakeKVManager
|
||||||
|
|
||||||
|
manager = object.__new__(MooncakeKVManager)
|
||||||
|
manager.kv_args = SimpleNamespace(
|
||||||
|
kv_data_ptrs=[1000, 2000, 3000],
|
||||||
|
kv_item_lens=[100, 100, 100],
|
||||||
|
kv_layer_ids=[],
|
||||||
|
mla_compression_ratios=[4, 128, 4, 128],
|
||||||
|
prefill_start_layer=0,
|
||||||
|
prefill_end_layer=2,
|
||||||
|
)
|
||||||
|
manager._send_kvcache_generic = MagicMock(return_value=0)
|
||||||
|
executor = MagicMock()
|
||||||
|
|
||||||
|
manager.send_kvcache(
|
||||||
|
"session",
|
||||||
|
np.array([1], dtype=np.int32),
|
||||||
|
[10000, 20000, 30000],
|
||||||
|
np.array([7], dtype=np.int32),
|
||||||
|
executor,
|
||||||
|
dst_device_kv_indices=np.array([21], dtype=np.int32),
|
||||||
|
)
|
||||||
|
|
||||||
|
kwargs = manager._send_kvcache_generic.call_args.kwargs
|
||||||
|
self.assertEqual(kwargs["dst_device_data_ptrs"], {20000, 30000})
|
||||||
|
|
||||||
|
def test_mooncake_transfer_metadata_carries_device_page_indices(self):
|
||||||
|
from sglang.srt.disaggregation.mooncake.conn import TransferInfo
|
||||||
|
|
||||||
|
host_pages = np.array([7, 8], dtype=np.int32)
|
||||||
|
device_pages = np.array([21, 22], dtype=np.int32)
|
||||||
|
info = TransferInfo.from_zmq(
|
||||||
|
[
|
||||||
|
b"9",
|
||||||
|
b"127.0.0.1",
|
||||||
|
b"12345",
|
||||||
|
b"session",
|
||||||
|
host_pages.tobytes(),
|
||||||
|
b"0",
|
||||||
|
b"",
|
||||||
|
b"1",
|
||||||
|
b"0",
|
||||||
|
device_pages.tobytes(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
np.testing.assert_array_equal(info.dst_kv_indices, host_pages)
|
||||||
|
np.testing.assert_array_equal(info.dst_device_kv_indices, device_pages)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user