mcl_rs/
mcl.rs

1#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
2use crate::low_level;
3#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
4use crate::low_level::ArgOpt;
5use crate::low_level::TaskOpt;
6
7// use crate::device::DevInfo;
8use crate::prog::{PrgType, Prog};
9use crate::registered_buffer::RegisteredBuffer;
10#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
11use crate::registered_buffer::SharedMemBuffer;
12#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
13use crate::task::SharedTask;
14use crate::task::{Task, TaskArg};
15use crate::transfer::Transfer;
16
17use bitflags::bitflags;
18
19bitflags! {
20    #[derive(Default)]
21    pub(crate) struct MclEnvBuilderFlags: u64 {
22        const MCL_SET_BIND_WORKERS = 0x01 as u64;
23    }
24}
25
26/// This structure is used to setup the MCL environment with the given parameters
27/// # Examples
28///```
29/// use mcl_rs::MclEnvBuilder;
30///
31/// let env = MclEnvBuilder::new()
32///                 .num_workers(10)
33///                 .bind_workers()
34///                 .initialize();
35///```
36pub struct MclEnvBuilder {
37    num_workers: usize,
38    flags: MclEnvBuilderFlags,
39}
40
41impl MclEnvBuilder {
42    /// Creates and returns a new MclEnvBuilder with the default values
43    ///
44    /// # Examples
45    ///```
46    /// use mcl_rs::MclEnvBuilder;
47    ///
48    /// let env = MclEnvBuilder::new()
49    ///                 .initialize();
50    ///```
51    pub fn new() -> MclEnvBuilder {
52        MclEnvBuilder {
53            num_workers: 1,
54            flags: Default::default(),
55        }
56    }
57
58    /// Set the num_workers to pass to the mcl initialization function
59    ///
60    ///
61    /// # Examples
62    ///```
63    /// use mcl_rs::MclEnvBuilder;
64    ///
65    /// let env = MclEnvBuilder::new()
66    ///                 .num_workers(1);
67    ///```
68    pub fn num_workers(mut self, workers: usize) -> MclEnvBuilder {
69        assert!(workers > 0);
70
71        self.num_workers = workers;
72
73        self
74    }
75
76    /// Bind worker threads to their own core
77    ///
78    /// # Examples
79    ///```
80    /// use mcl_rs::MclEnvBuilder;
81    ///
82    /// let env = MclEnvBuilder::new()
83    ///                 .bind_workers();
84    ///```
85    pub fn bind_workers(mut self) -> MclEnvBuilder {
86        assert!(self.num_workers > 0);
87
88        self.flags |= MclEnvBuilderFlags::MCL_SET_BIND_WORKERS;
89
90        self
91    }
92
93    /// Initializes mcl
94    ///
95    /// Returns an [Mcl] instance
96    ///  
97    /// # Examples
98    ///```
99    /// use mcl_rs::MclEnvBuilder;
100    ///
101    /// let env = MclEnvBuilder::new()
102    ///                 .initialize();
103    ///```
104    pub fn initialize(self) -> Mcl {
105        low_level::init(self.num_workers as u64, self.flags.bits());
106
107        Mcl { _env: MclEnv }
108    }
109}
110
111/// Represents an initialize MCL environment. When this struct goes out of scope the MCL environment is finalized.
112/// Thus, there is no need to explicitly call the equivalent of the (c-api) mcl_finit()
113pub(crate) struct MclEnv;
114
115impl Drop for MclEnv {
116    /// Finalizes mcl when MclEnv goes out of scope
117    fn drop(&mut self) {
118        low_level::finit();
119    }
120}
121
122/// Represents an initialize MCL environment. When this struct goes out of scope the MCL environment is finalized.
123/// Thus, there is no need to explicitly call the equivalent of (c-api) mcl_finit()
124pub struct Mcl {
125    pub(crate) _env: MclEnv,
126}
127
128impl Mcl {
129    /// Creates a new mcl prog from the given source file pointed to by `prog_path` and the specified Program Type [crate::prog::PrgType].
130    ///
131    /// Returns a new [Prog][crate::prog::Prog] that can be loaded into the current mcl environment (that is [load()][crate::prog::Prog::load] will need to be called on it at a later time).
132    ///
133    /// # Examples
134    ///```no_run
135    /// use mcl_rs::{MclEnvBuilder,PrgType};
136    ///
137    /// let mcl = MclEnvBuilder::new().num_workers(10).initialize();
138    /// mcl.create_prog("my_path",PrgType::Src);
139    ///```
140    pub fn create_prog(&self, prog_path: &str, prog_type: PrgType) -> Prog {
141        Prog::from(prog_path, prog_type)
142    }
143
144    /// Creates and loads a new mcl prog from the given source file pointed to by `prog_path` and the specified Program Type [crate::prog::PrgType].
145    ///
146    /// This is a convenience function for when no additional arguments need to be supplied with program/kernel during compile time
147    ///
148    /// # Examples
149    ///```no_run
150    /// use mcl_rs::{MclEnvBuilder,PrgType};
151    ///
152    /// let mcl = MclEnvBuilder::new().num_workers(10).initialize();
153    /// mcl.load_prog("my_path",PrgType::Src);
154    ///```
155    pub fn load_prog(&self, prog_path: &str, prog_type: PrgType) {
156        Prog::from(prog_path, prog_type).load()
157    }
158
159    /// Creates a new mcl task from the given kernel.
160    ///
161    /// This kernel must exist in a previously loaded [Prog][crate::prog::Prog].
162    ///
163    /// Returns a new Task representing this kernel
164    /// # Examples
165    ///
166    ///```no_run
167    ///     let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
168    ///     mcl.load_prog("my_path", mcl_rs::PrgType::Src);
169    ///
170    ///     let t = mcl.task("my_kernel", 2);
171    ///```
172    pub fn task<'a>(&self, kernel_name_cl: &str, nargs: usize) -> Task<'a> {
173        Task::new(kernel_name_cl, nargs, TaskOpt::EMPTY)
174    }
175
176    /// Creates a new mcl shared task from the given kernel.
177    ///
178    /// This kernel must exist in a previously loaded [Prog][crate::prog::Prog].
179    ///
180    /// Other processes will be able to attach to this task by its shared task ID
181    /// i.e. [crate::task::Task::shared_id]
182    ///
183    /// Returns a new Task representing this kernel
184    /// # Examples
185    ///
186    ///```no_run
187    ///     let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
188    ///     mcl.load_prog("my_path", mcl_rs::PrgType::Src);
189    ///
190    ///     let t = mcl.shared_task("my_kernel", 2);
191    ///```
192    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
193    pub fn shared_task<'a>(&self, kernel_name_cl: &str, nargs: usize) -> Task<'a> {
194        Task::new(kernel_name_cl, nargs, TaskOpt::SHARED)
195    }
196
197    /// Creates a new mcl shared task from the given process id and unique task id.
198    ///
199    /// This task must have been created by process `pid`
200    /// # Examples
201    ///
202    ///```no_run
203    ///     let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
204    ///     mcl.load_prog("my_path", mcl_rs::PrgType::Src);
205    ///     let pid = 0; //user is required to set this approriately
206    ///     let task_id = 0; //user is required to set this approriately
207    ///
208    ///     let t = mcl.attach_shared_task(pid,task_id);
209    ///```
210    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
211    pub fn attach_shared_task(&self, pid: i32, task_id: u32) -> SharedTask {
212        SharedTask::new(pid, task_id)
213    }
214
215    /// Creates a new mcl transfer task with that will transfer `nargs` and suggest to the scheduler that `ncopies` should be created
216    /// # Examples
217    ///```no_run     
218    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
219    ///     mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
220    ///
221    ///     let tr = mcl.transfer(1, 1);
222    ///```
223    pub fn transfer<'a>(&self, nargs: usize, ncopies: usize) -> Transfer<'a> {
224        Transfer::new(nargs, ncopies, 0)
225    }
226
227    /// Register the provided `arg` as a buffer for future use with MCL resident memory
228    ///
229    /// # Panics
230    ///
231    /// This call will panic if the provided TaskArg was not created from one of the slice variants:
232    /// [input_slice][crate::task::TaskArg::input_slice]
233    /// [output_slice][crate::task::TaskArg::output_slice]
234    /// [inout_slice][crate::task::TaskArg::inout_slice]
235    ///
236    /// Further this call will also panic if the argument [residient][crate::task::TaskArg::resident()] property was not set to true.
237    ///
238    /// # Examples
239    ///```no_run     
240    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
241    ///     mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
242    ///
243    ///     let mut a = vec![1;100];
244    ///     let buffer = mcl.register_buffer(mcl_rs::TaskArg::inout_slice(& mut a).resident(true));
245    ///```
246    pub fn register_buffer<'a>(&self, arg: TaskArg<'a>) -> RegisteredBuffer<'a> {
247        RegisteredBuffer::new(arg)
248    }
249
250    /// Creates a buffer that can be accessed via shared memory from multiple processes.
251    /// If using only the "shared_mem" feature this buffer will be shared only in host memory.
252    /// If the "pocl_extensions" feature is enabled, and the patched version of POCL 1.8 has been succesfully
253    /// installed (please see <https://github.com/pnnl/mcl/tree/dev#using-custom-pocl-extensions> for more information)
254    ///
255    /// # Panics
256    ///
257    /// This call will panic if the provided TaskArg was not created from one of the shared variants:
258    /// [input_shared][crate::task::TaskArg::input_shared]
259    /// [output_shared][crate::task::TaskArg::output_shared]
260    /// [inout_shared][crate::task::TaskArg::inout_shared]
261    ///
262    ///
263    /// # Examples
264    ///```no_run     
265    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
266    ///     mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
267    ///     let num_elems = 100;
268    ///     let buffer = mcl.create_shared_buffer(mcl_rs::TaskArg::inout_shared::<f32>("my_buffer",num_elems).resident(true));
269    ///```
270    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
271    pub fn create_shared_buffer(&self, mut arg: TaskArg<'_>) -> SharedMemBuffer {
272        arg.flags |= ArgOpt::SHARED_MEM_NEW
273            | ArgOpt::SHARED_MEM_DEL_OLD
274            | ArgOpt::DYNAMIC
275            | ArgOpt::RESIDENT;
276        // println!("{:?}",arg.flags);
277        SharedMemBuffer::new(arg)
278    }
279
280    /// Attaches to a shared buffer that was previously created by another process
281    /// If using only the "shared_mem" feature this buffer will be shared only in host memory.
282    /// If the "pocl_extensions" feature is enabled, and the patched version of POCL 1.8 has been succesfully
283    /// installed (please see <https://github.com/pnnl/mcl/tree/dev#using-custom-pocl-extensions> for more information)
284    ///
285    /// The provided `TaskArg` should have been constructed with identical arguments to the original buffer
286    ///
287    /// # Panics
288    ///
289    /// This call will panic if the provided TaskArg was not created from one of the shared variants:
290    /// [input_shared][crate::task::TaskArg::input_shared]
291    /// [output_shared][crate::task::TaskArg::output_shared]
292    /// [inout_shared][crate::task::TaskArg::inout_shared]
293    ///
294    ///
295    /// # Examples
296    ///```no_run     
297    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
298    ///     mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
299    ///     let num_elems = 100;
300    ///
301    ///     let buffer = mcl.attach_shared_buffer(mcl_rs::TaskArg::inout_shared::<f32>("my_buffer",num_elems).resident(true));
302    ///```
303    #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
304    pub fn attach_shared_buffer(&self, mut arg: TaskArg<'_>) -> SharedMemBuffer {
305        arg.flags |= ArgOpt::DYNAMIC | ArgOpt::RESIDENT;
306        SharedMemBuffer::new(arg)
307    }
308
309    /// Returns the info of the device specified by `id`
310    ///
311    ///```no_run
312    ///     let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
313    ///
314    ///     let t = mcl.task("my_kernel", 2);
315    ///```
316    pub fn get_dev(&self, id: u32) -> crate::DevInfo {
317        low_level::get_dev(id)
318    }
319
320    /// Return the number of devices in the system
321    ///
322    ///```no_run
323    ///     let mcl = mcl_rs::MclEnvBuilder::new().initialize();
324    ///     let num_dev = mcl.get_ndev();
325    ///```
326    pub fn get_ndev(&self) -> u32 {
327        low_level::get_ndev()
328    }
329}