Writing Pre-Collected Data#
NVTX code annotations and NVTX Writer solve different problems:
Use the regular NVTX API (
nvtx.Domain.push_range,nvtx.Domain.pop_range, etc.) to annotate work as it executes. A profiling tool observes those calls while the application is running.Use a writer to submit events and counter samples that already have timestamps. A writer backend consumes the data and decides where to store, merge, or display it.
Backends and Integrations#
The nvtx.writer module defines the backend-independent writer API. A
backend implementation must be available before a session can write data.
For example, NVIDIA Nsight Systems provides the nsys_writer integration.
See the Nsight Systems documentation
for its setup and specific usage.
Backend configuration and output behavior are integration-specific.
Session and Basic Events#
A nvtx.writer.Session owns one collection of output data. Within a
session, a nvtx.writer.Domain namespaces registered objects, while a
nvtx.writer.Stream represents a logical source such as a thread,
process, rank, or device stream.
Create sessions and streams with context managers so their output is finalized
even if writing fails. For individual events, use
nvtx.writer.Stream.write_mark(),
write_pushpop(), or
write_startend().
from nvtx.writer import Session, load_backend
# The integration providing the backend defines its path and configuration.
backend = load_backend("/path/to/backend/library")
with Session("Example Writer", backend=backend) as session:
domain = session.get_domain("My Domain")
with session.create_stream("My Stream", domain=domain) as stream:
stream.write_mark(100, message="My Mark")
stream.write_pushpop(
start=200,
end=500,
message="My Message",
color="green",
)
The following sections reuse the domain and stream names from this
setup.
Counters and Batches#
Regular NVTX counters submit annotations with nvtx.Counter.sample().
Writer counter samples are instead submitted to a stream with an explicit
timestamp.
Register a counter with nvtx.writer.Domain.get_counter(),
then submit values with nvtx.writer.Stream.write_counter_sample() or
write_counter_batch(). Each sample has a timestamp
in the stream’s time domain; a batch pairs values and timestamps by position.
counter = domain.get_counter("My Counter", int)
# Write one sample ...
stream.write_counter_sample(counter, timestamp=600, value=12)
# ... or submit several samples together.
stream.write_counter_batch(
counter,
[10, 8, 4],
timestamps=[700, 800, 900],
)
Generic Events#
Generic events are useful when a mark or range needs additional data fields.
A structured NumPy dtype defines the layout: annotated fields provide the
event’s timing and message, while ordinary fields carry application-specific
data. Register it with nvtx.writer.Domain.get_schema(), then submit a row
with nvtx.writer.Stream.write_event().
import numpy as np
import nvtx
event_dtype = np.dtype([
("start", nvtx.numpy_dtype(
np.int64, entry_kind=nvtx.EntryKind.RANGE_BEGIN)),
("end", nvtx.numpy_dtype(
np.int64, entry_kind=nvtx.EntryKind.RANGE_END)),
("message", nvtx.numpy_dtype(
(np.str_, 32), entry_kind=nvtx.EntryKind.MESSAGE)),
("items", np.int64),
("input_bytes", np.int64),
])
event_schema = domain.get_schema(
event_dtype,
kind=nvtx.EventKind.RANGE_STARTEND,
)
stream.write_event(
event_schema,
(1_000, 1_500, "process items", 64, 16_384),
)
Use nvtx.writer.Stream.write_event_batch() to submit multiple rows with
the same schema. See the Writer API for all supported operations
and their dtype requirements.