Skip to main content

rublas/kernel_ir/components/global/read/reader/
full_reader.rs

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