cubek_std/plane_flow.rs
1//! Plane-flow vocabulary used by stage / global readers / partition tiles.
2//! All types live here:
3//! - comptime data: [`PlaneFlowCounts`], [`PlaneFlowPartitionRule`],
4//! [`PlaneFlowConfig`], [`InputLoadFlow`]
5//! - runtime cube types: [`PartitionThreshold`], [`PlaneFlowPartition`]
6//! - composition helper: [`partition_coordinates`]
7//!
8//! cubek-matmul (and other consumers) re-export these to keep their own paths
9//! stable; the canonical home is here so tile-level primitives can compose
10//! plane-flow logic without reaching into cubek-matmul.
11
12use cubecl::{prelude::*, std::tensor::layout::Coords2d};
13
14use crate::tile::Partitioner;
15
16// ============================================================================
17// Comptime data: plane counts, partition rule, full config, input-load flow.
18// ============================================================================
19
20#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
21/// Represents how many planes are used for main computation and for loading-only tasks.
22pub struct PlaneFlowCounts {
23 /// Number of planes participating in main flow and (possibly) loading.
24 pub main_flow: u32,
25 /// Number of planes dedicated solely to loading.
26 pub load_only: u32,
27}
28
29impl PlaneFlowCounts {
30 /// Return the total number of planes
31 pub fn total_count(&self) -> u32 {
32 self.main_flow + self.load_only
33 }
34}
35
36#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
37/// How planes are partitioned by id between the main flow and load-only roles.
38pub enum PlaneFlowPartitionRule {
39 MainFlowOnly,
40 LoadOnlyFirst { load_only: u32 },
41 LoadOnlyLast { main_flow: u32 },
42}
43
44#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
45/// Plane-flow configuration carried by `CubeDimResource::Specialized`. Holds the
46/// counts for main-flow vs load-only planes and the partition rule used at
47/// runtime.
48pub struct PlaneFlowConfig {
49 pub counts: PlaneFlowCounts,
50 pub partition_rule: PlaneFlowPartitionRule,
51}
52
53impl PlaneFlowConfig {
54 /// All planes participate in the main flow; no load-only planes.
55 pub fn new_unspecialized(num_planes: u32) -> Self {
56 Self {
57 counts: PlaneFlowCounts {
58 main_flow: num_planes,
59 load_only: 0,
60 },
61 partition_rule: PlaneFlowPartitionRule::MainFlowOnly,
62 }
63 }
64
65 /// Number of planes participating in main flow.
66 pub fn main_flow_count(&self) -> u32 {
67 self.counts.main_flow
68 }
69
70 /// Whether the configuration uses dedicated load-only planes.
71 pub fn has_specialization(&self) -> bool {
72 self.counts.load_only > 0
73 }
74}
75
76#[derive(Default, Copy, Clone, Debug, Hash, PartialEq, Eq)]
77/// Determines which types of planes are responsible for loading a tensor.
78pub enum InputLoadFlow {
79 /// Loaded exclusively by planes that participate in the main computation flow.
80 #[default]
81 MainOnly,
82 /// Loaded exclusively by planes dedicated to loading (load-only planes).
83 LoadOnly,
84}
85
86impl InputLoadFlow {
87 /// Whether there is specialization for the tensor.
88 pub fn has_specialization(&self) -> bool {
89 matches!(self, InputLoadFlow::LoadOnly)
90 }
91}
92
93// ============================================================================
94// Runtime cube types: PartitionThreshold, PlaneFlowPartition.
95// ============================================================================
96
97#[derive(CubeType, Copy, Clone, Debug, Hash, PartialEq, Eq)]
98/// Threshold of plane id at which the roles change.
99///
100/// Only exists because Cube enums cannot hold a comptime value directly.
101pub struct PartitionThreshold {
102 #[cube(comptime)]
103 threshold: u32,
104}
105
106#[derive(CubeType, Copy, Clone, Debug, Hash, PartialEq, Eq)]
107/// Runtime view of [`PlaneFlowPartitionRule`]: distinguishes a plane's role
108/// based on its plane id.
109pub enum PlaneFlowPartition {
110 /// All planes are in the main flow (no specialization).
111 MainFlowOnly,
112 /// Load-only planes: `[0, Threshold)`; main-flow planes: `[Threshold, total)`.
113 LoadOnlyFirst(PartitionThreshold),
114 /// Main-flow planes: `[0, Threshold)`; load-only planes: `[Threshold, total)`.
115 LoadOnlyLast(PartitionThreshold),
116}
117
118#[cube]
119impl PlaneFlowPartition {
120 /// Construct from comptime rule.
121 pub fn new(#[comptime] comptime_rule: PlaneFlowPartitionRule) -> PlaneFlowPartition {
122 match comptime_rule {
123 PlaneFlowPartitionRule::MainFlowOnly => PlaneFlowPartition::new_MainFlowOnly(),
124 PlaneFlowPartitionRule::LoadOnlyFirst { load_only } => {
125 PlaneFlowPartition::new_LoadOnlyFirst(PartitionThreshold {
126 threshold: load_only,
127 })
128 }
129 PlaneFlowPartitionRule::LoadOnlyLast { main_flow } => {
130 PlaneFlowPartition::new_LoadOnlyLast(PartitionThreshold {
131 threshold: main_flow,
132 })
133 }
134 }
135 }
136
137 /// The index of the current plane among planes that perform compute,
138 /// ignoring load-only planes.
139 pub fn compute_index(self) -> u32 {
140 match self {
141 PlaneFlowPartition::MainFlowOnly => UNIT_POS_Y,
142 PlaneFlowPartition::LoadOnlyFirst(load_only) => UNIT_POS_Y - load_only.threshold,
143 PlaneFlowPartition::LoadOnlyLast(_) => UNIT_POS_Y,
144 }
145 }
146
147 /// The index of the current plane among planes that perform loading,
148 /// ignoring any plane that does not participate for this `ident`.
149 pub fn load_index(self, #[comptime] specialization_tensor_config: InputLoadFlow) -> u32 {
150 match self {
151 PlaneFlowPartition::MainFlowOnly => UNIT_POS_Y,
152 PlaneFlowPartition::LoadOnlyFirst(load_only) => match specialization_tensor_config {
153 InputLoadFlow::MainOnly => UNIT_POS_Y - load_only.threshold,
154 InputLoadFlow::LoadOnly => UNIT_POS_Y,
155 },
156 PlaneFlowPartition::LoadOnlyLast(main_flow) => match specialization_tensor_config {
157 InputLoadFlow::LoadOnly => UNIT_POS_Y - main_flow.threshold,
158 InputLoadFlow::MainOnly => UNIT_POS_Y,
159 },
160 }
161 }
162
163 /// Whether this unit is the leader of the loading units. Always the lowest
164 /// unit in the correct group. Used by TMA; `plane_broadcast` / `plane_elect`
165 /// keep the value warp-uniform.
166 pub fn elect_load_leader(&self) -> bool {
167 let plane_id = plane_broadcast(UNIT_POS_Y, 0u32);
168
169 let is_elected_plane = match self {
170 PlaneFlowPartition::MainFlowOnly | PlaneFlowPartition::LoadOnlyFirst(_) => {
171 plane_id == 0
172 }
173 PlaneFlowPartition::LoadOnlyLast(main_flow) => plane_id == main_flow.threshold,
174 };
175
176 is_elected_plane && plane_elect()
177 }
178
179 /// Whether the current plane is a load-only plane.
180 pub fn is_load_plane(self) -> bool {
181 match self {
182 PlaneFlowPartition::MainFlowOnly => false,
183 PlaneFlowPartition::LoadOnlyFirst(load_only) => UNIT_POS_Y < load_only.threshold,
184 PlaneFlowPartition::LoadOnlyLast(main_flow) => UNIT_POS_Y >= main_flow.threshold,
185 }
186 }
187
188 /// Whether this plane is part of the compute planes. Used in specialized
189 /// kernels; `plane_broadcast` keeps the value warp-uniform.
190 pub fn is_compute_plane(self) -> bool {
191 let plane_id = plane_broadcast(UNIT_POS_Y, 0u32);
192
193 match self {
194 PlaneFlowPartition::MainFlowOnly => true,
195 PlaneFlowPartition::LoadOnlyFirst(load_only) => plane_id >= load_only.threshold,
196 PlaneFlowPartition::LoadOnlyLast(main_flow) => plane_id < main_flow.threshold,
197 }
198 }
199}
200
201// ============================================================================
202// Composition helper: combines PlaneFlowPartition::compute_index with a
203// Partitioner's `coordinates` to return the current primitive's (row, col)
204// in the partition grid.
205// ============================================================================
206
207#[cube]
208/// Returns the `(row, col)` of the current compute primitive within the stage,
209/// deriving `compute_index` from `role_rule_config` via [`PlaneFlowPartition`]
210/// and delegating the per-scope math to
211/// [`Partitioner::coordinates`](crate::tile::Partitioner::coordinates).
212pub fn partition_coordinates<P: Partitioner>(
213 #[comptime] role_rule_config: PlaneFlowPartitionRule,
214 #[comptime] plane_dim: u32,
215 #[comptime] num_partitions_col: u32,
216) -> Coords2d {
217 let compute_index = PlaneFlowPartition::new(role_rule_config).compute_index();
218 P::coordinates(compute_index, plane_dim, num_partitions_col)
219}