Skip to content

[doc-only] cuda_core: add "10 minutes to cuda.core" tutorial to docs - #2289

Merged
Andy-Jost merged 2 commits into
NVIDIA:mainfrom
sri-koundinyan:docs/cuda-core-10min
Jul 8, 2026
Merged

[doc-only] cuda_core: add "10 minutes to cuda.core" tutorial to docs#2289
Andy-Jost merged 2 commits into
NVIDIA:mainfrom
sri-koundinyan:docs/cuda-core-10min

Conversation

@sri-koundinyan

@sri-koundinyansri-koundinyan commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

This adds a beginner-friendly "10 minutes to cuda.core" guide to the cuda.core docs. It walks through the core workflow with small, runnable snippets: selecting a device, compiling a CUDA C++ kernel, allocating memory, copying, launching, timing with events, using multiple streams, capturing a CUDA graph, and interoperating with CuPy and PyTorch. The new page lives at cuda_core/docs/source/10_minutes_to_cuda_core.rst and is added to the docs table of contents between the Installation and Examples sections. All code snippets were verified to run end-to-end on CUDA 13.

@github-actionsgithub-actionsBot added the cuda.core Everything related to the cuda.core module label Jul 1, 2026
@github-actions

This comment has been minimized.

Add a beginner-friendly "10 minutes to cuda.core" guide that walks through
the core workflow (select a device, compile a kernel, allocate memory, copy,
launch, time with events, use multiple streams, capture a CUDA graph, and
interoperate with CuPy/PyTorch), then wire it into the cuda.core docs table of
contents between the installation and examples sections.
Signed-off-by: Sri Koundinyan <skoundinyan@nvidia.com>
@leofangleofang added the PR review get-together Mark PRs you'd like the team to review at the weekly PR review get-together. label Jul 6, 2026

@Andy-JostAndy-Jost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a great addition to the docs. Blocking merge for a few minor correctness issues but mostly it looks fine to me.

:doc:`examples <examples>` show this scaled up across multiple GPUs.


Capturing work in a CUDA graph

@Andy-JostAndy-JostJul 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth adding a sentence to note that cuda.core also provides an explicit graph interface. It can be used to build, inspect, and modify graphs (including graphs built via stream capture). There are lots of examples under cuda_core/tests/graph.

@sri-koundinyansri-koundinyanJul 8, 2026

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, added a short paragraph pointing to the explicit cuda.core.graph interface with a link to the graph tests.


.. code-block:: console

$ pip install cuda-core[cu13]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Square brackets require being quoted in most shells.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


.. code-block:: python

dev = Device() # device 0 by default; Device(1) selects another GPU

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is misleading. Device() returns the current device, while falling back to device 0 if none is current.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: now "current device (device 0 if none is current)".

Comment on lines +79 to +81
:class:`Device` objects are thread-local singletons: ``Device(0)`` always hands
you back the same object for device 0 on that thread, so libraries sharing your
process see and use the same GPU. Rich device attributes live under

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a false cause-and-effect. Thread-locality is not the reason why libraries can share the same device.

What users may observe is that on a given thread Device(0) always hands back the same object for device 0, so its context, default memory resource, and cached attributes are reused rather than rebuilt on each call.

On different threads, Device(0) returns different objects bound to distinct contexts. This makes it possible to interact with CUDA from multiple threads in cuda.core.

@sri-koundinyansri-koundinyanJul 8, 2026

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Updated to include a short note (same-thread Device(0) returns the same object, so repeat calls are cheap) and left the primary-context sharing content to the interop section.

try:
Program(bad, code_type="c++", options=opts).compile("cubin")
except Exception as e:
print(e) # includes: error: identifier "not_a_real_symbol" is undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could say assert 'identifier "not_a_real_symbol" is undefined' in str(e)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Capturing work in a CUDA graph
------------------------------

When you launch the same sequence of operations repeatedly, per-launch CPU

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use "launch" to refer to an arbitrary CUDA operation. CUDA graphs capture many types of operations, and kernel launches are only one type.

