1use crate::{
2 config::{TypeNameFormatLevel, type_name_format},
3 kernel::KernelMetadata,
4 logging::ProfileLevel,
5 memory_management::{MemoryAllocationMode, MemoryUsage},
6 runtime::Runtime,
7 server::{
8 CommunicationId, ComputeServer, CopyDescriptor, CubeCount, 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 cubecl_common::{
17 backtrace::BackTrace,
18 bytes::{AllocationProperty, Bytes},
19 device::{Device, DeviceId},
20 device_handle::DeviceHandle,
21 future::DynFut,
22 profile::ProfileDuration,
23};
24use cubecl_ir::{DeviceProperties, ElemType, VectorSize, features::Features};
25use cubecl_zspace::Shape;
26
27#[allow(unused)]
28use cubecl_common::profile::TimingMethod;
29use cubecl_common::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 unsafe fn set_stream(&mut self, stream_id: StreamId) {
98 self.stream_id = Some(stream_id);
99 }
100
101 fn do_read(&self, descriptors: Vec<CopyDescriptor>) -> DynFut<Result<Vec<Bytes>, ServerError>> {
102 let stream_id = self.stream_id();
103 self.device
104 .submit_blocking(move |server| server.read(descriptors, stream_id))
105 .unwrap()
106 }
107
108 pub fn read_async(
110 &self,
111 handles: Vec<Handle>,
112 ) -> impl Future<Output = Result<Vec<Bytes>, ServerError>> + Send {
113 let shapes = handles
114 .iter()
115 .map(|it| [it.size_in_used() as usize].into())
116 .collect::<Vec<Shape>>();
117 let descriptors = handles
118 .into_iter()
119 .zip(shapes)
120 .map(|(handle, shape)| CopyDescriptor::new(handle.binding(), shape, [1].into(), 1))
121 .collect();
122
123 self.do_read(descriptors)
124 }
125
126 pub fn read(&self, handles: Vec<Handle>) -> Vec<Bytes> {
132 cubecl_common::reader::read_sync(self.read_async(handles)).expect("TODO")
133 }
134
135 pub fn read_one(&self, handle: Handle) -> Result<Bytes, ServerError> {
137 Ok(cubecl_common::reader::read_sync(self.read_async(vec![handle]))?.remove(0))
138 }
139
140 pub fn read_one_unchecked(&self, handle: Handle) -> Bytes {
146 cubecl_common::reader::read_sync(self.read_async(vec![handle]))
147 .unwrap()
148 .remove(0)
149 }
150
151 pub fn read_tensor_async(
153 &self,
154 descriptors: Vec<CopyDescriptor>,
155 ) -> impl Future<Output = Result<Vec<Bytes>, ServerError>> + Send {
156 self.do_read(descriptors)
157 }
158
159 pub fn read_tensor(&self, descriptors: Vec<CopyDescriptor>) -> Vec<Bytes> {
172 cubecl_common::reader::read_sync(self.read_tensor_async(descriptors)).expect("TODO")
173 }
174
175 pub fn read_one_tensor_async(
178 &self,
179 descriptor: CopyDescriptor,
180 ) -> impl Future<Output = Result<Bytes, ServerError>> + Send {
181 let fut = self.read_tensor_async(vec![descriptor]);
182
183 async { Ok(fut.await?.remove(0)) }
184 }
185
186 pub fn read_one_unchecked_tensor(&self, descriptor: CopyDescriptor) -> Bytes {
193 self.read_tensor(vec![descriptor]).remove(0)
194 }
195
196 pub fn get_resource(
198 &self,
199 handle: Handle,
200 ) -> Result<
201 ManagedResource<<<R::Server as ComputeServer>::Storage as ComputeStorage>::Resource>,
202 ServerError,
203 > {
204 let stream_id = self.stream_id();
205 let binding = handle.binding();
206
207 self.device
208 .submit_blocking(move |state| state.get_resource(binding, stream_id))
209 .unwrap()
210 }
211
212 fn do_create(
213 &self,
214 descriptors: Vec<MemoryLayoutDescriptor>,
215 mut data: Vec<Bytes>,
216 ) -> Result<Vec<MemoryLayout>, IoError> {
217 self.staging(data.iter_mut(), true);
218
219 let stream_id = self.stream_id();
220 let (handle_base, layouts) = self.utilities.layout_policy.apply(stream_id, &descriptors);
221
222 let descriptors = descriptors
223 .into_iter()
224 .zip(layouts.iter())
225 .zip(data)
226 .map(|((desc, layout), data)| {
227 (
228 CopyDescriptor::new(
229 layout.memory.clone().binding(),
230 desc.shape,
231 layout.strides.clone(),
232 desc.elem_size,
233 ),
234 data,
237 )
238 })
239 .collect::<Vec<_>>();
240
241 let (size, memory) = (handle_base.size(), handle_base.memory);
242 self.device.submit(move |server| {
243 server.initialize_memory(memory, size, stream_id);
244 server.write(descriptors, stream_id);
245 });
246
247 Ok(layouts)
248 }
249
250 pub fn create_from_slice(&self, slice: &[u8]) -> Handle {
256 let shape: Shape = [slice.len()].into();
257
258 self.do_create(
259 vec![MemoryLayoutDescriptor::new(
260 MemoryLayoutStrategy::Contiguous,
261 shape,
262 1,
263 )],
264 vec![staged_bytes(slice)],
265 )
266 .unwrap()
267 .remove(0)
268 .memory
269 }
270
271 pub fn exclusive<'a, Re: Send + 'static, F: FnOnce() -> Re + Send + 'a>(
273 &'a self,
274 task: F,
275 ) -> Result<Re, ServerError> {
276 self.device
278 .exclusive(task)
279 .map_err(|err| ServerError::Generic {
280 reason: format!("Communication channel with the server is down: {err:?}"),
281 backtrace: BackTrace::capture(),
282 })
283 }
284
285 pub fn memory_persistent_allocation<
287 'a,
288 Re: Send,
289 Input: Send,
290 F: FnOnce(Input) -> Re + Send + 'a,
291 >(
292 &'a self,
293 input: Input,
294 task: F,
295 ) -> Result<Re, ServerError> {
296 let stream_id = StreamId::current();
297
298 self.device.submit(move |server| {
299 server.allocation_mode(MemoryAllocationMode::Persistent, stream_id);
300 });
301
302 let output = task(input);
304
305 self.device.submit(move |server| {
306 server.allocation_mode(MemoryAllocationMode::Auto, stream_id);
307 });
308
309 Ok(output)
310 }
311
312 pub fn create(&self, data: Bytes) -> Handle {
314 let shape = [data.len()].into();
315
316 self.do_create(
317 vec![MemoryLayoutDescriptor::new(
318 MemoryLayoutStrategy::Contiguous,
319 shape,
320 1,
321 )],
322 vec![data],
323 )
324 .unwrap()
325 .remove(0)
326 .memory
327 }
328
329 pub fn create_tensor_from_slice(
347 &self,
348 slice: &[u8],
349 shape: Shape,
350 elem_size: usize,
351 ) -> MemoryLayout {
352 self.do_create(
353 vec![MemoryLayoutDescriptor::new(
354 MemoryLayoutStrategy::Optimized,
355 shape,
356 elem_size,
357 )],
358 vec![staged_bytes(slice)],
359 )
360 .unwrap()
361 .remove(0)
362 }
363
364 pub fn create_tensor(&self, bytes: Bytes, shape: Shape, elem_size: usize) -> MemoryLayout {
378 self.do_create(
379 vec![MemoryLayoutDescriptor::new(
380 MemoryLayoutStrategy::Optimized,
381 shape,
382 elem_size,
383 )],
384 vec![bytes],
385 )
386 .unwrap()
387 .remove(0)
388 }
389
390 pub fn create_tensors_from_slices(
398 &self,
399 descriptors: Vec<(MemoryLayoutDescriptor, &[u8])>,
400 ) -> Vec<MemoryLayout> {
401 let mut data = Vec::with_capacity(descriptors.len());
402 let mut descriptors_ = Vec::with_capacity(descriptors.len());
403 for (a, b) in descriptors {
404 data.push(staged_bytes(b));
405 descriptors_.push(a);
406 }
407
408 self.do_create(descriptors_, data).unwrap()
409 }
410
411 pub fn create_tensors(
415 &self,
416 descriptors: Vec<(MemoryLayoutDescriptor, Bytes)>,
417 ) -> Vec<MemoryLayout> {
418 let (descriptors, data) = descriptors.into_iter().unzip();
419
420 self.do_create(descriptors, data).unwrap()
421 }
422
423 fn do_empty(
424 &self,
425 descriptors: Vec<MemoryLayoutDescriptor>,
426 ) -> Result<Vec<MemoryLayout>, IoError> {
427 let stream_id = self.stream_id();
428 let (handle_base, layouts) = self.utilities.layout_policy.apply(stream_id, &descriptors);
429
430 let (size, memory) = (handle_base.size(), handle_base.memory);
431 self.device.submit(move |server| {
432 server.initialize_memory(memory, size, stream_id);
433 });
434
435 Ok(layouts)
436 }
437
438 pub fn empty(&self, size: usize) -> Handle {
440 let shape: Shape = [size].into();
441 let descriptor = MemoryLayoutDescriptor::new(MemoryLayoutStrategy::Contiguous, shape, 1);
442 self.do_empty(vec![descriptor]).unwrap().remove(0).memory
443 }
444
445 pub fn empty_tensor(&self, shape: Shape, elem_size: usize) -> MemoryLayout {
448 let descriptor =
449 MemoryLayoutDescriptor::new(MemoryLayoutStrategy::Optimized, shape, elem_size);
450 self.do_empty(vec![descriptor]).unwrap().remove(0)
451 }
452
453 pub fn empty_tensors(&self, descriptors: Vec<MemoryLayoutDescriptor>) -> Vec<MemoryLayout> {
456 self.do_empty(descriptors).unwrap()
457 }
458
459 pub fn staging<'a, I>(&self, bytes: I, file_only: bool)
464 where
465 I: Iterator<Item = &'a mut Bytes>,
466 {
467 let has_staging = |b: &Bytes| match b.property() {
468 AllocationProperty::Pinned => false,
469 AllocationProperty::File => true,
470 AllocationProperty::Native | AllocationProperty::Other => !file_only,
471 };
472
473 let mut to_be_updated = Vec::new();
474 let sizes = bytes
475 .filter_map(|b| match has_staging(b) {
476 true => {
477 let len = b.len();
478 to_be_updated.push(b);
479 Some(len)
480 }
481 false => None,
482 })
483 .collect::<Vec<usize>>();
484
485 if sizes.is_empty() {
486 return;
487 }
488
489 let stream_id = self.stream_id();
490 let sizes = sizes.to_vec();
491 let stagings = self
492 .device
493 .submit_blocking(move |server| server.staging(&sizes, stream_id))
494 .unwrap();
495
496 let stagings = match stagings {
497 Ok(val) => val,
498 Err(_) => return,
499 };
500
501 to_be_updated
502 .into_iter()
503 .zip(stagings)
504 .for_each(|(b, mut staging)| {
505 b.copy_into(&mut staging);
506 core::mem::swap(b, &mut staging);
507 });
508 }
509
510 #[cfg_attr(
512 feature = "tracing",
513 tracing::instrument(level = "trace", skip(self, src, dst_server))
514 )]
515 pub fn to_client(&mut self, src: Handle, dst_server: &Self, dtype: ElemType) -> Handle {
516 let shape = [src.size_in_used() as usize];
517 let src_descriptor = src.copy_descriptor(shape.into(), [1].into(), 1);
518
519 if R::Server::SERVER_COMM_ENABLED {
520 self.to_client_tensor(src_descriptor, dst_server, dtype)
521 } else {
522 let alloc_desc = MemoryLayoutDescriptor::new(
523 MemoryLayoutStrategy::Contiguous,
524 src_descriptor.shape.clone(),
525 src_descriptor.elem_size,
526 );
527 self.change_client_sync(src_descriptor, alloc_desc, dst_server)
528 .memory
529 }
530 }
531
532 #[cfg_attr(
534 feature = "tracing",
535 tracing::instrument(level = "trace", skip(self, device_ids))
536 )]
537 pub fn ensure_init_collective(&mut self, device_ids: Vec<DeviceId>) {
538 let comm_id = CommunicationId::from(device_ids.clone());
539 let is_comms_init = self
540 .utilities
541 .initialized_comms
542 .read()
543 .unwrap()
544 .contains(&comm_id);
545 if !is_comms_init {
546 self.device
547 .submit(move |server| server.comm_init(device_ids).unwrap());
548 let mut initialized_comms = self.utilities.initialized_comms.write().unwrap();
549 initialized_comms.insert(comm_id);
550 self.device.flush_queue();
552 }
553 }
554
555 #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip(self)))]
557 pub fn sync_collective(&self) {
558 if DeviceHandle::<R::Server>::is_blocking() {
559 panic!("Can't use `sync_collective` with a blocking device handle");
560 }
561 let stream_id = self.stream_id();
562
563 self.device.submit(move |server| {
564 server.sync_collective(stream_id).unwrap();
565 });
566
567 self.device.flush_queue();
570 }
571
572 #[cfg_attr(
574 feature = "tracing",
575 tracing::instrument(level = "trace", skip(self, src, dst, dtype, device_ids, op))
576 )]
577 pub fn all_reduce(
578 &mut self,
579 src: Handle,
580 dst: Handle,
581 dtype: ElemType,
582 device_ids: Vec<DeviceId>,
583 op: ReduceOperation,
584 ) {
585 if DeviceHandle::<R::Server>::is_blocking() {
586 panic!("Can't use `all_reduce` with a blocking device handle");
587 }
588
589 let stream_id = self.stream_id();
590 let src = src.binding();
591 let dst = dst.binding();
592
593 self.ensure_init_collective(device_ids.clone());
594
595 self.device.submit(move |server| {
596 server
597 .all_reduce(src, dst, dtype, stream_id, op, device_ids)
598 .unwrap();
599 });
600 }
601
602 #[cfg_attr(
606 feature = "tracing",
607 tracing::instrument(level = "trace", skip(self, src_descriptor, dst_server))
608 )]
609 pub fn to_client_tensor(
610 &mut self,
611 src_descriptor: CopyDescriptor,
612 dst_server: &Self,
613 dtype: ElemType,
614 ) -> Handle {
615 let stream_id_src = self.stream_id();
616 let stream_id_dst = dst_server.stream_id();
617
618 let device_id_src = self.device.device_id();
619 let device_id_dst = dst_server.device.device_id();
620
621 let mut dst_server = dst_server.clone();
622 let handle = Handle::new(stream_id_dst, src_descriptor.handle.size_in_used());
623 let handle_cloned = handle.clone();
624
625 let device_ids = vec![device_id_src, device_id_dst];
626 self.ensure_init_collective(device_ids.clone());
627 dst_server.ensure_init_collective(device_ids);
628
629 self.device.submit(move |server_src| {
630 server_src
631 .send(src_descriptor, dtype, stream_id_src, device_id_dst)
632 .unwrap()
633 });
634
635 dst_server.device.submit(move |server_dst| {
636 server_dst
637 .recv(handle_cloned, dtype, stream_id_dst, device_id_src)
638 .unwrap();
639 server_dst.sync_collective(stream_id_dst).unwrap();
640 });
641
642 self.device.flush_queue();
646 dst_server.device.flush_queue();
647
648 handle
649 }
650
651 #[track_caller]
652 #[cfg_attr(feature = "tracing", tracing::instrument(level="trace",
653 skip(self, kernel, bindings),
654 fields(
655 kernel.name = %kernel.name(),
656 kernel.id = %kernel.id(),
657 )
658 ))]
659 unsafe fn launch_inner(
660 &self,
661 kernel: <R::Server as ComputeServer>::Kernel,
662 count: CubeCount,
663 bindings: KernelArguments,
664 mode: ExecutionMode,
665 stream_id: StreamId,
666 ) {
667 let level = self.utilities.logger.profile_level();
668
669 match level {
670 None | Some(ProfileLevel::ExecutionOnly) => {
671 let utilities = self.utilities.clone();
672 self.device.submit(move |state| {
673 let name = kernel.name();
674 unsafe { state.launch(kernel, count, bindings, mode, stream_id) };
675
676 if matches!(level, Some(ProfileLevel::ExecutionOnly)) {
677 let info = type_name_format(name, TypeNameFormatLevel::Balanced);
678 utilities.logger.register_execution(info);
679 }
680 });
681 }
682 Some(level) => {
683 let name = kernel.name();
684 let kernel_id = kernel.id();
685 let context = self.device.clone();
686 let count_moved = count.clone();
687 let (result, profile) = self
688 .profile(
689 move || {
690 context
691 .submit_blocking(move |state| unsafe {
692 state.launch(kernel, count_moved, bindings, mode, stream_id)
693 })
694 .unwrap()
695 },
696 name,
697 )
698 .unwrap();
699 let info = match level {
700 ProfileLevel::Full => {
701 format!("{name}: {kernel_id} CubeCount {count:?}")
702 }
703 _ => type_name_format(name, TypeNameFormatLevel::Balanced),
704 };
705 self.utilities.logger.register_profiled(info, profile);
706 result
707 }
708 }
709 }
710
711 #[track_caller]
713 pub fn launch(
714 &self,
715 kernel: <R::Server as ComputeServer>::Kernel,
716 count: CubeCount,
717 bindings: KernelArguments,
718 ) {
719 unsafe {
721 self.launch_inner(
722 kernel,
723 count,
724 bindings,
725 ExecutionMode::Checked,
726 self.stream_id(),
727 )
728 }
729 }
730
731 #[track_caller]
739 pub unsafe fn launch_unchecked(
740 &self,
741 kernel: <R::Server as ComputeServer>::Kernel,
742 count: CubeCount,
743 bindings: KernelArguments,
744 ) {
745 unsafe {
747 self.launch_inner(
748 kernel,
749 count,
750 bindings,
751 match self.utilities.check_mode {
752 crate::config::compilation::BoundsCheckMode::Enforce => ExecutionMode::Checked,
753 crate::config::compilation::BoundsCheckMode::Validate => {
754 ExecutionMode::Validate
755 }
756 crate::config::compilation::BoundsCheckMode::Auto => ExecutionMode::Unchecked,
757 },
758 self.stream_id(),
759 )
760 }
761 }
762
763 pub fn flush(&self) -> Result<(), ServerError> {
765 let stream_id = self.stream_id();
766
767 self.device
768 .submit_blocking(move |server| server.flush(stream_id))
769 .unwrap()
770 }
771
772 pub fn sync(&self) -> DynFut<Result<(), ServerError>> {
774 let stream_id = self.stream_id();
775
776 let fut = self
777 .device
778 .submit_blocking(move |server| server.sync(stream_id))
779 .unwrap();
780
781 self.utilities.logger.profile_summary();
782
783 fut
784 }
785
786 pub fn properties(&self) -> &DeviceProperties {
788 &self.utilities.properties
789 }
790
791 pub fn features(&self) -> &Features {
793 &self.utilities.properties.features
794 }
795
796 pub fn properties_mut(&mut self) -> Option<&mut DeviceProperties> {
800 Arc::get_mut(&mut self.utilities).map(|state| &mut state.properties)
801 }
802
803 pub fn memory_usage(&self) -> Result<MemoryUsage, ServerError> {
805 let stream_id = self.stream_id();
806 self.device
807 .submit_blocking(move |server| server.memory_usage(stream_id))
808 .unwrap()
809 }
810
811 pub fn with_server<R2: Send + 'static>(
819 &self,
820 f: impl FnOnce(&mut R::Server) -> R2 + Send + 'static,
821 ) -> Option<R2> {
822 self.device.submit_blocking(f).ok()
823 }
824
825 pub fn enumerate_devices(&self, type_id: u16) -> Vec<DeviceId> {
827 R::enumerate_devices(type_id, self.info())
828 }
829
830 pub fn enumerate_all_devices(&self) -> Vec<DeviceId> {
832 R::enumerate_all_devices(self.info())
833 }
834
835 pub fn device_count(&self, type_id: u16) -> usize {
837 self.enumerate_devices(type_id).len()
838 }
839
840 pub fn device_count_total(&self) -> usize {
842 self.enumerate_all_devices().len()
843 }
844
845 pub unsafe fn allocation_mode(&self, mode: MemoryAllocationMode) {
851 let stream_id = self.stream_id();
852 self.device
853 .submit(move |server| server.allocation_mode(mode, stream_id));
854 }
855
856 pub fn memory_cleanup(&self) {
861 let stream_id = self.stream_id();
862 self.device
863 .submit(move |server| server.memory_cleanup(stream_id));
864 }
865
866 #[track_caller]
868 pub fn profile<O: Send + 'static>(
869 &self,
870 func: impl FnOnce() -> O + Send,
871 #[allow(unused)] func_name: &str,
872 ) -> Result<(O, ProfileDuration), ProfileError> {
873 #[cfg(feature = "profile-tracy")]
876 let location = std::panic::Location::caller();
877
878 #[cfg(feature = "profile-tracy")]
880 let _span = tracy_client::Client::running().unwrap().span_alloc(
881 None,
882 func_name,
883 location.file(),
884 location.line(),
885 0,
886 );
887
888 let stream_id = self.stream_id();
889
890 #[cfg(feature = "profile-tracy")]
891 let gpu_span = if self.utilities.properties.timing_method == TimingMethod::Device {
892 let gpu_span = self
893 .utilities
894 .gpu_client
895 .span_alloc(func_name, "profile", location.file(), location.line())
896 .unwrap();
897 Some(gpu_span)
898 } else {
899 None
900 };
901
902 let device = self.device.clone();
903 #[allow(unused_mut, reason = "Used in profile-tracy")]
904 let mut result = self
905 .device
906 .exclusive(move || {
907 let token =
910 match device.submit_blocking(move |server| server.start_profile(stream_id)) {
911 Ok(token) => match token {
912 Ok(token) => token,
913 Err(err) => return Err(err),
914 },
915 Err(err) => {
916 return Err(ServerError::Generic {
917 reason: alloc::format!(
918 "Can't start profiling because of a call error: {err:?}"
919 ),
920 backtrace: BackTrace::capture(),
921 });
922 }
923 };
924
925 let out = func();
927
928 let result = device
930 .submit_blocking(move |server| {
931 let mut result = server.end_profile(stream_id, token);
932
933 match result {
934 Ok(result) => Ok((out, result)),
935 Err(err) => Err(err),
936 }
937 })
938 .unwrap();
939
940 Ok(result)
941 })
942 .unwrap()
943 .map_err(|err| ProfileError::Unknown {
944 reason: alloc::format!("{err:?}"),
945 backtrace: BackTrace::capture(),
946 })?;
947
948 #[cfg(feature = "profile-tracy")]
949 if let Some(mut gpu_span) = gpu_span {
950 gpu_span.end_zone();
951 let epoch = self.utilities.epoch_time;
952 result = result.map(|(o, result)| {
954 (
955 o,
956 ProfileDuration::new(
957 alloc::boxed::Box::pin(async move {
958 let ticks = result.resolve().await;
959 let start_duration =
960 ticks.start_duration_since(epoch).as_nanos() as i64;
961 let end_duration = ticks.end_duration_since(epoch).as_nanos() as i64;
962 gpu_span.upload_timestamp_start(start_duration);
963 gpu_span.upload_timestamp_end(end_duration);
964 ticks
965 }),
966 TimingMethod::Device,
967 ),
968 )
969 });
970 }
971
972 result
973 }
974
975 #[cfg_attr(
977 feature = "tracing",
978 tracing::instrument(
979 level = "trace",
980 skip(self, src_descriptor, alloc_descriptor, dst_server)
981 )
982 )]
983 fn change_client_sync(
984 &self,
985 src_descriptor: CopyDescriptor,
986 alloc_descriptor: MemoryLayoutDescriptor,
987 dst_server: &Self,
988 ) -> MemoryLayout {
989 let shape = src_descriptor.shape.clone();
990 let elem_size = src_descriptor.elem_size;
991 let stream_id = self.stream_id();
992
993 let read = self
994 .device
995 .submit_blocking(move |server| server.read(vec![src_descriptor], stream_id))
996 .unwrap();
997
998 let mut data = cubecl_common::future::block_on(read).unwrap();
999
1000 let (handle_base, mut layouts) = self
1001 .utilities
1002 .layout_policy
1003 .apply(stream_id, &[alloc_descriptor]);
1004 let alloc = layouts.remove(0);
1005
1006 let desc_descriptor = CopyDescriptor {
1007 handle: handle_base.clone().binding(),
1008 shape,
1009 strides: alloc.strides.clone(),
1010 elem_size,
1011 };
1012
1013 let (size, memory) = (handle_base.size(), handle_base.memory);
1014 dst_server.device.submit(move |server| {
1015 server.initialize_memory(memory, size, stream_id);
1016 server.write(vec![(desc_descriptor, data.remove(0))], stream_id)
1017 });
1018
1019 alloc
1020 }
1021
1022 pub fn io_optimized_vector_sizes(
1024 &self,
1025 size: usize,
1026 ) -> impl Iterator<Item = VectorSize> + Clone {
1027 let load_width = self.properties().hardware.load_width as usize;
1028 let size_bits = size * 8;
1029 let max = load_width / size_bits;
1030 let max = usize::min(self.properties().hardware.max_vector_size, max);
1031
1032 let num_candidates = max.trailing_zeros() + 1;
1034
1035 (0..num_candidates).map(|i| 2usize.pow(i)).rev()
1036 }
1037}
1038
1039fn staged_bytes(slice: &[u8]) -> Bytes {
1048 let mut bytes = Bytes::from_elems(Vec::<u128>::new());
1051 bytes.extend_from_byte_slice(slice);
1052 bytes
1053}
1054
1055#[cfg(test)]
1056mod staged_bytes_tests {
1057 use super::staged_bytes;
1058 use cubecl_common::bytes::Bytes;
1059
1060 #[test]
1061 fn staged_bytes_copies_once_into_max_aligned_storage() {
1062 for len in [0usize, 1, 7, 16, 17, 4096, 65_537] {
1063 let src: alloc::vec::Vec<u8> = (0..len).map(|i| (i * 31 + 7) as u8).collect();
1064 let staged = staged_bytes(&src);
1065 assert_eq!(&staged[..], &src[..], "len {len}");
1066 assert_eq!(staged.len(), len);
1067 assert!(staged.align() >= core::mem::align_of::<u128>(), "len {len}");
1068 if len > 0 {
1069 assert_ne!(staged.as_ptr(), src.as_ptr(), "staging must own its copy");
1070 }
1071 }
1072 }
1073
1074 #[test]
1075 fn owned_bytes_keep_their_allocation() {
1076 let data = alloc::vec![1.0f64, 2.0, 3.0];
1079 let ptr = data.as_ptr() as *const u8;
1080 let bytes = Bytes::from_elems(data);
1081 assert_eq!(bytes.as_ptr(), ptr);
1082 }
1083}