Selkies
Developer Referencewebrtc_utils

GPUMonitor

Periodically samples GPU load and memory for the pipeline's card.

Each sample is delivered through the optional async on_stats callback as (load, mem_total, mem_used). GPU queries run in a worker thread so sampling never blocks the event loop. When no GPU is found on the first probe, the loop exits instead of polling forever.

Attributes

attributeperiod
= max(1, int(period))
attributeenabled
= enabled
attributegpu_id
= gpu_id
attributedri_node
= dri_node

The render node the pipeline captures/encodes on; stats must describe the same card, so get_gpus filters by it when set.

attributestop_event
= asyncio.Event()
attributetaskOptional[asyncio.Task]
= None
attributeon_statsOptional[Callable[..., Awaitable[None]]]
= None

Functions

func__init__(self, gpu_id=0, period=1, enabled=True, dri_node='')
Source Code
def __init__(self, gpu_id: int = 0, period: int = 1, enabled: bool = True, dri_node: str = ""):
    self.period = max(1, int(period))
    self.enabled = enabled
    self.gpu_id = gpu_id
    self.dri_node = dri_node
    self.stop_event = asyncio.Event()
    self.task: Optional[asyncio.Task] = None
    self.on_stats: Optional[Callable[..., Awaitable[None]]] = None
paramself
paramgpu_idint
= 0
paramperiodint
= 1
paramenabledbool
= True
paramdri_nodestr
= ''

Returns

None
funcstart(self) -> None

Starts the sampling task; no-op when disabled.

Source Code
def start(self) -> None:
    """Starts the sampling task; no-op when disabled."""
    if not self.enabled:
        return
    self.stop_event.clear()
    self.task = asyncio.create_task(self._monitor_loop())
    logger_gpu.info("GPU monitor started")
paramself

Returns

None
func_get_gpu_stats(self) -> Optional[Tuple]

Returns (load, mem_total, mem_used) for the target GPU; blocking.

A dri_node match already narrows get_gpus to the pipeline's card; gpu_id indexes only the unfiltered list.

Source Code
def _get_gpu_stats(self) -> Optional[Tuple]:
    """Returns `(load, mem_total, mem_used)` for the target GPU; blocking.

    A `dri_node` match already narrows `get_gpus` to the pipeline's card;
    `gpu_id` indexes only the unfiltered list.

    Returns:
        The stats tuple, or None when the GPU cannot be found or queried.
    """
    try:
        gpus = gpu_stats.get_gpus(self.dri_node)
        idx = 0 if (self.dri_node and len(gpus) == 1) else self.gpu_id
        if not gpus or idx >= len(gpus):
            return None
        gpu = gpus[idx]
        return (gpu.load, gpu.memoryTotal, gpu.memoryUsed)
    except Exception as e:
        logger_gpu.warning(f"Error while fetching GPU stats: {e}")
        return None
paramself

Returns

typing.Optional

The stats tuple, or None when the GPU cannot be found or queried.

func_monitor_loop(self) -> None

Samples until stopped; exits at once when the first probe finds no GPU.

Nothing is substituted for a missing GPU: CPU load and system memory are SystemMonitor's, and the GPU gauge contract (fractional load, MiB memory) cannot carry them without unit errors.

Source Code
async def _monitor_loop(self) -> None:
    """Samples until stopped; exits at once when the first probe finds no GPU.

    Nothing is substituted for a missing GPU: CPU load and system memory
    are `SystemMonitor`'s, and the GPU gauge contract (fractional load, MiB
    memory) cannot carry them without unit errors.
    """
    try:
        if await asyncio.to_thread(self._get_gpu_stats) is None:
            logger_gpu.info(
                f"No GPU with ID {self.gpu_id} found; GPU stats disabled "
                "(CPU and system memory are reported by the system monitor)."
            )
            return
        while not self.stop_event.is_set():
            stats = await asyncio.to_thread(self._get_gpu_stats)
            if stats is not None and self.on_stats:
                load, mem_total, mem_used = stats
                await self.on_stats(load, mem_total, mem_used)
            try:
                await asyncio.wait_for(self.stop_event.wait(), timeout=self.period)
            except asyncio.TimeoutError:
                pass
    except asyncio.CancelledError:
        pass
    except Exception as e:
        logger_gpu.error(f"GPU monitor error: {e}", exc_info=True)
    finally:
        logger_gpu.debug("GPU monitor loop exited")
paramself

Returns

None
funcstop(self) -> None

Signals the loop to exit and waits for the task to finish.

Source Code
async def stop(self) -> None:
    """Signals the loop to exit and waits for the task to finish."""
    self.stop_event.set()
    if self.task:
        await self.task
    logger_gpu.info("GPU monitor stopped")
paramself

Returns

None

On this page

Edit on GitHub