Skip to main content

cubek_matmul/components/global/read/reader/
full_reader.rs

1use std::marker::PhantomData;
2
3use crate::{
4    components::global::multi_stage::JobExecutor,
5    components::global::multi_stage::LoadMaxRoundPlaneCount,
6    components::global::read::LoadingJob,
7    components::global::read::LoadingValidation,
8    components::global::read::StageBuffer,
9    components::global::read::SyncStrategy,
10    components::global::read::TaskCounter,
11    components::global::{multi_stage::JobIterator, read::FullLoaderStage},
12    components::{global::memory::GlobalIterator, stage::LoadStageFamily},
13    {args::RuntimeConfig, components::global::GlobalReaderConfig},
14};
15use cubecl::{
16    prelude::*,
17    std::tensor::{View, layout::Coords2d},
18};
19use cubek_std::tile::TilingLayout;
20
21pub type SyncBarrier<S> = <S as SyncStrategy>::Barrier;
22
23#[cube]
24/// A strategy for synchronously loading a full stage memory.
25pub trait FullLoadingStrategy<RC: RuntimeConfig>:
26    'static + Send + Sync + Clone + LoadingValidation + LoadMaxRoundPlaneCount
27{
28    /// The layout describing how data is tiled across the stage.
29    type TilingLayout: TilingLayout;
30    /// The synchronization strategy that should be used with this loading strategy
31    type SyncStrategy: SyncStrategy;
32    type Stage: LoadStageFamily;
33
34    /// The [LoadingJob] for this strategy.
35    type Job<EG: Numeric, NG: Size, ES: Numeric, NS: Size>: LoadingJob<EG, NG, ES, NS, Self::TilingLayout, Self::SyncStrategy, Stage = Self::Stage>;
36
37    const SHOULD_CLEAR: bool = false;
38
39    /// Returns the job with preliminary calculations done.
40    fn new_job<EG: Numeric, NG: Size, ES: Numeric, NS: Size>(
41        config: RC,
42        #[comptime] config: GlobalReaderConfig,
43    ) -> Self::Job<EG, NG, ES, NS>;
44}
45
46#[derive(Clone, CubeType)]
47#[expand(derive(Clone))]
48/// Loads the entire stage memory.
49///
50/// A complete load is referred to as a `Job`, which is divided into `Tasks`—
51/// each Task represents a single data transfer for a specific unit
52pub struct FullStageGlobalReader<
53    'a,
54    EG: Numeric,
55    NG: Size,
56    ES: Numeric,
57    NS: Size,
58    RC: RuntimeConfig,
59    L: FullLoadingStrategy<RC>,
60> {
61    global_iter: GlobalIterator<'a, Vector<EG, NG>>,
62    runtime_config: RC,
63    stage: FullLoaderStage<RC, L, ES, NS>,
64    loading_job: ComptimeOption<L::Job<EG, NG, ES, NS>>,
65    #[cube(comptime)]
66    _phantom: PhantomData<L>,
67}
68
69#[cube]
70impl<
71    'a,
72    EG: Numeric,
73    NG: Size,
74    ES: Numeric,
75    NS: Size,
76    RC: RuntimeConfig,
77    L: FullLoadingStrategy<RC>,
78> FullStageGlobalReader<'a, EG, NG, ES, NS, RC, L>
79{
80    /// Create a new SyncFullStageGlobalReader
81    pub fn new(
82        view: View<'a, Vector<EG, NG>, Coords2d>,
83        runtime_config: RC,
84        k_step: u32,
85        #[comptime] config: GlobalReaderConfig,
86    ) -> Self {
87        // Maybe make align a property on the strategy, but it's fine to over-align so this works
88        // for now. Swizzling will require more though.
89        let stage = L::Stage::create(128usize, config.smem_config);
90
91        let global_iter =
92            GlobalIterator::new(view, k_step, config.gmem_config.view_direction, false);
93
94        let loading_job = match config.precompute_job {
95            true => ComptimeOption::new_Some(L::new_job::<EG, NG, ES, NS>(
96                runtime_config.clone(),
97                config,
98            )),
99            false => ComptimeOption::new_None(),
100        };
101
102        FullStageGlobalReader::<'a, EG, NG, ES, NS, RC, L> {
103            global_iter,
104            runtime_config,
105            stage,
106            loading_job,
107            _phantom: PhantomData::<L>,
108        }
109    }
110
111    /// Give a reader to the loaded stage memory.
112    pub fn stage(&self) -> FullLoaderStage<RC, L, ES, NS> {
113        L::Stage::with_buffer_index(&self.stage, 0)
114    }
115
116    /// Frees the stage memory for reuse
117    pub fn free_stage(self) {
118        L::Stage::free(&self.stage);
119    }
120
121    /// Advance the view over global memory along the k dimension by a specified offset, `k_offset`.
122    pub fn advance_view(&mut self) {
123        self.global_iter.advance();
124    }
125
126    /// Accomplish the entire job of loading data into the stage memory
127    pub fn load_stage(
128        &mut self,
129        barrier: &SyncBarrier<L::SyncStrategy>,
130        #[comptime] config: GlobalReaderConfig,
131    ) {
132        let mut loading_job = self
133            .loading_job
134            .clone()
135            .unwrap_or_else(|| L::new_job::<EG, NG, ES, NS>(self.runtime_config.clone(), config));
136
137        let len = L::Job::task_count(&loading_job);
138
139        #[unroll]
140        for task_id in 0..len {
141            L::Job::<EG, NG, ES, NS>::execute_task(
142                &mut loading_job,
143                task_id,
144                &self.global_iter,
145                &mut self.stage,
146                barrier,
147                config,
148            );
149        }
150    }
151}
152
153#[cube]
154impl<EG: Numeric, NG: Size, ES: Numeric, NS: Size, RC: RuntimeConfig, L: FullLoadingStrategy<RC>>
155    JobExecutor<L::SyncStrategy> for FullStageGlobalReader<'_, EG, NG, ES, NS, RC, L>
156{
157    type JobIterator = FullStageJobIterator<EG, NG, ES, NS, RC, L>;
158
159    fn create_job_iterator(
160        this: &Self,
161        #[comptime] _stage_buffer: StageBuffer,
162        #[comptime] config: GlobalReaderConfig,
163    ) -> Self::JobIterator {
164        let job = this
165            .loading_job
166            .clone()
167            .unwrap_or_else(|| L::new_job::<EG, NG, ES, NS>(this.runtime_config.clone(), config));
168
169        let num_tasks = L::Job::task_count(&job);
170
171        FullStageJobIterator::<EG, NG, ES, NS, RC, L> {
172            job,
173            num_tasks,
174            current: ComptimeCell::new(TaskCounter { counter: 0u32 }),
175        }
176    }
177
178    fn execute_task(
179        this: &mut Self,
180        job_iterator: &mut FullStageJobIterator<EG, NG, ES, NS, RC, L>,
181        barrier: &SyncBarrier<L::SyncStrategy>,
182        #[comptime] config: GlobalReaderConfig,
183    ) {
184        let task_id = job_iterator.current.read().counter.comptime();
185
186        L::Job::<EG, NG, ES, NS>::execute_task(
187            &mut job_iterator.job,
188            task_id,
189            &this.global_iter,
190            &mut this.stage,
191            barrier,
192            config,
193        );
194
195        job_iterator.current.store(TaskCounter {
196            counter: task_id + 1,
197        });
198    }
199
200    fn execute_all_remaining_tasks(
201        this: &mut Self,
202        job_iterator: &mut Self::JobIterator,
203        barrier: &SyncBarrier<L::SyncStrategy>,
204        #[comptime] config: GlobalReaderConfig,
205    ) {
206        let task_counter = job_iterator.current.read().counter;
207
208        #[unroll]
209        for task_id in task_counter..job_iterator.num_tasks {
210            L::Job::<EG, NG, ES, NS>::execute_task(
211                &mut job_iterator.job,
212                task_id,
213                &this.global_iter,
214                &mut this.stage,
215                barrier,
216                config,
217            );
218        }
219
220        job_iterator.current.store(TaskCounter {
221            counter: job_iterator.num_tasks,
222        });
223    }
224
225    fn execute_whole_job(
226        this: &mut Self,
227        barrier: &SyncBarrier<L::SyncStrategy>,
228        #[comptime] stage_buffer: StageBuffer,
229        #[comptime] config: GlobalReaderConfig,
230    ) {
231        let mut iter = Self::create_job_iterator(&*this, stage_buffer, config);
232        Self::execute_all_remaining_tasks(this, &mut iter, barrier, config);
233    }
234}
235
236#[derive(CubeType)]
237/// A comptime iterator over a job for sync full stage reader
238pub struct FullStageJobIterator<
239    EG: Numeric,
240    NG: Size,
241    ES: Numeric,
242    NS: Size,
243    RC: RuntimeConfig,
244    L: FullLoadingStrategy<RC>,
245> {
246    job: L::Job<EG, NG, ES, NS>,
247    #[cube(comptime)]
248    pub num_tasks: u32,
249    pub current: ComptimeCell<TaskCounter>,
250}
251
252#[cube]
253impl<EG: Numeric, NG: Size, ES: Numeric, NS: Size, RC: RuntimeConfig, L: FullLoadingStrategy<RC>>
254    JobIterator for FullStageJobIterator<EG, NG, ES, NS, RC, L>
255{
256    fn current(this: &Self) -> comptime_type!(u32) {
257        this.current.read().counter
258    }
259
260    fn num_tasks(this: &Self) -> comptime_type!(u32) {
261        this.num_tasks
262    }
263}