Skip to content

Commit 90a3cfd

Browse files
authored
[fix] Add back TQ_ZERO_COPY_SERIALIZATION switch (#19)
## Background In previous PRs (https://gitcode.com/Ascend/TransferQueue/pull/7、https://gitcode.com/Ascend/TransferQueue/pull/19) the zero-copy transfer is refactored, and the environmental variable switch of `TQ_ZERO_COPY_SERIALIZATION` is deprecated. However, during recent tests, we observe some minor bugs in current implementation. This PR add back the `TQ_ZERO_COPY_SERIALIZATION` switch and set default value to `False`, which falls back to `pickle` to perform serialization. --------- Signed-off-by: hzy_19 <o0shower0o@outlook.com>
1 parent b6b41ae commit 90a3cfd

2 files changed

Lines changed: 40 additions & 23 deletions

File tree

transfer_queue/storage/managers/simple_backend_manager.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from transfer_queue.storage.managers.base import TransferQueueStorageManager
3131
from transfer_queue.storage.managers.factory import TransferQueueStorageManagerFactory
3232
from transfer_queue.storage.simple_backend import StorageMetaGroup
33+
from transfer_queue.utils.utils import get_env_bool
3334
from transfer_queue.utils.zmq_utils import ZMQMessage, ZMQRequestType, ZMQServerInfo, create_zmq_socket
3435

3536
logger = logging.getLogger(__name__)
@@ -44,6 +45,8 @@
4445
TQ_SIMPLE_STORAGE_MANAGER_RECV_TIMEOUT = int(os.environ.get("TQ_SIMPLE_STORAGE_MANAGER_RECV_TIMEOUT", 200)) # seconds
4546
TQ_SIMPLE_STORAGE_MANAGER_SEND_TIMEOUT = int(os.environ.get("TQ_SIMPLE_STORAGE_MANAGER_SEND_TIMEOUT", 200)) # seconds
4647

48+
TQ_ZERO_COPY_SERIALIZATION = get_env_bool("TQ_ZERO_COPY_SERIALIZATION", default=False)
49+
4750

4851
@TransferQueueStorageManagerFactory.register("AsyncSimpleStorageManager")
4952
class AsyncSimpleStorageManager(TransferQueueStorageManager):
@@ -236,7 +239,7 @@ async def _put_to_single_storage_unit(
236239
"""
237240

238241
request_msg = ZMQMessage.create(
239-
request_type=ZMQRequestType.PUT_DATA,
242+
request_type=ZMQRequestType.PUT_DATA, # type: ignore[arg-type]
240243
sender_id=self.storage_manager_id,
241244
receiver_id=target_storage_unit,
242245
body={"local_indexes": local_indexes, "data": storage_data},
@@ -331,7 +334,7 @@ async def _get_from_single_storage_unit(
331334
fields = storage_meta_group.get_field_names()
332335

333336
request_msg = ZMQMessage.create(
334-
request_type=ZMQRequestType.GET_DATA,
337+
request_type=ZMQRequestType.GET_DATA, # type: ignore[arg-type]
335338
sender_id=self.storage_manager_id,
336339
receiver_id=target_storage_unit,
337340
body={"local_indexes": local_indexes, "fields": fields},
@@ -452,6 +455,10 @@ def _filter_storage_data(storage_meta_group: StorageMetaGroup, data: TensorDict)
452455
result = (result,)
453456
results[fname] = list(result)
454457

458+
if not TQ_ZERO_COPY_SERIALIZATION:
459+
# Explicitly copy tensor slices to prevent pickling the whole tensor for every storage unit.
460+
# The tensors may still be contiguous, so we cannot use .contiguous() to trigger copy from parent tensors.
461+
results[fname] = [item.clone() if isinstance(item, torch.Tensor) else item for item in results[fname]]
455462
return results
456463

457464

transfer_queue/utils/zmq_utils.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import logging
1717
import os
18+
import pickle
1819
import socket
1920
import time
2021
from dataclasses import dataclass
@@ -28,6 +29,7 @@
2829
from transfer_queue.utils.utils import (
2930
ExplicitEnum,
3031
TransferQueueRole,
32+
get_env_bool,
3133
)
3234

3335
logger = logging.getLogger(__name__)
@@ -42,6 +44,8 @@
4244

4345
bytestr: TypeAlias = bytes | bytearray | memoryview
4446

47+
TQ_ZERO_COPY_SERIALIZATION = get_env_bool("TQ_ZERO_COPY_SERIALIZATION", default=False)
48+
4549

4650
class ZMQRequestType(ExplicitEnum):
4751
"""
@@ -155,36 +159,42 @@ def create(
155159

156160
def serialize(self) -> list:
157161
"""
158-
Serialize message using unified MsgpackEncoder.
159-
Returns: list[bytestr] - [msgpack_header, *tensor_buffers]
162+
Serialize message using unified MsgpackEncoder or pickle.
163+
Returns: list[bytestr] - [msgpack_header, *tensor_buffers] or [bytes]
160164
"""
161-
msg_dict = {
162-
"request_type": self.request_type.value, # Enum -> str for msgpack
163-
"sender_id": self.sender_id,
164-
"receiver_id": self.receiver_id,
165-
"request_id": self.request_id,
166-
"timestamp": self.timestamp,
167-
"body": self.body,
168-
}
169-
return list(_encoder.encode(msg_dict))
165+
if TQ_ZERO_COPY_SERIALIZATION:
166+
msg_dict = {
167+
"request_type": self.request_type.value, # Enum -> str for msgpack
168+
"sender_id": self.sender_id,
169+
"receiver_id": self.receiver_id,
170+
"request_id": self.request_id,
171+
"timestamp": self.timestamp,
172+
"body": self.body,
173+
}
174+
return list(_encoder.encode(msg_dict))
175+
else:
176+
return [pickle.dumps(self)]
170177

171178
@classmethod
172179
def deserialize(cls, frames: list) -> "ZMQMessage":
173180
"""
174-
Deserialize message using unified MsgpackDecoder.
181+
Deserialize message using unified MsgpackDecoder or pickle.
175182
"""
176183
if not frames:
177184
raise ValueError("Empty frames received")
178185

179-
msg_dict = _decoder.decode(frames)
180-
return cls(
181-
request_type=ZMQRequestType(msg_dict["request_type"]),
182-
sender_id=msg_dict["sender_id"],
183-
receiver_id=msg_dict["receiver_id"],
184-
body=msg_dict["body"],
185-
request_id=msg_dict["request_id"],
186-
timestamp=msg_dict["timestamp"],
187-
)
186+
if TQ_ZERO_COPY_SERIALIZATION:
187+
msg_dict = _decoder.decode(frames)
188+
return cls(
189+
request_type=ZMQRequestType(msg_dict["request_type"]),
190+
sender_id=msg_dict["sender_id"],
191+
receiver_id=msg_dict["receiver_id"],
192+
body=msg_dict["body"],
193+
request_id=msg_dict["request_id"],
194+
timestamp=msg_dict["timestamp"],
195+
)
196+
else:
197+
return pickle.loads(frames[0])
188198

189199

190200
def get_free_port() -> str:

0 commit comments

Comments
 (0)