|
1 | 1 | ### Synchronization and Asynchronous `parallel-by` |
2 | | -Similar to `inthreads`, `parallel-by` can also be made asynchronous. |
3 | 2 |
|
| 3 | +Similar to `inthreads`, `parallel-by` can also be made asynchronous by appending `.async`: |
| 4 | + |
| 5 | +```choreo |
| 6 | +parallel.async p by 6 : block { |
| 7 | + // asynchronous kernel launch (no host-side synchronization) |
| 8 | +} |
| 9 | +``` |
| 10 | + |
| 11 | +Without `.async`, the host waits for the kernel to finish before continuing: |
| 12 | + |
| 13 | +```choreo |
| 14 | +parallel p by 6 : block { |
| 15 | + // synchronous kernel launch (host blocks until completion) |
| 16 | +} |
| 17 | +// host continues only after the kernel finishes |
| 18 | +``` |
| 19 | + |
| 20 | +With `.async`, the host code continues immediately after launching the kernel. |
| 21 | +This is essential for overlapping computation with data transfers or launching |
| 22 | +independent kernels concurrently. |
| 23 | + |
| 24 | +### Stream Binding |
| 25 | + |
| 26 | +CUDA streams allow multiple kernels and memory transfers to execute concurrently |
| 27 | +on the GPU. In Choreo, you can bind a `parallel-by` block to a specific stream |
| 28 | +using angle-bracket syntax: |
| 29 | + |
| 30 | +```choreo |
| 31 | +__co__ void foo(stream s0, stream s1, f32[M] a, f32[N] b) { |
| 32 | + parallel.async<s0> p by M : block { |
| 33 | + a.at(p) = 1.0f; |
| 34 | + } |
| 35 | + parallel.async<s1> q by N : block { |
| 36 | + b.at(q) = 2.0f; |
| 37 | + } |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +In this example: |
| 42 | + |
| 43 | +- **`stream s0, stream s1`**: Stream parameters passed from the host. |
| 44 | +- **`parallel.async<s0>`**: Launches the kernel asynchronously on stream `s0`. |
| 45 | +- **`parallel.async<s1>`**: Launches a second kernel on stream `s1`. |
| 46 | + |
| 47 | +Since both kernels are launched on different streams with `.async`, they can |
| 48 | +execute concurrently on the GPU. |
| 49 | + |
| 50 | +### Stream Binding without `.async` |
| 51 | + |
| 52 | +Stream binding also works with synchronous `parallel-by`. In this case, the host |
| 53 | +launches the kernel on the specified stream and then waits for that stream to |
| 54 | +complete: |
| 55 | + |
| 56 | +```choreo |
| 57 | +__co__ void bar(stream _s, f32[M] a) { |
| 58 | + parallel<_s> p by M : block { |
| 59 | + a.at(p) = 1.0f; |
| 60 | + } |
| 61 | + // host synchronizes on stream _s before continuing |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +### Rules |
| 66 | + |
| 67 | +- Stream binding is only allowed on **block-level** parallel (`: block`). |
| 68 | +- The identifier inside `<...>` must be a `stream`-typed variable. |
| 69 | +- Without stream binding, synchronous `parallel-by` uses device-wide |
| 70 | + synchronization, and asynchronous `parallel-by` uses the default stream. |
0 commit comments