You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
| CPU | No-op | No-op |`qd_stream` is silently ignored, kernels run serially |
14
-
| Metal | No-op | No-op |`qd_stream` is silently ignored, kernels run serially |
15
-
| Vulkan | No-op | No-op |`qd_stream` is silently ignored, kernels run serially |
16
-
17
-
On backends without native stream support, `create_stream()` and `create_event()` return objects with handle
18
-
`0`. All stream/event operations become no-ops and kernels run serially. Code written with streams is portable across all backends in the sense that it will run without modifications, but serially.
6
+
## Stream parallelism
19
7
20
-
## Creating and using streams
8
+
Inside a `@qd.kernel`, each `with qd.stream_parallel():` block runs on its own GPU stream. The runtime
9
+
creates temporary streams, launches the for loops, and synchronizes automatically before the next
10
+
non-parallel statement.
21
11
22
12
```python
23
13
import quadrants as qd
@@ -27,17 +17,56 @@ qd.init(arch=qd.cuda)
27
17
N =1024
28
18
a = qd.field(qd.f32, shape=(N,))
29
19
b = qd.field(qd.f32, shape=(N,))
20
+
c = qd.field(qd.f32, shape=(N,))
30
21
31
22
@qd.kernel
32
-
deffill_a():
33
-
for i inrange(N):
34
-
a[i] =1.0
23
+
defcompute_ab():
24
+
with qd.stream_parallel():
25
+
for i inrange(N):
26
+
a[i] = compute_a(i)
27
+
with qd.stream_parallel():
28
+
for j inrange(N):
29
+
b[j] = compute_b(j)
35
30
36
31
@qd.kernel
37
-
deffill_b():
32
+
defcombine():
38
33
for i inrange(N):
39
-
b[i] =2.0
34
+
c[i] = a[i] + b[i]
35
+
36
+
compute_ab() # the two stream_parallel blocks run concurrently
37
+
combine() # runs after compute_ab() returns — a[] and b[] are ready
38
+
```
39
+
40
+
Consecutive `with qd.stream_parallel():` blocks run concurrently. Multiple for loops within a single block
41
+
share a stream and run serially on it. All streams are synchronized before the kernel returns.
42
+
43
+
### Restrictions
44
+
45
+
- All top-level statements in a kernel must be either all `stream_parallel` blocks or all regular statements.
46
+
Mixing the two at the top level is a compile-time error.
47
+
- Nesting `stream_parallel` blocks is not supported.
40
48
49
+
## Supported platforms
50
+
51
+
| Backend | Streams | Events |
52
+
|---------|---------|--------|
53
+
| CUDA | Yes | Yes |
54
+
| AMDGPU | Yes | Yes |
55
+
| CPU | No-op | No-op |
56
+
| Metal | No-op | No-op |
57
+
| Vulkan | No-op | No-op |
58
+
59
+
On backends without native stream support, stream operations are no-ops and for loops run serially. Code using
60
+
streams is portable across all backends — it will run without modifications, but serially.
61
+
62
+
## Explicit streams
63
+
64
+
For cases that require manual control — such as launching separate kernels on different streams or
65
+
interoperating with PyTorch — you can create and manage streams directly.
66
+
67
+
### Creating and using streams
68
+
69
+
```python
41
70
s1 = qd.create_stream()
42
71
s2 = qd.create_stream()
43
72
@@ -54,7 +83,7 @@ s2.destroy()
54
83
Pass `qd_stream=` to any kernel call to launch it on that stream. Kernels on different streams may execute
55
84
concurrently. Call `synchronize()` to block until all work on a stream completes.
56
85
57
-
## Events
86
+
###Events
58
87
59
88
Events let you express dependencies between streams without full synchronization.
60
89
@@ -89,7 +118,7 @@ s2.destroy()
89
118
`e.record(stream)` captures the point in `stream`'s execution. `e.wait(qd_stream=stream)` makes `stream` wait
90
119
until the recorded point is reached. If `qd_stream` is omitted, the default stream waits.
91
120
92
-
## Context managers
121
+
###Context managers
93
122
94
123
Streams and events support `with` blocks for automatic cleanup:
95
124
@@ -100,13 +129,13 @@ with qd.create_stream() as s:
100
129
# s.destroy() called automatically
101
130
```
102
131
103
-
## PyTorch interop (CUDA)
132
+
###PyTorch interop (CUDA)
104
133
105
134
When mixing Quadrants kernels with PyTorch operations on CUDA, both frameworks must use the same stream to
106
135
avoid race conditions. Without explicit stream management, Quadrants and PyTorch may launch work on different
107
136
streams with no ordering guarantees, leading to intermittent data corruption.
0 commit comments