TensorPlay
latest (dev)
Copy
View Markdown

Latest development documentation · Updated 2026-10-15

Source code for tensorplay.cuda.streams

# mypy: allow-untyped-defs
# pylint: disable=useless-parent-delegation
r"""CUDA stream and event wrappers.

The underlying objects are ``tensorplay._C._cuda._CudaStream`` /
``tensorplay._C._cuda._CudaEvent`` when native CUDA support is available.
"""

from __future__ import annotations

from typing import Any, Optional

import tensorplay
import tensorplay._C
from tensorplay._C import Device
from tensorplay._C import _cuda as _lcuda

from tensorplay._streambase import EventBase, StreamBase

from ._utils import _dummy_type, _get_device_index


if not hasattr(_lcuda, "_CudaStream"):
    # Define dummy base classes (build without CUDA support)
    _CudaStreamBase = _dummy_type("_CudaStream")
    _CudaEventBase = _dummy_type("_CudaEvent")
    _has_core = False
else:
    _CudaStreamBase = _lcuda._CudaStream
    _CudaEventBase = _lcuda._CudaEvent
    _has_core = True



[docs]
class Stream(StreamBase):
    r"""Wrapper around a CUDA stream.

    A CUDA stream is a linear sequence of execution that belongs to a specific
    device, independent from other streams. It supports with statement as a
    context manager to ensure the operators within the with block are running
    on the corresponding stream.  See the CUDA semantics documentation for details.

    Args:
        device(tensorplay.Device or int, optional): a device on which to allocate
            the stream. If :attr:`device` is ``None`` (default) or a negative
            integer, this will use the current device.
        priority(int, optional): priority of the stream, which can be positive, 0, or negative.
            A lower number indicates a higher priority. By default, the priority is set to 0.

    """

    def __new__(cls, device=None, priority=0, **kwargs):
        if not _has_core:
            raise RuntimeError("tensorplay.cuda.Stream requires CUDA support")
        core_stream = kwargs.pop("_stream", None)
        if core_stream is None:
            from . import device as device_ctx

            idx = _get_device_index(device, optional=True)
            with device_ctx(idx):
                core_stream = _lcuda.get_stream_from_pool(int(priority), idx)
        self = super().__new__(cls)
        self.__dict__["_stream"] = core_stream
        return self

    @property
    def device(self) -> Device:
        return self._stream.device

    @property
    def cuda_stream(self) -> int:
        return self._stream.cuda_stream

    @property
    def stream_id(self) -> int:
        return self._stream.cuda_stream

    @property
    def device_index(self) -> int:
        return int(self.device.index)

    @property
    def device_type(self) -> str:
        return "cuda"


[docs]
    def wait_event(self, event: "Event | tensorplay.Event") -> None:
        r"""Make all future work submitted to the stream wait for an event."""
        event.wait(self)



[docs]
    def wait_stream(self, stream: "Stream") -> None:
        r"""Synchronize with another stream."""
        self.wait_event(stream.record_event())



[docs]
    def record_event(self, event: Optional["Event"] = None):
        r"""Record an event.

        Args:
            event (Event, optional): event to record. If not given, a new one
                will be allocated.

        Returns:
            Recorded event.
        """
        if event is None:
            event = Event()
        event.record(self)
        return event



[docs]
    def query(self) -> bool:
        r"""Check if all the work submitted has been completed."""
        return self._stream.query()



[docs]
    def synchronize(self) -> None:
        r"""Wait for all the kernels in this stream to complete."""
        self._stream.synchronize()


    def __enter__(self):
        from . import StreamContext

        self._ctx = StreamContext(self)
        self._ctx.__enter__()
        return self

    def __exit__(self, type: Any, value: Any, traceback: Any):
        from . import StreamContext

        ctx = getattr(self, "_ctx", None)
        if ctx is not None:
            ctx.__exit__(type, value, traceback)
        return False

    def __eq__(self, o) -> bool:
        if isinstance(o, Stream):
            return bool(self._stream == o._stream)
        return False

    def __hash__(self):
        return hash((self.cuda_stream, self.device.index))

    def __repr__(self):
        return f"<tensorplay.cuda.Stream device={self.device} cuda_stream={self.cuda_stream:#x}>"




