CHAPTER 7
What if lanes need different calculations?
Follow an elementwise condition from input signs to participating lanes, then separate different paths from an incomplete warp.
Start with the operation
PyTorch
import torch
values = [2.] * 32
x = torch.tensor(values[:32], device="cuda")
y = torch.where(x > 0, x * 2, -x)
print(y.cpu())This small activation-like operation doubles positive inputs and negates negative inputs. torch.where(condition, a, b) selects an output value from a where the condition is true and from b otherwise. The opening has 32 inputs so we can inspect one full warp.
You choose the condition and expressions; PyTorch chooses its implementations and launches. Python evaluates the two expressions supplied to torch.where, so this source is not evidence of a GPU branch. Our browser model assigns one input to each participating lane and explicitly runs the non-empty paths. It illustrates lane participation, not PyTorch compiler output.
Run the examples below in your browser with no installation or GPU required. The browser simulates the calculation; it does not execute PyTorch or GPU code. To run the PyTorch snippet itself, use a Python environment with PyTorch and an NVIDIA GPU.
Idea 1 of 3
Start with every lane on the same path
Definitions
- Path
- The sequence of operations chosen by one outcome of a condition. Here the positive path doubles an input; the negative path negates it.
- Lane mask
- The set of lanes participating in one issued operation. A lane outside that mask does no work for that operation.
All 32 lanes begin with the same sign. A non-empty path issues its operations; an empty path is skipped. In this first experiment each path has one operation, so changing from all positive to all negative changes the selected arithmetic without creating mixed participation.
The dynamic PyTorch example follows the sign control. The diagram highlights the selected operation’s mask; the lane cards include final outputs for checking the calculation.
Try it
Predict how many paths are needed when all inputs are positive. Run and inspect the mask. Change only the signs to all negative, run again, and explain whether two paths are now needed.
Inspect the PyTorch example for these settings
PyTorch
import torch
values = [2.] * 32
x = torch.tensor(values[:32], device="cuda")
y = torch.where(x > 0, x * 2, -x)
print(y.cpu())Predict the result, then press Run.
- Paths used
- 1
- Path operations
- 1
Positive: multiply by 2: 32 lanes participate.
Inspect the lane indices for each path
| Operation | Participating lane indices |
|---|---|
| Positive: multiply by 2 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 |
Each card shows input → final output. “Now” participates in the selected operation; “Other path” is masked off now; “Unused” has no input throughout.
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
All participating lanes follow the same path. The other path has no participating lanes, so the model skips it.
Idea 2 of 3
Let a warp contain both signs
Definitions
- Divergence
- Different participating lanes of one warp taking different paths through a condition.
- Lane slot
- One lane’s opportunity to take part in an issued operation. This model counts 32 slots for every path operation.
With both signs present, the model runs each non-empty path using its own mask. It issues positive-path operations first, then negative-path operations. Lanes on the other path wait without contributing to the current operation. The displayed order is a teaching choice, not a CUDA guarantee.
Begin with one operation per path. Then change the negative expression from -x to -x * 2 + 1: negate, multiply, add. Even one negative lane causes that non-empty path to appear. Useful lane slots count participating lanes across all issued operations; lane-slot use divides that count by all 32-slot operations.
These counts cover only the listed arithmetic. A compiler can combine operations or execute them conditionally, so this trace does not establish compiled instruction counts or timings.
Try it
Predict which masks appear for alternating signs, then run. Change only the pattern to one negative lane. Next change only the negative path from one to three operations. Explain why the rare path still appears in the trace.
Inspect the PyTorch example for these settings
PyTorch
import torch
values = [2., -2.] * 16
x = torch.tensor(values[:32], device="cuda")
y = torch.where(x > 0, x * 2, -x)
print(y.cpu())Predict the result, then press Run.
- Paths used
- 2
- Path operations
- 2
- Useful lane slots
- 32
- Lane-slot use
- 50%
Positive: multiply by 2: 16 lanes participate.
Inspect the lane indices for each path
| Operation | Participating lane indices |
|---|---|
| Positive: multiply by 2 | 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 |
| Negative: negate | 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 |
Each card shows input → final output. “Now” participates in the selected operation; “Other path” is masked off now; “Unused” has no input throughout.
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
2 → 4
Now
-2 → 2
Other path
32 useful lane slots out of 64 possible slots. Empty paths are skipped. Both different paths and unused tail positions can reduce this ratio; these are explicit path operations, not compiled instructions or timings.
Idea 3 of 3
Separate unused lanes from different paths
Return to the five-value scale of earlier chapters. Here only the first five lanes have inputs. The remaining positions are unused throughout the calculation. That differs from a participating lane masked off for one path that later takes part in its own path.
Keep all inputs positive and vary the input count first. Lane-slot use can fall even when there is only one path. Then keep five inputs and switch to alternating signs to see both effects together. Empty positions in this warp are not filled with another block’s threads; other resident warps may still make progress, as chapter 6 showed.
Try it
With five positive inputs, predict whether the low participation means divergence. Run, then change only the signs to alternating. Explain which lane positions are unused throughout and which participate on a different path.
Inspect the PyTorch example for these settings
PyTorch
import torch
values = [2.] * 32
x = torch.tensor(values[:5], device="cuda")
y = torch.where(x > 0, x * 2, -x)
print(y.cpu())Predict the result, then press Run.
- Paths used
- 1
- Path operations
- 1
- Useful lane slots
- 5
- Lane-slot use
- 15.625%
Positive: multiply by 2: 5 lanes participate.
Inspect the lane indices for each path
| Operation | Participating lane indices |
|---|---|
| Positive: multiply by 2 | 0, 1, 2, 3, 4 |
Each card shows input → final output. “Now” participates in the selected operation; “Other path” is masked off now; “Unused” has no input throughout.
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
2 → 4
Now
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
—
Unused
5 useful lane slots out of 32 possible slots. Empty paths are skipped. Both different paths and unused tail positions can reduce this ratio; these are explicit path operations, not compiled instructions or timings.
Try a new case
Apply it
Compare five all-positive inputs with 32 inputs containing one negative value. Which case has different paths within the warp? Can a low lane-slot use alone tell you the cause?
Compare your answer
The 32-input case has both paths, because lane 0 is negative and the others are positive. Five all-positive inputs use only one path but leave 27 positions unused. A low lane-slot use alone cannot distinguish incomplete input participation from mixed paths; inspect the actual masks.
Run the example on your desktop
Download this chapter’s opening PyTorch example. See the complete example index and setup appendix for CPU PyTorch, the supported GPGPU-Sim programs and optional NVIDIA GPU execution.
The browser still requires no installation. Downloads are generated from the displayed source; CPU checks and simulator checks do not establish GPU performance.
References
Use these official references to check the concepts and code on this page.
- PyTorch: torch.where (new tab)
Check how the opening operation selects a result for each condition.
- CUDA Best Practices: branching and divergence (new tab)
Check the warp-level scope of divergent paths and predication.
These browser exercises do not run an installed PyTorch or CUDA toolkit. For optional local execution, choose documentation matching your installed tools. Links open online in a new tab; the questions and answer comparisons below work offline.
Use the documentation
Use the references to explain each answer, then apply it to this page’s example. No installation is needed.