TensorDictPipe#
- class tensordict._ucxx.TensorDictPipe(endpoint, *, consolidated: bool = True, listener: Any | None = None)#
Bases:
objectA channel between two processes that streams tensordicts over UCXX.
Warning
TensorDictPipeis experimental: its API can change in a minor release without a deprecation cycle.It needs the optional
ucxxpackage. Import it fromtensordict._ucxx: there is no public import path. Create a pipe withconnect()on one side andlisten()on the other, rather than with the constructor. The protocol does not authenticate the peer, which controls the size of the allocations: use it only on a trusted network. See Distributed tensordicts.With
consolidated=True, a pipe sends a tensordict in two phases:The first message, and each message with a new structure (keys, shapes or dtypes), carries the metadata and then the consolidated storage (see
consolidate()). The receiver allocates one buffer, and builds a tensordict whose leaves are views into it.The next messages carry the storage only. The receiver writes it into the buffer of the first message, without allocating memory or parsing metadata.
With
consolidated=False, each message carries a JSON header, with the batch size and the non-tensor entries, and then the tensor leaves of the tensordict, nested ones included, one by one. The receiver builds a new tensordict each time, with the batch size of the sent one; its nested tensordicts get the same batch size. A non-tensor entry must be aNonTensorDataorNonTensorStackwhose values arestr,int,float,bool,None, or lists or dicts (withstrkeys) of them: a send of any other non-tensor entry raises aValueError. A stack comes back with the same batch size and stack dimension.A pipe is a context manager and an async context manager, which close it at the end of the block, and an async iterator:
async for td in pipereceives until the pipe is closed.Warning
With
consolidated=True, every receive with the same structure writes into the same tensordict, and returns it. The next receive overwrites the values of the previous one. Callclone()on a received tensordict to keep it.Warning
async for td in pipestops at the first error, without raising it. To see the error, callarecv()orrecv()in a loop.- Parameters:
endpoint (ucxx.Endpoint) – the UCXX endpoint of the connection.
- Keyword Arguments:
Examples
The sender and the receiver run in two processes, on the same or on two machines. This example is not tested: it needs
ucxxand two processes.# receiver, on host "node-b" import asyncio from tensordict._ucxx import TensorDictPipe async def main(): pipe = await TensorDictPipe.listen(13337) td = await pipe.arecv() # first receive: allocates the buffer async for td in pipe: # same schema: overwrites td in place print(td["obs"].sum()) asyncio.run(main())
# sender import asyncio import torch from tensordict import TensorDict from tensordict._ucxx import TensorDictPipe async def main(): pipe = await TensorDictPipe.connect("node-b", 13337) for _ in range(10): await pipe.asend(TensorDict(obs=torch.randn(64, 84))) await pipe.aclose() asyncio.run(main())
- async aclose() None#
Closes the pipe, its endpoint and its listener (if any), asynchronously.
Closing a closed pipe does nothing.
- async arecv(td: TensorDictBase | None = None, *, device: device | str | None = None) TensorDictBase#
Receives a tensordict from the pipe, asynchronously.
With
consolidated=True, the first message, and each message with a new structure, carries the metadata: the pipe receives intotdif its consolidated layout matches, and builds a new tensordict otherwise. The next messages carry the storage only, and the pipe writes it into the same tensordict, without allocating memory. So each receive can overwrite the values of the previous one: clone a tensordict to keep it.With
consolidated=False, each call returns a new tensordict with the batch size of the sent one (an empty batch size from an older sender).- Parameters:
td (TensorDictBase, optional) – a consolidated tensordict to receive into, if its layout matches the data. Otherwise the pipe returns a new tensordict, or the tensordict of its previous receive, so use the return value. Not used with
consolidated=False. Defaults toNone, which receives into the tensordict of the previous receive.- Keyword Arguments:
device (device, str or int, optional) – the device of the storage that the pipe allocates for a new structure. Defaults to
None, which uses the device of the sender withconsolidated=True, and the CPU otherwise.- Returns:
The received tensordict.
- Raises:
RuntimeError – if the pipe is closed, or if the sender and the receiver are out of sync.
- async asend(td: TensorDictBase) None#
Sends a tensordict through the pipe, asynchronously.
With
consolidated=True, the first call, and each call where the structure of the tensordict changes, sends the metadata and then the storage. The other calls send the storage only. A tensordict that is not consolidated is consolidated on each call, which copies every leaf: consolidate it once and refresh it withupdate_()to avoid the copy.- Parameters:
td (TensorDictBase) – the tensordict to send.
- Raises:
RuntimeError – if the pipe is closed.
ValueError – with
consolidated=False, iftdholds a non-tensor entry that the pipe cannot send (see the class description).
- close() None#
Closes the pipe, and waits until it is closed.
Runs
aclose()in a new event loop. In a coroutine,await pipe.aclose()instead. A pipe also closes at the end of awithorasync withblock, and when it is garbage collected.
- async classmethod connect(host: str, port: int, *, consolidated: bool = True) TensorDictPipe#
Connects to a pipe that listens on
hostandport, and returns the pipe.The other process calls
listen()with the same port.- Parameters:
- Keyword Arguments:
consolidated (bool, optional) – if
True, sends and receives consolidated tensordicts (see the class description). Both ends must pass the same value. Defaults toTrue.- Returns:
A connected
TensorDictPipe.- Raises:
ImportError – if
ucxxis not installed.
- async classmethod listen(port: int, *, consolidated: bool = True) TensorDictPipe#
Listens on
port, and returns a pipe to the first client that connects.Waits until a client calls
connect(). The listener stops when the pipe is closed. To accept several clients, useTensorDictServer.- Parameters:
port (int) – the port to listen on.
- Keyword Arguments:
consolidated (bool, optional) – if
True, sends and receives consolidated tensordicts (see the class description). Both ends must pass the same value. Defaults toTrue.- Returns:
A connected
TensorDictPipe.- Raises:
ImportError – if
ucxxis not installed.
- recv(td: TensorDictBase | None = None, *, device: device | str | None = None) TensorDictBase#
Receives a tensordict from the pipe, and waits until it is received.
Runs
arecv()in a new event loop, with the same arguments. In a coroutine,await pipe.arecv()instead: in a thread where an event loop runs,recvraises aRuntimeError.- Parameters:
td (TensorDictBase, optional) – a consolidated tensordict to receive into, if its layout matches the data. See
arecv(). Defaults toNone.- Keyword Arguments:
device (device, str or int, optional) – the device of the storage that the pipe allocates for a new structure. See
arecv(). Defaults toNone.- Returns:
The received tensordict.
- send(td: TensorDictBase) None#
Sends a tensordict through the pipe, and waits until it is sent.
Runs
asend()in a new event loop. In a coroutine,await pipe.asend(td)instead: in a thread where an event loop runs,sendraises aRuntimeError.- Parameters:
td (TensorDictBase) – the tensordict to send.