[docs]
class ExternalStream(Stream):
    r"""Wrapper around an externally allocated CUDA stream.

    This class is used to wrap streams allocated in other libraries in order
    to facilitate data exchange and multi-library interactions.

    .. note:: This class doesn't manage the stream life-cycle, it is the user
       responsibility to keep the referenced stream alive while this class is
       being used.

    Args:
        stream_ptr(int): Integer representation of the `cudaStream_t` value
            allocated externally.
        device(tensorplay.Device or int, optional): the device where the stream
            was originally allocated. If device is specified incorrectly,
            subsequent launches using this stream may fail.
    """

    def __new__(cls, stream_ptr, device=None, **kwargs):
        if not _has_core:
            raise RuntimeError("tensorplay.cuda.Stream requires CUDA support")
        raise RuntimeError(
            "External streams are not exposed by this TensorPlay build"
        )




[docs]
class Event(EventBase):
    r"""Wrapper around a CUDA event.

    CUDA events are synchronization markers that can be used to monitor the
    device's progress, to accurately measure timing, and to synchronize CUDA
    streams.

    Args:
        enable_timing (bool, optional): indicates if the event should measure time
            (default: ``False``)
        blocking (bool, optional): if ``True``, :meth:`wait` will be blocking (default: ``False``)
        interprocess (bool): if ``True``, the event can be shared between processes
            (default: ``False``)
    """

    def __new__(
        cls, enable_timing=False, blocking=False, interprocess=False, external=False
    ):
        if not _has_core:
            raise RuntimeError("tensorplay.cuda.Event requires CUDA support")
        self = super().__new__(cls)
        self.__dict__["_event"] = _lcuda._CudaEvent(
            enable_timing, blocking, interprocess
        )
        return self


[docs]
    @classmethod
    def from_ipc_handle(cls, device, handle):
        r"""Reconstruct an event from an IPC handle on the given device."""
        raise RuntimeError("IPC events are not supported by this TensorPlay build")


    @property
    def device(self):
        return self._event.device

    @property
    def cuda_event(self) -> int:
        return self._event.cuda_event


[docs]
    def record(self, stream: Optional[Stream] = None):
        r"""Record the event in a given stream.

        Args:
            stream (Stream, optional): Uses ``tensorplay.cuda.current_stream()`` if no stream is specified.
        """
        if stream is None:
            from . import current_stream

            stream = current_stream()
        self._event.record(stream._stream)



[docs]
    def wait(self, stream: Optional[Stream] = None) -> None:
        r"""Make all future work submitted to the given stream wait for this event.

        Args:
            stream (Stream, optional): Uses ``tensorplay.cuda.current_stream()`` if no stream is specified.
        """
        if stream is None:
            from . import current_stream

            stream = current_stream()
        self._event.wait(stream._stream)



[docs]
    def query(self):
        r"""Check if all work currently captured by event has completed."""
        return self._event.query()



[docs]
    def elapsed_time(self, end_event: "Event"):
        r"""Return the time elapsed.

        Time reported in milliseconds after the event was recorded and
        before the end_event was recorded.

        Args:
            end_event (Event): the end event.
        """
        return self._event.elapsed_time(end_event._event)



[docs]
    def synchronize(self) -> None:
        r"""Wait for the event to complete."""
        self._event.synchronize()



[docs]
    def ipc_handle(self):
        r"""Return an IPC handle of this event."""
        raise RuntimeError("IPC events are not supported by this TensorPlay build")


    def __repr__(self) -> str:
        if self.cuda_event:
            return f"<tensorplay.cuda.Event {self.cuda_event:#x}>"
        else:
            return "<tensorplay.cuda.Event uninitialized>"
Ask DeepWiki