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}