1use crate::runtime::{
2 config::{TypeNameFormatLevel, type_name_format},
3 kernel::KernelMetadata,
4 logging::ProfileLevel,
5 memory_management::{MemoryAllocationMode, MemoryUsage},
6 backend::Runtime,
7 server::{
8 CommunicationId, ComputeServer, CopyDescriptor, RudaCount, ExecutionMode, Handle, IoError,
9 KernelArguments, MemoryLayout, MemoryLayoutDescriptor, MemoryLayoutPolicy,
10 MemoryLayoutStrategy, ProfileError, ReduceOperation, ServerCommunication, ServerError,
11 ServerUtilities,
12 },
13 storage::{ComputeStorage, ManagedResource},
14};
15use alloc::{format, sync::Arc, vec, vec::Vec};
16use ruda_core::{
17 backtrace::BackTrace,
18 bytes::{AllocationProperty, Bytes},
19 device::{Device, DeviceId},
20 device_handle::DeviceHandle,
21 future::DynFut,
22 profile::ProfileDuration,
23};
24use ruda_core::ir::{DeviceProperties, ElemType, VectorSize, features::Features};
25use ruda_core::tensor::Shape;
26
27#[allow(unused)]
28use ruda_core::profile::TimingMethod;
29use ruda_core::stream_id::StreamId;
30
31pub struct ComputeClient<R: Runtime> {
34 device: DeviceHandle<R::Server>,
35 utilities: Arc<ServerUtilities<R::Server>>,
36 stream_id: Option<StreamId>,
37}
38
39impl<R: Runtime> Clone for ComputeClient<R> {
40 fn clone(&self) -> Self {
41 Self {
42 device: self.device.clone(),
43 utilities: self.utilities.clone(),
44 stream_id: self.stream_id,
45 }
46 }
47}
48
49impl<R: Runtime> ComputeClient<R> {
50 pub fn info(&self) -> &<R::Server as ComputeServer>::Info {
52 &self.utilities.info
53 }
54
55 pub fn init<D: Device>(device: &D, server: R::Server) -> Self {
57 let utilities = server.utilities();
58 let context = DeviceHandle::<R::Server>::insert(device.to_id(), server)
59 .expect("Can't create a new client on an already registered server");
60
61 Self {
62 device: context,
63 utilities,
64 stream_id: None,
65 }
66 }
67
68 pub fn load<D: Device>(device: &D) -> Self {
70 let context = DeviceHandle::<R::Server>::new(device.to_id());
71
72 let utilities = context
74 .utilities()
75 .downcast::<ServerUtilities<R::Server>>()
76 .expect("Can downcast to `ServerUtilities`");
77
78 Self {
79 device: context,
80 utilities,
81 stream_id: None,
82 }
83 }
84
85 fn stream_id(&self) -> StreamId {
86 match self.stream_id {
87 Some(val) => val,
88 None => StreamId::current(),
89 }
90 }
91
92 pub fn same_execution_queue(&self, other: &Self) -> bool {
95 self.device.device_id() == other.device.device_id() && self.stream_id() == other.stream_id()
96 }
97
98 pub unsafe fn set_stream(&mut self, stream_id: StreamId) {
104 self.stream_id = Some(stream_id);
105 }
106
107 fn do_read(&self, descriptors: Vec<CopyDescriptor>) -> DynFut<Result<Vec<Bytes>, ServerError>> {
108 let stream_id = self.stream_id();
109 self.device
110 .submit_blocking(move |server| server.read(descriptors, stream_id))
111 .unwrap()
112 }
113
114 pub fn read_async(
116 &self,
117 handles: Vec<Handle>,
118 ) -> impl Future<Output = Result<Vec<Bytes>, ServerError>> + Send {
119 let shapes = handles
120 .iter()
121 .map(|it| [it.size_in_used() as usize].into())
122 .collect::<Vec<Shape>>();
123 let descriptors = handles
124 .into_iter()
125 .zip(shapes)
126 .map(|(handle, shape)| CopyDescriptor::new(handle.binding(), shape, [1].into(), 1))
127 .collect();
128
129 self.do_read(descriptors)
130 }
131
132 pub fn read(&self, handles: Vec<Handle>) -> Vec<Bytes> {
138 ruda_core::reader::read_sync(self.read_async(handles)).expect("TODO")
139 }
140
141 pub fn read_one(&self, handle: Handle) -> Result<Bytes, ServerError> {
143 Ok(ruda_core::reader::read_sync(self.read_async(vec![handle]))?.remove(0))
144 }
145
146 pub fn read_one_unchecked(&self, handle: Handle) -> Bytes {
152 ruda_core::reader::read_sync(self.read_async(vec![handle]))
153 .unwrap()
154 .remove(0)
155 }
156
157 pub fn read_tensor_async(
159 &self,
160 descriptors: Vec<CopyDescriptor>,
161 ) -> impl Future<Output = Result<Vec<Bytes>, ServerError>> + Send {
162 self.do_read(descriptors)
163 }
164
165 pub fn read_tensor(&self, descriptors: Vec<CopyDescriptor>) -> Vec<Bytes> {
178 ruda_core::reader::read_sync(self.read_tensor_async(descriptors)).expect("TODO")
179 }
180
181 pub fn read_one_tensor_async(
184 &self,
185 descriptor: CopyDescriptor,
186 ) -> impl Future<Output = Result<Bytes, ServerError>> + Send {
187 let fut = self.read_tensor_async(vec![descriptor]);
188
189 async { Ok(fut.await?.remove(0)) }
190 }
191
192 pub fn read_one_unchecked_tensor(&self, descriptor: CopyDescriptor) -> Bytes {
199 self.read_tensor(vec![descriptor]).remove(0)
200 }
201
202 pub fn get_resource(
204 &self,
205 handle: Handle,
206 ) -> Result<
207 ManagedResource<<<R::Server as ComputeServer>::Storage as ComputeStorage>::Resource>,
208 ServerError,
209 > {
210 let stream_id = self.stream_id();
211 let binding = handle.binding();
212
213 self.device
214 .submit_blocking(move |state| state.get_resource(binding, stream_id))
215 .unwrap()
216 }
217
218 fn do_create_from_slices(
219 &self,
220 descriptors: Vec<MemoryLayoutDescriptor>,
221 slices: Vec<Vec<u8>>,
222 ) -> Result<Vec<MemoryLayout>, IoError> {
223 let stream_id = self.stream_id();
224 let (handle_base, layouts) = self.utilities.layout_policy.apply(stream_id, &descriptors);
225
226 let descriptors = descriptors
227 .into_iter()
228 .zip(layouts.iter())
229 .zip(slices)
230 .map(|((desc, alloc), data)| {
231 (
232 CopyDescriptor::new(
233 alloc.memory.clone().binding(),
234 desc.shape,
235 alloc.strides.clone(),
236 desc.elem_size,
237 ),
238 Bytes::from_bytes_vec(data),
239 )
240 })
241 .collect::<Vec<_>>();
242
243 let (size, memory) = (handle_base.size(), handle_base.memory);
244 self.device.submit(move |server| {
245 server.initialize_memory(memory, size, stream_id);
246 server.write(descriptors, stream_id);
247 });
248
249 Ok(layouts)
250 }
251
252 fn do_create(
253 &self,
254 descriptors: Vec<MemoryLayoutDescriptor>,
255 mut data: Vec<Bytes>,
256 ) -> Result<Vec<MemoryLayout>, IoError> {
257 self.staging(data.iter_mut(), true);
258
259 let stream_id = self.stream_id();
260 let (handle_base, layouts) = self.utilities.layout_policy.apply(stream_id, &descriptors);
261
262 let descriptors = descriptors
263 .into_iter()
264 .zip(layouts.iter())
265 .zip(data)
266 .map(|((desc, layout), data)| {
267 (
268 CopyDescriptor::new(
269 layout.memory.clone().binding(),
270 desc.shape,
271 layout.strides.clone(),
272 desc.elem_size,
273 ),
274 data,
275 )
276 })
277 .collect::<Vec<_>>();
278
279 let (size, memory) = (handle_base.size(), handle_base.memory);
280 self.device.submit(move |server| {
281 server.initialize_memory(memory, size, stream_id);
282 server.write(descriptors, stream_id);
283 });
284
285 Ok(layouts)
286 }
287
288 pub fn create_from_slice(&self, slice: &[u8]) -> Handle {
294 let shape: Shape = [slice.len()].into();
295
296 self.do_create_from_slices(
297 vec![MemoryLayoutDescriptor::new(
298 MemoryLayoutStrategy::Contiguous,
299 shape,
300 1,
301 )],
302 vec![slice.to_vec()],
303 )
304 .unwrap()
305 .remove(0)
306 .memory
307 }
308
309 pub fn exclusive<'a, Re: Send + 'static, F: FnOnce() -> Re + Send + 'a>(
311 &'a self,
312 task: F,
313 ) -> Result<Re, ServerError> {
314 self.device
316 .exclusive(task)
317 .map_err(|err| ServerError::Generic {
318 reason: format!("Communication channel with the server is down: {err:?}"),
319 backtrace: BackTrace::capture(),
320 })
321 }
322
323 pub fn memory_persistent_allocation<
325 'a,
326 Re: Send,
327 Input: Send,
328 F: FnOnce(Input) -> Re + Send + 'a,
329 >(
330 &'a self,
331 input: Input,
332 task: F,
333 ) -> Result<Re, ServerError> {
334 let stream_id = StreamId::current();
335
336 self.device.submit(move |server| {
337 server.allocation_mode(MemoryAllocationMode::Persistent, stream_id);
338 });
339
340 let output = task(input);
342
343 self.device.submit(move |server| {
344 server.allocation_mode(MemoryAllocationMode::Auto, stream_id);
345 });
346
347 Ok(output)
348 }
349
350 pub fn create(&self, data: Bytes) -> Handle {
352 let shape = [data.len()].into();
353
354 self.do_create(
355 vec![MemoryLayoutDescriptor::new(
356 MemoryLayoutStrategy::Contiguous,
357 shape,
358 1,
359 )],
360 vec![data],
361 )
362 .unwrap()
363 .remove(0)
364 .memory
365 }
366
367 pub fn create_tensor_from_slice(
385 &self,
386 slice: &[u8],
387 shape: Shape,
388 elem_size: usize,
389 ) -> MemoryLayout {
390 self.do_create_from_slices(
391 vec![MemoryLayoutDescriptor::new(
392 MemoryLayoutStrategy::Optimized,
393 shape,
394 elem_size,
395 )],
396 vec![slice.to_vec()],
397 )
398 .unwrap()
399 .remove(0)
400 }
401
402 pub fn create_tensor(&self, bytes: Bytes, shape: Shape, elem_size: usize) -> MemoryLayout {
416 self.do_create(
417 vec![MemoryLayoutDescriptor::new(
418 MemoryLayoutStrategy::Optimized,
419 shape,
420 elem_size,
421 )],
422 vec![bytes],
423 )
424 .unwrap()
425 .remove(0)
426 }
427
428 pub fn create_tensors_from_slices(
436 &self,
437 descriptors: Vec<(MemoryLayoutDescriptor, &[u8])>,
438 ) -> Vec<MemoryLayout> {
439 let mut data = Vec::with_capacity(descriptors.len());
440 let mut descriptors_ = Vec::with_capacity(descriptors.len());
441 for (a, b) in descriptors {
442 data.push(b.to_vec());
443 descriptors_.push(a);
444 }
445
446 self.do_create_from_slices(descriptors_, data).unwrap()
447 }
448
449 pub fn create_tensors(
453 &self,
454 descriptors: Vec<(MemoryLayoutDescriptor, Bytes)>,
455 ) -> Vec<MemoryLayout> {
456 let (descriptors, data) = descriptors.into_iter().unzip();
457
458 self.do_create(descriptors, data).unwrap()
459 }
460
461 fn do_empty(
462 &self,
463 descriptors: Vec<MemoryLayoutDescriptor>,
464 ) -> Result<Vec<MemoryLayout>, IoError> {
465 let stream_id = self.stream_id();
466 let (handle_base, layouts) = self.utilities.layout_policy.apply(stream_id, &descriptors);
467
468 let (size, memory) = (handle_base.size(), handle_base.memory);
469 self.device.submit(move |server| {
470 server.initialize_memory(memory, size, stream_id);
471 });
472
473 Ok(layouts)
474 }
475
476 pub fn empty(&self, size: usize) -> Handle {
478 let shape: Shape = [size].into();
479 let descriptor = MemoryLayoutDescriptor::new(MemoryLayoutStrategy::Contiguous, shape, 1);
480 self.do_empty(vec![descriptor]).unwrap().remove(0).memory
481 }
482
483 pub fn empty_tensor(&self, shape: Shape, elem_size: usize) -> MemoryLayout {
486 let descriptor =
487 MemoryLayoutDescriptor::new(MemoryLayoutStrategy::Optimized, shape, elem_size);
488 self.do_empty(vec![descriptor]).unwrap().remove(0)
489 }
490
491 pub fn empty_tensors(&self, descriptors: Vec<MemoryLayoutDescriptor>) -> Vec<MemoryLayout> {
494 self.do_empty(descriptors).unwrap()
495 }
496
497 pub fn staging<'a, I>(&self, bytes: I, file_only: bool)
502 where
503 I: Iterator<Item = &'a mut Bytes>,
504 {
505 let has_staging = |b: &Bytes| match b.property() {
506 AllocationProperty::Pinned => false,
507 AllocationProperty::File => true,
508 AllocationProperty::Native | AllocationProperty::Other => !file_only,
509 };
510
511 let mut to_be_updated = Vec::new();
512 let sizes = bytes
513 .filter_map(|b| match has_staging(b) {
514 true => {
515 let len = b.len();
516 to_be_updated.push(b);
517 Some(len)
518 }
519 false => None,
520 })
521 .collect::<Vec<usize>>();
522
523 if sizes.is_empty() {
524 return;
525 }
526
527 let stream_id = self.stream_id();
528 let sizes = sizes.to_vec();
529 let stagings = self
530 .device
531 .submit_blocking(move |server| server.staging(&sizes, stream_id))
532 .unwrap();
533
534 let stagings = match stagings {
535 Ok(val) => val,
536 Err(_) => return,
537 };
538
539 to_be_updated
540 .into_iter()
541 .zip(stagings)
542 .for_each(|(b, mut staging)| {
543 b.copy_into(&mut staging);
544 core::mem::swap(b, &mut staging);
545 });
546 }
547
548 #[cfg_attr(
550 feature = "runtime-tracing",
551 tracing::instrument(level = "trace", skip(self, src, dst_server))
552 )]
553 pub fn to_client(&mut self, src: Handle, dst_server: &Self, dtype: ElemType) -> Handle {
554 let shape = [src.size_in_used() as usize];
555 let src_descriptor = src.copy_descriptor(shape.into(), [1].into(), 1);
556
557 if R::Server::SERVER_COMM_ENABLED {
558 self.to_client_tensor(src_descriptor, dst_server, dtype)
559 } else {
560 let alloc_desc = MemoryLayoutDescriptor::new(
561 MemoryLayoutStrategy::Contiguous,
562 src_descriptor.shape.clone(),
563 src_descriptor.elem_size,
564 );
565 self.change_client_sync(src_descriptor, alloc_desc, dst_server)
566 .memory
567 }
568 }
569
570 #[cfg_attr(
572 feature = "runtime-tracing",
573 tracing::instrument(level = "trace", skip(self, device_ids))
574 )]
575 pub fn ensure_init_collective(&mut self, device_ids: Vec<DeviceId>) {
576 let comm_id = CommunicationId::from(device_ids.clone());
577 let is_comms_init = self
578 .utilities
579 .initialized_comms
580 .read()
581 .unwrap()
582 .contains(&comm_id);
583 if !is_comms_init {
584 self.device
585 .submit(move |server| server.comm_init(device_ids).unwrap());
586 let mut initialized_comms = self.utilities.initialized_comms.write().unwrap();
587 initialized_comms.insert(comm_id);
588 self.device.flush_queue();
590 }
591 }
592
593 #[cfg_attr(feature = "runtime-tracing", tracing::instrument(level = "trace", skip(self)))]
595 pub fn sync_collective(&self) {
596 if DeviceHandle::<R::Server>::is_blocking() {
597 panic!("Can't use `sync_collective` with a blocking device handle");
598 }
599 let stream_id = self.stream_id();
600
601 self.device.submit(move |server| {
602 server.sync_collective(stream_id).unwrap();
603 });
604
605 self.device.flush_queue();
608 }
609
610 #[cfg_attr(
612 feature = "runtime-tracing",
613 tracing::instrument(level = "trace", skip(self, src, dst, dtype, device_ids, op))
614 )]
615 pub fn all_reduce(
616 &mut self,
617 src: Handle,
618 dst: Handle,
619 dtype: ElemType,
620 device_ids: Vec<DeviceId>,
621 op: ReduceOperation,
622 ) {
623 if DeviceHandle::<R::Server>::is_blocking() {
624 panic!("Can't use `all_reduce` with a blocking device handle");
625 }
626
627 let stream_id = self.stream_id();
628 let src = src.binding();
629 let dst = dst.binding();
630
631 self.ensure_init_collective(device_ids.clone());
632
633 self.device.submit(move |server| {
634 server
635 .all_reduce(src, dst, dtype, stream_id, op, device_ids)
636 .unwrap();
637 });
638 }
639
640 #[cfg_attr(
644 feature = "runtime-tracing",
645 tracing::instrument(level = "trace", skip(self, src_descriptor, dst_server))
646 )]
647 pub fn to_client_tensor(
648 &mut self,
649 src_descriptor: CopyDescriptor,
650 dst_server: &Self,
651 dtype: ElemType,
652 ) -> Handle {
653 let stream_id_src = self.stream_id();
654 let stream_id_dst = dst_server.stream_id();
655
656 let device_id_src = self.device.device_id();
657 let device_id_dst = dst_server.device.device_id();
658
659 let mut dst_server = dst_server.clone();
660 let handle = Handle::new(stream_id_dst, src_descriptor.handle.size_in_used());
661 let handle_cloned = handle.clone();
662
663 let device_ids = vec![device_id_src, device_id_dst];
664 self.ensure_init_collective(device_ids.clone());
665 dst_server.ensure_init_collective(device_ids);
666
667 self.device.submit(move |server_src| {
668 server_src
669 .send(src_descriptor, dtype, stream_id_src, device_id_dst)
670 .unwrap()
671 });
672
673 dst_server.device.submit(move |server_dst| {
674 server_dst
675 .recv(handle_cloned, dtype, stream_id_dst, device_id_src)
676 .unwrap();
677 server_dst.sync_collective(stream_id_dst).unwrap();
678 });
679
680 self.device.flush_queue();
684 dst_server.device.flush_queue();
685
686 handle
687 }
688
689 #[track_caller]
690 #[cfg_attr(feature = "runtime-tracing", tracing::instrument(level="trace",
691 skip(self, kernel, bindings),
692 fields(
693 kernel.name = %kernel.name(),
694 kernel.id = %kernel.id(),
695 )
696 ))]
697 unsafe fn launch_inner(
698 &self,
699 kernel: <R::Server as ComputeServer>::Kernel,
700 count: RudaCount,
701 bindings: KernelArguments,
702 mode: ExecutionMode,
703 stream_id: StreamId,
704 ) {
705 let level = self.utilities.logger.profile_level();
706
707 match level {
708 None | Some(ProfileLevel::ExecutionOnly) => {
709 let utilities = self.utilities.clone();
710 self.device.submit(move |state| {
711 let name = kernel.name();
712 unsafe { state.launch(kernel, count, bindings, mode, stream_id) };
713
714 if matches!(level, Some(ProfileLevel::ExecutionOnly)) {
715 let info = type_name_format(name, TypeNameFormatLevel::Balanced);
716 utilities.logger.register_execution(info);
717 }
718 });
719 }
720 Some(level) => {
721 let name = kernel.name();
722 let kernel_id = kernel.id();
723 let context = self.device.clone();
724 let count_moved = count.clone();
725 let (result, profile) = self
726 .profile(
727 move || {
728 context
729 .submit_blocking(move |state| unsafe {
730 state.launch(kernel, count_moved, bindings, mode, stream_id)
731 })
732 .unwrap()
733 },
734 name,
735 )
736 .unwrap();
737 let info = match level {
738 ProfileLevel::Full => {
739 format!("{name}: {kernel_id} RudaCount {count:?}")
740 }
741 _ => type_name_format(name, TypeNameFormatLevel::Balanced),
742 };
743 self.utilities.logger.register_profiled(info, profile);
744 result
745 }
746 }
747 }
748
749 #[track_caller]
751 pub fn launch(
752 &self,
753 kernel: <R::Server as ComputeServer>::Kernel,
754 count: RudaCount,
755 bindings: KernelArguments,
756 ) {
757 unsafe {
759 self.launch_inner(
760 kernel,
761 count,
762 bindings,
763 ExecutionMode::Checked,
764 self.stream_id(),
765 )
766 }
767 }
768
769 #[track_caller]
777 pub unsafe fn launch_unchecked(
778 &self,
779 kernel: <R::Server as ComputeServer>::Kernel,
780 count: RudaCount,
781 bindings: KernelArguments,
782 ) {
783 unsafe {
785 self.launch_inner(
786 kernel,
787 count,
788 bindings,
789 match self.utilities.check_mode {
790 crate::runtime::config::compilation::BoundsCheckMode::Enforce => ExecutionMode::Checked,
791 crate::runtime::config::compilation::BoundsCheckMode::Validate => {
792 ExecutionMode::Validate
793 }
794 crate::runtime::config::compilation::BoundsCheckMode::Auto => ExecutionMode::Unchecked,
795 },
796 self.stream_id(),
797 )
798 }
799 }
800
801 pub fn flush(&self) -> Result<(), ServerError> {
803 let stream_id = self.stream_id();
804
805 self.device
806 .submit_blocking(move |server| server.flush(stream_id))
807 .unwrap()
808 }
809
810 pub fn sync(&self) -> DynFut<Result<(), ServerError>> {
812 let stream_id = self.stream_id();
813
814 let fut = self
815 .device
816 .submit_blocking(move |server| server.sync(stream_id))
817 .unwrap();
818
819 self.utilities.logger.profile_summary();
820
821 fut
822 }
823
824 pub fn properties(&self) -> &DeviceProperties {
826 &self.utilities.properties
827 }
828
829 pub fn features(&self) -> &Features {
831 &self.utilities.properties.features
832 }
833
834 pub fn properties_mut(&mut self) -> Option<&mut DeviceProperties> {
838 Arc::get_mut(&mut self.utilities).map(|state| &mut state.properties)
839 }
840
841 pub fn memory_usage(&self) -> Result<MemoryUsage, ServerError> {
843 let stream_id = self.stream_id();
844 self.device
845 .submit_blocking(move |server| server.memory_usage(stream_id))
846 .unwrap()
847 }
848
849 pub fn enumerate_devices(&self, type_id: u16) -> Vec<DeviceId> {
851 R::enumerate_devices(type_id, self.info())
852 }
853
854 pub fn enumerate_all_devices(&self) -> Vec<DeviceId> {
856 R::enumerate_all_devices(self.info())
857 }
858
859 pub fn device_count(&self, type_id: u16) -> usize {
861 self.enumerate_devices(type_id).len()
862 }
863
864 pub fn device_count_total(&self) -> usize {
866 self.enumerate_all_devices().len()
867 }
868
869 pub unsafe fn allocation_mode(&self, mode: MemoryAllocationMode) {
875 let stream_id = self.stream_id();
876 self.device
877 .submit(move |server| server.allocation_mode(mode, stream_id));
878 }
879
880 pub fn memory_cleanup(&self) {
885 let stream_id = self.stream_id();
886 self.device
887 .submit(move |server| server.memory_cleanup(stream_id));
888 }
889
890 #[track_caller]
892 pub fn profile<O: Send + 'static>(
893 &self,
894 func: impl FnOnce() -> O + Send,
895 #[allow(unused)] func_name: &str,
896 ) -> Result<(O, ProfileDuration), ProfileError> {
897 #[cfg(feature = "runtime-profile-tracy")]
900 let location = std::panic::Location::caller();
901
902 #[cfg(feature = "runtime-profile-tracy")]
904 let _span = tracy_client::Client::running().unwrap().span_alloc(
905 None,
906 func_name,
907 location.file(),
908 location.line(),
909 0,
910 );
911
912 let stream_id = self.stream_id();
913
914 #[cfg(feature = "runtime-profile-tracy")]
915 let gpu_span = if self.utilities.properties.timing_method == TimingMethod::Device {
916 let gpu_span = self
917 .utilities
918 .gpu_client
919 .span_alloc(func_name, "profile", location.file(), location.line())
920 .unwrap();
921 Some(gpu_span)
922 } else {
923 None
924 };
925
926 let device = self.device.clone();
927 #[allow(unused_mut, reason = "Used in profile-tracy")]
928 let mut result = self
929 .device
930 .exclusive(move || {
931 let token =
934 match device.submit_blocking(move |server| server.start_profile(stream_id)) {
935 Ok(token) => match token {
936 Ok(token) => token,
937 Err(err) => return Err(err),
938 },
939 Err(err) => {
940 return Err(ServerError::Generic {
941 reason: alloc::format!(
942 "Can't start profiling because of a call error: {err:?}"
943 ),
944 backtrace: BackTrace::capture(),
945 });
946 }
947 };
948
949 let out = func();
951
952 let result = device
954 .submit_blocking(move |server| {
955 let mut result = server.end_profile(stream_id, token);
956
957 match result {
958 Ok(result) => Ok((out, result)),
959 Err(err) => Err(err),
960 }
961 })
962 .unwrap();
963
964 Ok(result)
965 })
966 .unwrap()
967 .map_err(|err| ProfileError::Unknown {
968 reason: alloc::format!("{err:?}"),
969 backtrace: BackTrace::capture(),
970 })?;
971
972 #[cfg(feature = "runtime-profile-tracy")]
973 if let Some(mut gpu_span) = gpu_span {
974 gpu_span.end_zone();
975 let epoch = self.utilities.epoch_time;
976 result = result.map(|(o, result)| {
978 (
979 o,
980 ProfileDuration::new(
981 alloc::boxed::Box::pin(async move {
982 let ticks = result.resolve().await;
983 let start_duration =
984 ticks.start_duration_since(epoch).as_nanos() as i64;
985 let end_duration = ticks.end_duration_since(epoch).as_nanos() as i64;
986 gpu_span.upload_timestamp_start(start_duration);
987 gpu_span.upload_timestamp_end(end_duration);
988 ticks
989 }),
990 TimingMethod::Device,
991 ),
992 )
993 });
994 }
995
996 result
997 }
998
999 #[cfg_attr(
1001 feature = "runtime-tracing",
1002 tracing::instrument(
1003 level = "trace",
1004 skip(self, src_descriptor, alloc_descriptor, dst_server)
1005 )
1006 )]
1007 fn change_client_sync(
1008 &self,
1009 src_descriptor: CopyDescriptor,
1010 alloc_descriptor: MemoryLayoutDescriptor,
1011 dst_server: &Self,
1012 ) -> MemoryLayout {
1013 let shape = src_descriptor.shape.clone();
1014 let elem_size = src_descriptor.elem_size;
1015 let stream_id = self.stream_id();
1016
1017 let read = self
1018 .device
1019 .submit_blocking(move |server| server.read(vec![src_descriptor], stream_id))
1020 .unwrap();
1021
1022 let mut data = ruda_core::future::block_on(read).unwrap();
1023
1024 let (handle_base, mut layouts) = self
1025 .utilities
1026 .layout_policy
1027 .apply(stream_id, &[alloc_descriptor]);
1028 let alloc = layouts.remove(0);
1029
1030 let desc_descriptor = CopyDescriptor {
1031 handle: handle_base.clone().binding(),
1032 shape,
1033 strides: alloc.strides.clone(),
1034 elem_size,
1035 };
1036
1037 let (size, memory) = (handle_base.size(), handle_base.memory);
1038 dst_server.device.submit(move |server| {
1039 server.initialize_memory(memory, size, stream_id);
1040 server.write(vec![(desc_descriptor, data.remove(0))], stream_id)
1041 });
1042
1043 alloc
1044 }
1045
1046 pub fn io_optimized_vector_sizes(
1048 &self,
1049 size: usize,
1050 ) -> impl Iterator<Item = VectorSize> + Clone {
1051 let load_width = self.properties().hardware.load_width as usize;
1052 let size_bits = size * 8;
1053 let max = load_width / size_bits;
1054 let max = usize::min(self.properties().hardware.max_vector_size, max).max(1);
1059
1060 let num_candidates = max.trailing_zeros() + 1;
1062
1063 (0..num_candidates).map(|i| 2usize.pow(i)).rev()
1064 }
1065}