mcl_rs/task.rs
1#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
2use crate::device::DevType;
3use crate::low_level;
4use crate::low_level::{ArgOpt, ReqStatus, TaskOpt};
5use crate::registered_buffer::RegisteredBuffer;
6#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
7use crate::registered_buffer::SharedMemBuffer;
8use libmcl_sys::*;
9#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
10use std::os::unix::raw::pid_t;
11
12unsafe impl Send for Task<'_> {}
13unsafe impl Sync for Task<'_> {}
14
15/// Represents an MCL task whose kernel has been set but the arguments and target device are missing
16pub struct Task<'a> {
17 // we want to stare the actual reference to the original argument so we can track lifetimes appropriately
18 // this also will allow us to protect agains the same argument being used as an output simulataneous
19 // aka this prevents us from doing dirty C-memory things :)
20 args: Vec<TaskArgOrBuf<'a>>,
21 curr_arg: usize,
22 les: Option<[u64; 3]>,
23 dev: DevType,
24 c_handle: *mut mcl_handle,
25 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
26 shared_id: Option<u32>,
27}
28
29impl<'a> Task<'a> {
30 pub(crate) fn new(kernel_name_cl: &str, nargs: usize, flags: TaskOpt) -> Self {
31 let c_handle = low_level::task_create(flags);
32 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
33 let mut id = None;
34 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
35 if flags.contains(TaskOpt::SHARED) {
36 id = low_level::get_shared_task_id(c_handle)
37 }
38 let task = Task {
39 args: vec![Default::default(); nargs],
40 curr_arg: 0,
41 les: None,
42 dev: DevType::ANY,
43 c_handle: c_handle,
44 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
45 shared_id: id,
46 };
47 low_level::task_set_kernel(task.c_handle, kernel_name_cl, nargs as u64);
48 task
49 }
50
51 /// Set a new argument `arg` for `this` mcl task.
52 /// Note the order in which tasks are set must match the order expected by the kernel
53 ///
54 /// Returns the Task with the arg set
55 ///
56 /// # Examples
57 ///```no_run
58 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
59 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
60 /// .with_compile_args("-D MYDEF").load();
61 ///
62 /// let data = vec![0; 4];
63 /// let mut out = vec![0; 4];
64 /// let pes: [u64; 3] = [1, 1, 1];
65 ///
66 /// let mcl_future = mcl.task("my_kernel", 1)
67 /// .arg(mcl_rs::TaskArg::input_slice(&data)) //the first argument to the kernel is the input array
68 /// .arg(mcl_rs::TaskArg::output_slice(&mut out))//the second argument to the kernel is the output array
69 /// .exec(pes);
70 /// futures::executor::block_on(mcl_future);
71 ///
72 ///```
73 pub fn arg(mut self, arg: TaskArg<'a>) -> Self {
74 match &arg.data {
75 TaskArgData::Scalar(x) => {
76 low_level::task_set_arg(self.c_handle, self.curr_arg as u64, x, arg.flags)
77 }
78 TaskArgData::Buffer(x) => {
79 low_level::task_set_arg(self.c_handle, self.curr_arg as u64, x, arg.flags)
80 }
81 // TaskArgData::Local(x) => {
82 // low_level::task_set_local(self.c_handle, self.curr_arg as u64, *x, arg.flags)
83 // }
84 #[cfg(feature = "shared_mem")]
85 TaskArgData::Shared(..) => panic!("must use arg_shared_buffer api "),
86 TaskArgData::Empty => panic!("cannot have an empty arg"),
87 }
88 self.args[self.curr_arg] = TaskArgOrBuf::TaskArg(arg);
89 self.curr_arg += 1;
90
91 self
92 }
93
94 /// Set a new argument registered buffer `arg` for `this` mcl task in preparation
95 ///
96 /// Returns the Task with the arg set
97 ///
98 /// # Examples
99 ///```no_run
100 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
101 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
102 /// .with_compile_args("-D MYDEF").load();
103 ///
104 /// let mut a = vec![0;100];
105 /// let buf = mcl.register_buffer(mcl_rs::TaskArg::inout_slice(&mut a)
106 /// .resident(true)
107 /// .dynamic(true),
108 /// );
109 /// let pes: [u64; 3] = [1, 1, 1];
110 ///
111 /// let mcl_future = mcl.task("my_kernel", 1)
112 /// .arg_buffer(buf)
113 /// .exec(pes);
114 /// futures::executor::block_on(mcl_future);
115 ///
116 ///```
117 pub fn arg_buffer(mut self, buffer: RegisteredBuffer<'a>) -> Self {
118 low_level::task_set_arg_registered_buffer(self.c_handle, self.curr_arg as u64, &buffer);
119 self.args[self.curr_arg] = TaskArgOrBuf::RegBuf(buffer.clone());
120 self.curr_arg += 1;
121
122 self
123 }
124
125 /// Set a new shared argument buffer `arg` for `this` mcl task in preparation
126 ///
127 /// Returns the Task with the arg set
128 ///
129 /// # Saftey
130 ///
131 /// We track the reader and writers associated with this buffer to protect access, but that only applies to this process
132 ///
133 /// Regardless of and local saftey guarentees we provide, using a shared buffer is still inherently unsafe as this represents a multi-processed shared memory buffer, managed by the MCL c-library, no guarantees can be provided on
134 /// multiple processes modifying this buffer simultaneously
135 ///
136 /// # Examples
137 ///```no_run
138 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
139 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
140 /// .with_compile_args("-D MYDEF").load();
141 ///
142 /// let num_elems = 100;
143 /// let buf = mcl.create_shared_buffer(mcl_rs::TaskArg::inout_shared::<u32>("my_buffer", num_elems));
144 /// let pes: [u64; 3] = [1, 1, 1];
145 ///
146 /// let mcl_future = unsafe {
147 /// mcl.task("my_kernel", 1)
148 /// .arg_shared(buf) //potentially accessed by other proccesses hence unsafe
149 /// .exec(pes)
150 /// };
151 /// futures::executor::block_on(mcl_future);
152 ///
153 ///```
154 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
155 pub unsafe fn arg_shared_buffer(mut self, buffer: SharedMemBuffer) -> Self {
156 //TODO fix the offset issue
157 low_level::task_set_arg_shared_mem_buffer(self.c_handle, self.curr_arg as u64, &buffer);
158 self.args[self.curr_arg] = TaskArgOrBuf::ShmBuf(buffer.clone());
159 self.curr_arg += 1;
160
161 self
162 }
163
164 /// Set the local workgroup size `les` to the mcl task in preparation
165 ///
166 ///
167 /// Returns the Task with the local workgroup size set
168 ///
169 /// # Examples
170 ///
171 ///```no_run
172 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
173 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
174 /// .with_compile_args("-D MYDEF").load();
175 /// let data = vec![0; 4];
176 /// let les: [u64; 3] = [1, 1, 1];
177 /// let pes: [u64; 3] = [1, 1, 1];
178 /// let mcl_future = mcl.task("my_kernel", 1)
179 /// .arg(mcl_rs::TaskArg::input_slice(&data))
180 /// .lwsize(les)
181 /// .exec(pes);
182 /// futures::executor::block_on(mcl_future);
183 ///```
184 pub fn lwsize(mut self, les: [u64; 3]) -> Self {
185 self.les = Some(les);
186
187 return self;
188 }
189
190 /// Set the preferred device `dev` to the mcl task in preparation
191 ///
192 /// Returns the Task with the desired device set
193 ///
194 /// # Examples
195 ///
196 ///```no_run
197 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
198 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
199 /// .with_compile_args("-D MYDEF").load();
200 /// let data = vec![0; 4];
201 /// let les: [u64; 3] = [1, 1, 1];
202 /// let pes: [u64; 3] = [1, 1, 1];
203 /// let mcl_future = mcl.task("my_kernel", 1)
204 /// .arg(mcl_rs::TaskArg::input_slice(&data))
205 /// .lwsize(les)
206 /// .dev(mcl_rs::DevType::CPU)
207 /// .exec(pes);
208 /// futures::executor::block_on(mcl_future);
209 ///```
210 pub fn dev(mut self, dev: DevType) -> Self {
211 self.dev = dev;
212
213 return self;
214 }
215
216 /// If this is a shared task return the task id otherwise return None
217 ///
218 /// # Examples
219 ///```no_run
220 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
221 /// mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
222 /// let data = vec![0; 4];
223 /// let pes: [u64; 3] = [1, 1, 1];
224 ///
225 /// let task = mcl.task("my_kernel", 1);
226 /// assert_eq!(task.shared_id(), None);
227 ///
228 /// let shared_task = mcl.shared_task("my_kernel2", 1);
229 /// assert!(shared_task.shared_id().is_some());
230 ///```
231 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
232 pub fn shared_id(&self) -> Option<u32> {
233 self.shared_id
234 }
235
236 /// Submit the task for execution
237 /// This is an asynchronous operation, meaning that no work is actually performed until
238 /// the returned future is actualy `await`ed.
239 /// While awaiting a task execution, the user application will not make forward progress until
240 /// the underylying device has executed the task and performed any necessary data transfers.
241 /// Upon return from the await call, any output data is gauranteed to be written to its approriate buffer.
242 ///
243 /// Task execution order can be enforced by sequentially awaiting tasks, or may be executed simultaneously
244 /// using data structures such as Join_all <https://docs.rs/futures/latest/futures/future/fn.join_all.html>
245 /// # Examples
246 ///
247 ///```no_run
248 /// let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
249 /// mcl.load_prog("my_path", mcl_rs::PrgType::Src);
250 /// let data = vec![0; 4];
251 /// let pes: [u64; 3] = [1, 1, 1];
252 /// let task_1 = mcl.task("my_kernel", 1)
253 /// .arg(mcl_rs::TaskArg::input_slice(&data))
254 /// .dev(mcl_rs::DevType::CPU)
255 /// .exec(pes); //this creates a future we need to await
256 /// let task_2 = mcl.task("my_kernel", 1)
257 /// .arg(mcl_rs::TaskArg::input_slice(&data))
258 /// .dev(mcl_rs::DevType::CPU)
259 /// .exec(pes); //this creates a future we need to await
260 /// let sequential_tasks = async move{
261 /// task_1.await; //task will execute before task 2 is even submitted
262 /// task_2.await;
263 /// };
264 /// futures::executor::block_on(sequential_tasks);
265 ///
266 /// let task_3 = mcl.task("my_kernel", 1)
267 /// .arg(mcl_rs::TaskArg::input_slice(&data))
268 /// .dev(mcl_rs::DevType::CPU)
269 /// .exec(pes); //this creates a future we need to await
270 /// let task_4 = mcl.task("my_kernel", 1)
271 /// .arg(mcl_rs::TaskArg::input_slice(&data))
272 /// .dev(mcl_rs::DevType::CPU)
273 /// .exec(pes); //this creates a future we need to await
274 /// let simultaneous_tasks = futures::future::join_all([task_3,task_4]);
275 /// futures::executor::block_on(simultaneous_tasks); //both tasks submitted "simultaneously"
276 ///```
277 pub async fn exec(mut self, ref mut pes: [u64; 3]) {
278 assert_eq!(self.curr_arg, self.args.len());
279 for arg in &self.args {
280 match arg {
281 TaskArgOrBuf::RegBuf(buf) => buf.alloc().await,
282 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
283 TaskArgOrBuf::ShmBuf(buf) => buf.alloc().await,
284 TaskArgOrBuf::TaskArg(_) => {}
285 }
286 }
287
288 low_level::exec(self.c_handle, pes, &mut self.les, self.dev);
289
290 while low_level::test(self.c_handle) != ReqStatus::Completed {
291 async_std::task::yield_now().await;
292 }
293 }
294}
295
296impl Drop for Task<'_> {
297 fn drop(&mut self) {
298 low_level::task_free(self.c_handle);
299 }
300}
301
302#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
303pub struct SharedTask {
304 pid: pid_t,
305 hdl_id: u32,
306}
307
308/// Represents a shared task handle, that can be use to await the completion of a task on a different process from the one which created it
309#[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
310impl SharedTask {
311 pub(crate) fn new(pid: pid_t, hdl_id: u32) -> SharedTask {
312 SharedTask { pid, hdl_id }
313 }
314
315 /// Await the completetion of the shared task.
316 /// This is an asynchronous operation, meaning that no work is actually performed until
317 /// the returned future is actualy `await`ed.
318 /// While awaiting a task execution, the user application will not make forward progress until
319 /// the underylying device has executed the task and performed any necessary data transfers.
320 /// Upon return from the await call, any output data is gauranteed to be written to its approriate buffer.
321 ///
322 /// Task execution order can be enforced be sequentially awaiting tasks, or may be executed simultaneous
323 /// using data structures such as Join_all <https://docs.rs/futures/latest/futures/future/fn.join_all.html>
324 /// # Examples
325 ///
326 ///```no_run
327 /// let mcl = mcl_rs::MclEnvBuilder::new().num_workers(10).initialize();
328 /// mcl.load_prog("my_path", mcl_rs::PrgType::Src);
329 /// let pid = 0; //user is required to set this approriately
330 /// let task_ids = [0,1,2,3]; //user is required to set this approriately
331 ///
332 /// let t1 = mcl.attach_shared_task(pid,task_ids[0]);
333 /// let t2 = mcl.attach_shared_task(pid,task_ids[1]);
334 /// let t3 = mcl.attach_shared_task(pid,task_ids[2]);
335 /// let t4 = mcl.attach_shared_task(pid,task_ids[3]);
336 ///
337 /// let tasks = async move {
338 /// t1.await;
339 /// t2.await;
340 /// futures::future::join_all([t3,t4]).await;
341 /// }
342 /// futures::executor::block_on(tasks);
343 ///```
344 pub async fn wait(&self) {
345 while low_level::shared_task_test(self.pid, self.hdl_id) != ReqStatus::Completed {
346 async_std::task::yield_now().await;
347 }
348 }
349}
350
351#[derive(Clone)]
352enum TaskArgOrBuf<'a> {
353 TaskArg(TaskArg<'a>),
354 RegBuf(RegisteredBuffer<'a>),
355 #[cfg(any(feature = "shared_mem", feature = "pocl_extensions"))]
356 ShmBuf(SharedMemBuffer),
357}
358
359impl<'a> Default for TaskArgOrBuf<'a> {
360 fn default() -> Self {
361 TaskArgOrBuf::TaskArg(Default::default())
362 }
363}
364
365#[derive(Clone)]
366pub(crate) enum TaskArgData<'a> {
367 Scalar(&'a [u8]),
368 Buffer(&'a [u8]),
369 // Local(usize),
370 #[cfg(feature = "shared_mem")]
371 Shared(String, usize),
372 Empty,
373}
374
375impl<'a> TaskArgData<'a> {
376 pub(crate) fn len(&self) -> usize {
377 match self {
378 TaskArgData::Scalar(x) => x.len(),
379 TaskArgData::Buffer(x) => x.len(),
380 // TaskArgData::Local(x) => *x,
381 #[cfg(feature = "shared_mem")]
382 TaskArgData::Shared(_, x) => *x,
383 TaskArgData::Empty => 0,
384 }
385 }
386}
387
388/// Represents a data argument for an MCL task along with the use flags (e.g. input, output, access type etc.)
389#[derive(Clone)]
390pub struct TaskArg<'a> {
391 pub(crate) data: TaskArgData<'a>,
392 pub(crate) flags: ArgOpt,
393 pub(crate) orig_type_size: usize,
394}
395
396impl<'a> Default for TaskArg<'a> {
397 fn default() -> Self {
398 TaskArg {
399 data: TaskArgData::Empty,
400 flags: ArgOpt::EMPTY,
401 orig_type_size: 0,
402 }
403 }
404}
405
406fn to_u8_slice<T>(data: &[T]) -> &[u8] {
407 let num_bytes = std::mem::size_of::<T>() * data.len();
408 unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, num_bytes) }
409 //no alignment issues going from T to u8 as u8 aligns to everything
410}
411
412impl<'a> TaskArg<'a> {
413 /// Create a new task input argument from `slice`
414 ///
415 ///
416 /// # Examples
417 ///
418 ///```no_run
419 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
420 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
421 /// .with_compile_args("-D MYDEF").load();
422 /// let data = vec![0; 4];
423 /// let task = mcl.task("my_kernel", 1)
424 /// .arg(mcl_rs::TaskArg::input_slice(&data));
425 ///```
426 // pub fn input_slice<T: Into<MclBufferDataType<'a>>>(slice: T) -> Self {
427 pub fn input_slice<T>(slice: &'a [T]) -> Self {
428 TaskArg {
429 data: TaskArgData::Buffer(to_u8_slice(slice)),
430 flags: ArgOpt::INPUT | ArgOpt::BUFFER,
431 orig_type_size: std::mem::size_of::<T>(),
432 }
433 }
434
435 /// Create a new task input argument from `scalar`
436 ///
437 ///
438 /// # Examples
439 ///
440 ///```no_run
441 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
442 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
443 /// .with_compile_args("-D MYDEF").load();
444 /// let data = 4;
445 /// let task = mcl.task("my_kernel", 1)
446 /// .arg(mcl_rs::TaskArg::input_scalar(&data));
447 ///```
448 pub fn input_scalar<T>(scalar: &'a T) -> Self {
449 // let slice = std::
450 TaskArg {
451 data: TaskArgData::Scalar(to_u8_slice(std::slice::from_ref(scalar))),
452 flags: ArgOpt::INPUT | ArgOpt::SCALAR,
453 orig_type_size: std::mem::size_of::<T>(),
454 }
455 }
456 // /// Requests an allocation of `num_bytes`
457 // ///
458 // ///
459 // /// # Examples
460 // ///
461 // ///```no_run
462 // /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
463 // /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
464 // /// .with_compile_args("-D MYDEF").load();
465 // /// let data = 4;
466 // /// let les: [u64; 3] = [1, 1, 1];
467 // /// let pes: [u64; 3] = [1, 1, 1];
468 // /// let task = mcl.task("my_kernel", 1)
469 // /// .arg(mcl_rs::TaskArg::input_local(400));
470 // ///```
471 // pub fn input_local(num_bytes: usize) -> Self {
472 // TaskArg {
473 // data: TaskArgData::Local(num_bytes),
474 // flags: ArgOpt::LOCAL,
475 // orig_type_size: 1,
476 // }
477 // }
478
479 /// Create an new task input argument using a shared memory buffer
480 ///
481 /// # Examples
482 ///
483 ///```no_run
484 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
485 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
486 /// .with_compile_args("-D MYDEF").load();
487 ///
488 /// let num_elems = 100;
489 /// let buffer = mcl.create_shared_buffer(mcl_rs::TaskArg::input_shared::<f32>("my_buffer",num_elems).resident(true));
490 /// let task = mcl.task("my_kernel",1)
491 /// .input_shared(buffer);
492 ///```
493 #[cfg(feature = "shared_mem")]
494 pub fn input_shared<T>(name: &str, size: usize) -> Self {
495 TaskArg {
496 data: TaskArgData::Shared(name.to_owned(), size * std::mem::size_of::<T>()),
497 flags: ArgOpt::SHARED | ArgOpt::INPUT | ArgOpt::BUFFER,
498 orig_type_size: std::mem::size_of::<T>(),
499 }
500 }
501
502 /// Create a new task output argument from `slice`
503 ///
504 /// Returns a new TaskArg
505 ///
506 /// # Examples
507 ///
508 ///```no_run
509 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
510 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
511 /// .with_compile_args("-D MYDEF").load();
512 /// let mut data = vec![0; 4];
513 /// let mcl_future = mcl.task("my_kernel", 1)
514 /// .arg(mcl_rs::TaskArg::output_slice(&mut data));
515 ///```
516 pub fn output_slice<T>(slice: &'a mut [T]) -> Self {
517 TaskArg {
518 data: TaskArgData::Buffer(to_u8_slice(slice)),
519 flags: ArgOpt::OUTPUT | ArgOpt::BUFFER,
520 orig_type_size: std::mem::size_of::<T>(),
521 }
522 }
523
524 /// Create an new task output argument using a shared memory buffer
525 ///
526 /// # Examples
527 ///
528 ///```no_run
529 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
530 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
531 /// .with_compile_args("-D MYDEF").load();
532 ///
533 /// let num_elems = 100;
534 /// let buffer = mcl.create_shared_buffer(mcl_rs::TaskArg::output_shared::<f32>("my_buffer",num_elems).resident(true));
535 /// let task = mcl.task("my_kernel",1)
536 /// .output_shared(buffer);
537 ///```
538 #[cfg(feature = "shared_mem")]
539 pub fn output_shared<T>(name: &str, size: usize) -> Self {
540 TaskArg {
541 data: TaskArgData::Shared(name.to_owned(), size * std::mem::size_of::<T>()),
542 flags: ArgOpt::SHARED | ArgOpt::OUTPUT | ArgOpt::BUFFER,
543 orig_type_size: std::mem::size_of::<T>(),
544 }
545 }
546
547 /// Create a new task output argument from `scalar`
548 ///
549 /// Returns a new TaskArg
550 ///
551 /// # Examples
552 ///
553 ///```no_run
554 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
555 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
556 /// .with_compile_args("-D MYDEF").load();
557 /// let mut data = 4;
558 /// let mcl_future = mcl.task("my_kernel", 1)
559 /// .arg(mcl_rs::TaskArg::output_scalar(&mut data));
560 ///```
561 pub fn output_scalar<T>(scalar: &'a T) -> Self {
562 //mcl expects all outputs to be buffers but we want a nice consistent interface here!
563 TaskArg {
564 data: TaskArgData::Buffer(to_u8_slice(std::slice::from_ref(scalar))),
565 flags: ArgOpt::OUTPUT | ArgOpt::BUFFER,
566 orig_type_size: std::mem::size_of::<T>(),
567 }
568 }
569
570 /// Create a new task input+output argument from `slice`
571 ///
572 /// Returns a new TaskArg
573 ///
574 /// # Examples
575 ///
576 ///```no_run
577 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
578 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
579 /// .with_compile_args("-D MYDEF").load();
580 /// let mut data = vec![0; 4];
581 /// let mcl_future = mcl.task("my_kernel", 1)
582 /// .arg(mcl_rs::TaskArg::inout_slice(&mut data));
583 ///```
584 pub fn inout_slice<T>(slice: &'a [T]) -> Self {
585 TaskArg {
586 data: TaskArgData::Buffer(to_u8_slice(slice)),
587 flags: ArgOpt::OUTPUT | ArgOpt::INPUT | ArgOpt::BUFFER,
588 orig_type_size: std::mem::size_of::<T>(),
589 }
590 }
591
592 /// Create an new task input+output argument using a shared memory buffer
593 ///
594 /// # Examples
595 ///
596 ///```no_run
597 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
598 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
599 /// .with_compile_args("-D MYDEF").load();
600 ///
601 /// let num_elems = 100;
602 /// let buffer = mcl.create_shared_buffer(mcl_rs::TaskArg::inout_shared::<f32>("my_buffer",num_elems).resident(true));
603 /// let task = mcl.task("my_kernel",1)
604 /// .inout_shared(buffer);
605 ///```
606 #[cfg(feature = "shared_mem")]
607 pub fn inout_shared<T>(name: &str, size: usize) -> Self {
608 // println!("inout_shared: {size} {}",size*std::mem::size_of::<T>());
609 TaskArg {
610 data: TaskArgData::Shared(name.to_owned(), size * std::mem::size_of::<T>()),
611 flags: ArgOpt::SHARED | ArgOpt::INPUT | ArgOpt::OUTPUT | ArgOpt::BUFFER,
612 orig_type_size: std::mem::size_of::<T>(),
613 }
614 }
615
616 /// Create a new task input+output argument from `scalar`
617 ///
618 /// Returns a new TaskArg
619 ///
620 /// # Examples
621 ///
622 ///```no_run
623 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
624 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
625 /// .with_compile_args("-D MYDEF").load();
626 /// let mut data = 4;
627 /// let mcl_future = mcl.task("my_kernel", 1)
628 /// .arg(mcl_rs::TaskArg::inout_scalar(&mut data));
629 ///```
630 pub fn inout_scalar<T>(scalar: &'a T) -> Self {
631 TaskArg {
632 data: TaskArgData::Buffer(to_u8_slice(std::slice::from_ref(scalar))),
633 flags: ArgOpt::OUTPUT | ArgOpt::INPUT | ArgOpt::BUFFER,
634 orig_type_size: std::mem::size_of::<T>(),
635 }
636 }
637
638 /// Sets the resident memory flag for the argument
639 ///
640 /// Returns the TaskArg with the preference set
641 ///
642 /// # Examples
643 ///
644 ///```no_run
645 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
646 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
647 /// .with_compile_args("-D MYDEF").load();
648 /// let data = vec![0; 4];
649 /// let les: [u64; 3] = [1, 1, 1];
650 /// let pes: [u64; 3] = [1, 1, 1];
651 /// let mcl_future = mcl.task("my_kernel", 1)
652 /// .arg(mcl_rs::TaskArg::input_slice(&data).resident(true))
653 /// .exec(pes);
654 /// futures::executor::block_on(mcl_future);
655 ///```
656 pub fn resident(mut self, val: bool) -> Self {
657 if val {
658 self.flags = self.flags | ArgOpt::RESIDENT;
659 } else {
660 self.flags = self.flags & !ArgOpt::RESIDENT;
661 }
662 return self;
663 }
664
665 /// Sets the dynamic memory flag for the argument
666 ///
667 /// Returns the TaskArg with the preference set
668 ///
669 /// # Examples
670 ///
671 ///```no_run
672 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
673 /// mcl.load_prog("my_prog",mcl_rs::PrgType::Src);
674 /// let data = vec![0; 4];
675 /// let les: [u64; 3] = [1, 1, 1];
676 /// let pes: [u64; 3] = [1, 1, 1];
677 /// let mcl_future = mcl.task("my_kernel", 1)
678 /// .arg(mcl_rs::TaskArg::input_slice(&data).dynamic(true))
679 /// .exec(pes);
680 /// futures::executor::block_on(mcl_future);
681 ///```
682 pub fn dynamic(mut self, val: bool) -> Self {
683 if val {
684 self.flags = self.flags | ArgOpt::DYNAMIC;
685 } else {
686 self.flags = self.flags & !ArgOpt::DYNAMIC;
687 }
688 return self;
689 }
690
691 /// Sets the done flag for the argument
692 ///
693 /// Returns the TaskArg with the preference set
694 ///
695 /// # Examples
696 ///
697 ///```no_run
698 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
699 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
700 /// .with_compile_args("-D MYDEF").load();
701 /// let data = vec![0; 4];
702 /// let les: [u64; 3] = [1, 1, 1];
703 /// let pes: [u64; 3] = [1, 1, 1];
704 /// let mcl_future = mcl.task("my_kernel", 1)
705 /// .arg(mcl_rs::TaskArg::input_slice(&data).done(true))
706 /// .exec(pes);
707 /// futures::executor::block_on(mcl_future);
708 ///```
709 pub fn done(mut self, val: bool) -> Self {
710 if val {
711 self.flags = self.flags | ArgOpt::DONE;
712 } else {
713 self.flags = self.flags & !ArgOpt::DONE;
714 }
715 return self;
716 }
717
718 /// Sets the invalid flag for the argument
719 ///
720 /// Returns the TaskArg with the preference set
721 ///
722 /// # Examples
723 ///
724 ///```no_run
725 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
726 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
727 /// .with_compile_args("-D MYDEF").load();
728 /// let data = vec![0; 4];
729 /// let les: [u64; 3] = [1, 1, 1];
730 /// let pes: [u64; 3] = [1, 1, 1];
731 /// let mcl_future = mcl.task("my_kernel", 1)
732 /// .arg(mcl_rs::TaskArg::input_slice(&data).invalid(true))
733 /// .exec(pes);
734 /// futures::executor::block_on(mcl_future);
735 ///```
736 pub fn invalid(mut self, val: bool) -> Self {
737 if val {
738 self.flags = self.flags | ArgOpt::INVALID;
739 } else {
740 self.flags = self.flags & !ArgOpt::INVALID;
741 }
742 return self;
743 }
744
745 /// Sets the read_only flag for the argument
746 ///
747 /// Returns the TaskArg with the preference set
748 ///
749 /// # Examples
750 ///
751 ///```no_run
752 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
753 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
754 /// .with_compile_args("-D MYDEF").load();
755 /// let data = vec![0; 4];
756 /// let les: [u64; 3] = [1, 1, 1];
757 /// let pes: [u64; 3] = [1, 1, 1];
758 /// let mcl_future = mcl.task("my_kernel", 1)
759 /// .arg(mcl_rs::TaskArg::input_slice(&data).read_only(true))
760 /// .exec(pes);
761 /// futures::executor::block_on(mcl_future);
762 ///```
763 pub fn read_only(mut self, val: bool) -> Self {
764 if val {
765 self.flags = self.flags | ArgOpt::RDONLY;
766 } else {
767 self.flags = self.flags & !ArgOpt::RDONLY;
768 }
769 return self;
770 }
771
772 /// Sets the write_only flag for the argument
773 ///
774 /// Returns the TaskArg with the preference set
775 ///
776 /// # Examples
777 ///
778 ///```no_run
779 /// let mcl = mcl_rs::MclEnvBuilder::new().initialize();
780 /// mcl.create_prog("my_prog",mcl_rs::PrgType::Src)
781 /// .with_compile_args("-D MYDEF").load();
782 /// let data = vec![0; 4];
783 /// let les: [u64; 3] = [1, 1, 1];
784 /// let pes: [u64; 3] = [1, 1, 1];
785 /// let mcl_future = mcl.task("my_kernel", 1)
786 /// .arg(mcl_rs::TaskArg::input_slice(&data).write_only(true))
787 /// .exec(pes);
788 /// futures::executor::block_on(mcl_future);
789 ///```
790 pub fn write_only(mut self, val: bool) -> Self {
791 if val {
792 self.flags = self.flags | ArgOpt::WRONLY;
793 } else {
794 self.flags = self.flags & !ArgOpt::WRONLY;
795 }
796 return self;
797 }
798
799 // /// Overwrites the flags already set for the argument with the given ones
800 // ///
801 // /// ## Arguments
802 // ///
803 // /// * `bit_flags` - The bit_flags to replace existing flags with
804 // ///
805 // /// Returns the TaskArg with the preference set
806 // pub fn bit_flags(mut self, bit_flags: ArgOpt) -> Self {
807
808 // self.flags = bit_flags;
809
810 // return self;
811 // }
812}
813
814#[cfg(feature = "versal")]
815pub struct TaskBinProps {
816 c_handle: mcl_bin_properties,
817}
818
819#[cfg(feature = "versal")]
820impl TaskBinProps {
821 pub fn new(num_devices: u64, types: u64, name: &str) -> Self {
822 TaskBinProps {
823 c_handle: mcl_bin_properties {
824 devices: num_devices,
825 types: types,
826 name: CString::new(name).unwrap().into_raw(),
827 },
828 }
829 }
830
831 pub fn get_devices(&self) -> u64 {
832 self.c_handle.devices
833 }
834
835 pub fn get_types(&self) -> u64 {
836 self.c_handle.types
837 }
838
839 pub fn get_name(&self) -> String {
840 return unsafe {
841 CString::from_raw(self.c_handle.name as *mut _)
842 .into_string()
843 .unwrap()
844 };
845 }
846}