Skip to main content

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

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