spdl.pipeline.StatsThreadOutputQueue¶
- class StatsThreadOutputQueue(info: StageInfo, *, buffer_size: int = 1, interval: float = -1)[source]¶
A thread sink with background statistics and overridable reporting hooks.
Select this class explicitly with
use_thread_output_queueinbuild()orbuild_pipeline(). Foreground operations capture numeric observations; background batches update statistics. No storage backend is required. The default callbacks use Python logging; subclasses can instead keep packets in memory, display them, or send them to an application-owned destination. SeeThreadOutputQueuefor class selection and the subclass constructor contract.- Parameters:
info – Stage identity assigned by the builder.
buffer_size – Maximum queued items; zero permits unbounded buffering.
interval – Positive seconds between interval callbacks. Nonpositive values disable interval reports, not collection or the final callback.
Example without logging or a storage backend:
from collections import deque from spdl.pipeline import ( AsyncQueue, PipelineBuilder, QueuePerfStats, StatsThreadOutputQueue, ) packets = deque(maxlen=100) class MyStatsSink(StatsThreadOutputQueue): async def interval_stats_callback(self, stats: QueuePerfStats) -> None: packets.append(stats) async def final_stats_callback(self, stats: QueuePerfStats) -> None: packets.append(stats) pipeline = ( PipelineBuilder() .add_source(range(100)) .add_sink(8) .build( num_threads=2, queue_class=AsyncQueue, task_hook_factory=lambda _: [], use_thread_output_queue=MyStatsSink, report_stats_interval=5, ) ) with pipeline.auto_stop(): for item in pipeline: print(item)
The example disables statistics on internal queues and task hooks; those choices are independent of the final sink’s callbacks. A nonpositive
report_stats_intervaldisables interval reports, not collection or the final callback. Changing logger levels does not disable measurement. SelectThreadOutputQueueto avoid sink tracking entirely.Callbacks run on the background event loop. Keep their work bounded and avoid blocking that loop. They must terminate; finalization waits for the final callback. Classes passed through subprocess configuration must be importable and picklable, like other pipeline configuration classes.
Reporting covers the producer-stage window. Items buffered when that stage finishes remain readable, but those later reads do not extend the final measurement window.
num_itemscounts queue removals during that window, including cleanup drains, not necessarily batches consumed by a training loop. Overflow of bounded numeric capture marks statistics unavailable and suppresses invalid reports with a diagnostic instead of blocking data or silently biasing percentiles. Counter-allocation exhaustion also disables reporting. Unexpected aggregation errors propagate through the collector and stage finalization rather than silently discarding stage metrics.This customization does not change stage execution or serialization. Measure the intended workload before choosing an implementation.
Benchmark comparison¶
The complete three-way comparison below measures foreground iterator handoff latency on a Linux CPU host with CPython 3.12.16. Lower is better. It compares
use_thread_output_queue=False,ThreadOutputQueueandStatsThreadOutputQueueusing the same source-to-sink workload: 500 integer items, buffer capacity 8, two configured executor workers and 20 warmup items. Timed and untimed reads alternate, giving 240 observations per cell. Each cell lists mean / p50 / p95 / p99, all in microseconds.Foreground handoff latency (mean / p50 / p95 / p99, us)¶ Foreground work (ms)
FalseThreadOutputQueueStatsThreadOutputQueue0
142.6 / 142.2 / 290.7 / 340.8
87.5 / 95.7 / 190.1 / 251.2
106.3 / 102.7 / 220.2 / 286.5
3
152.2 / 170.3 / 352.5 / 472.1
119.9 / 128.5 / 303.1 / 368.3
133.9 / 128.1 / 328.9 / 478.0
6
53.4 / 11.9 / 242.2 / 357.9
91.3 / 102.4 / 251.6 / 375.8
89.7 / 13.8 / 314.6 / 380.3
9
51.5 / 19.1 / 288.1 / 340.5
62.8 / 10.2 / 208.5 / 308.6
42.1 / 11.3 / 261.8 / 402.8
12
23.4 / 21.2 / 42.6 / 87.9
14.8 / 9.6 / 22.3 / 175.3
22.7 / 9.9 / 177.2 / 248.5
15
22.6 / 20.6 / 34.6 / 46.4
12.7 / 11.8 / 21.0 / 28.7
16.5 / 14.9 / 33.8 / 39.9
18
23.4 / 20.5 / 39.1 / 87.6
14.5 / 12.9 / 22.6 / 58.9
19.6 / 17.6 / 34.3 / 59.9
21
24.3 / 21.9 / 40.8 / 57.0
14.5 / 14.0 / 22.1 / 33.1
19.1 / 17.5 / 34.4 / 49.6
24
25.7 / 22.9 / 49.5 / 63.1
17.2 / 15.4 / 32.7 / 41.6
17.7 / 17.1 / 28.2 / 40.0
27
25.7 / 22.2 / 55.2 / 73.1
15.2 / 14.3 / 26.5 / 39.9
19.7 / 17.6 / 39.1 / 64.6
30
36.9 / 31.3 / 64.7 / 91.1
32.3 / 29.2 / 64.4 / 86.6
33.0 / 30.5 / 58.3 / 69.7
Results are one full sweep, in the listed variant order at each work setting, not interleaved repeated trials. The foreground work is a CPU busy loop that contends for the GIL, not GPU work. The timed operation is advancing a newly created pipeline iterator, not a bare queue call. The
Falsecontrol uses the built-inStatsQueue/sync-mirror sink; both thread classes are selected explicitly. Internal queues/task hooks use ordinary defaults in all variants. The statistics class retains collection and its default final reporting callback; periodic reporting is disabled. P99 has few tail samples, and these lightweight results do not establish sustained-load, GPU-overlap or training performance.Reproduce the sweep with
spdl.examples.benchmark_thread_output_queue:python -m spdl.examples.benchmark_thread_output_queue
Added in version 0.9.0.
Methods
empty()Return whether the underlying thread buffer is empty.
final_stats_callback(stats)Handle the final producer-stage packet after pending samples are flushed.
full()Return whether the underlying thread buffer is full.
get()Wait for an item through an executor without blocking the event loop.
get_nowait()Return a queued value without waiting, raising queue.Empty if unavailable.
interval_stats_callback(stats)Handle one interval packet on the background loop.
join()Wait for every queued item to be acknowledged with task_done.
put(item)Enqueue an item, delegating full or unbounded queues to an executor.
put_nowait(item)Enqueue without waiting, raising queue.Full when capacity is exhausted.
qsize()Return the number of queued items.
Own collection and reporting through stage completion and cancellation.
task_done()Acknowledge one previously enqueued item for join accounting.
Attributes
maxsizeThe configured item-capacity limit; zero means unbounded.
- async final_stats_callback(stats: QueuePerfStats) None[source]¶
Handle the final producer-stage packet after pending samples are flushed.
- Parameters:
stats – Completed cumulative stage-window statistics. The default logs them; override this method to retain or consume them.
- async interval_stats_callback(stats: QueuePerfStats) None[source]¶
Handle one interval packet on the background loop.
- Parameters:
stats – Completed interval statistics. The default logs them; override this method to choose a different destination without storage dependencies in the queue implementation.
- stage_hook() AsyncIterator[None][source]¶
Own collection and reporting through stage completion and cancellation.