@sri-koundinyansri-koundinyanJul 8, 2026

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded to "operations" rather than "launches". "launch" now only refers to launching the graph itself.

------------------------------

When you launch the same sequence of operations repeatedly, per-launch CPU
overhead adds up. A CUDA graph lets you record that sequence once and replay it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Sequence" here is potentially confusing. In general, a graph is not a sequence.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Removed "sequence" from the prose and the code comment.


When you launch the same sequence of operations repeatedly, per-launch CPU
overhead adds up. A CUDA graph lets you record that sequence once and replay it
with a single launch. Use the stream's graph builder: begin building, issue the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The graph lets you replay a computation many times without incurring the cost of CUDA API calls each time.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A slight reframing: "record that work once and then launch the whole graph repeatedly, instead of re-issuing each operation every time."

ts = dev.create_stream(PyTorchStreamWrapper(torch.cuda.current_stream()))
launch(ts, config, scale, t.data_ptr(), np.float32(2.0), np.uint64(t.numel()))
ts.sync()
assert torch.allclose(t, torch.full_like(t, 2.0))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact comparison is appropriate here. It could match line 342 exactly or at least use torch.equal instead of allclose.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done

Comment on lines +412 to +414
# or scope a buffer with a with-statement:
with dev.allocate(nbytes, stream=stream) as tmp:
... # tmp is freed at the end of the block

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leofang I'm surprised to see context manager support in Buffer. I'm for it, but I thought we were avoiding context managers in cuda.core. FYI, it was added in PR #1701 ("Make GraphicsResource inherit from Buffer").

Users reading this might expect other cuda.core objects with close members to support this as well. Is that something we can add?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there was a special reason why graphic resource works differently. Are we teaching it here? If not, we should not teach about context managers.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the context-manager example

for _ in range(100):
launch(stream, config, scale, dbuf, np.float32(1.0), np.uint64(n))
stream.record(end)
end.sync()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be stream.sync()?

@sri-koundinyansri-koundinyanJul 8, 2026

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I kept end.sync(). It matches the pattern in the Event docstring. But stream.sync() would also work since end is the last op.

# PyTorch: wrap torch's stream via the __cuda_stream__ protocol
import torch

class PyTorchStreamWrapper:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adapter is no longer needed. You can use

ts = dev.create_stream(torch.cuda.current_stream())

below.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But note my comment here: #2305 (comment)

@sri-koundinyansri-koundinyanJul 8, 2026

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped the wrapper. Now dev.create_stream(torch.cuda.current_stream()) directly.

@leofangleofang added documentation Improvements or additions to documentation P1 Medium priority - Should do labels Jul 7, 2026
Mainly: fix the Device()/thread-local wording for accuracy, simplify the
PyTorch stream example (torch supports __cuda_stream__ natively), stop
teaching Buffer as a context manager, and tidy up the CUDA graph section.
Signed-off-by: Sri Koundinyan <skoundinyan@nvidia.com>

@Andy-JostAndy-Jost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks for the updates.

@Andy-JostAndy-Jost changed the title cuda_core: add "10 minutes to cuda.core" tutorial to docs[doc-only] cuda_core: add "10 minutes to cuda.core" tutorial to docsJul 8, 2026
@Andy-JostAndy-Jost added this to the cuda.core v1.1.0 milestone Jul 8, 2026
@Andy-JostAndy-Jost self-assigned this Jul 8, 2026
@Andy-JostAndy-Jost mentioned this pull request Jul 8, 2026
14 tasks
@Andy-Jost
Andy-Jost merged commit b1c474c into NVIDIA:mainJul 8, 2026
104 of 106 checks passed
@github-actions

Copy link
Copy Markdown
Doc Preview CI
Preview removed because the pull request was closed or merged.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cuda.coreEverything related to the cuda.core moduledocumentationImprovements or additions to documentationP1Medium priority - Should doPR review get-togetherMark PRs you'd like the team to review at the weekly PR review get-together.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@sri-koundinyan@leofang@juenglin@Andy-Jost