1#![doc(
64 html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
65 html_favicon_url = "https://commonware.xyz/favicon.ico"
66)]
67#![cfg_attr(not(any(feature = "std", test)), no_std)]
68
69commonware_macros::stability_scope!(BETA {
70 use cfg_if::cfg_if;
71 use core::{cmp::Ordering, fmt, num::NonZeroUsize};
72
73 cfg_if! {
74 if #[cfg(any(feature = "std", test))] {
75 use core::convert::Infallible;
76 use futures::{
77 channel::oneshot,
78 future::{self, Either},
79 };
80 use rayon::{
81 iter::{IntoParallelIterator, ParallelIterator},
82 slice::ParallelSliceMut,
83 ThreadPool as RThreadPool, ThreadPoolBuildError, ThreadPoolBuilder, Yield,
84 };
85 use std::{
86 panic::{self, AssertUnwindSafe, Location},
87 sync::Arc,
88 };
89
90 mod policy;
91 } else {
92 extern crate alloc;
93 use alloc::vec::Vec;
94 }
95 }
96
97 #[derive(Clone, Debug)]
102 pub struct Manual<S> {
103 strategy: S,
104 parallelism: usize,
105 }
106
107 impl<S> Manual<S> {
108 pub const fn new(strategy: S, parallelism: NonZeroUsize) -> Self {
110 Self {
111 strategy,
112 parallelism: parallelism.get(),
113 }
114 }
115
116 pub const fn parallelism(&self) -> usize {
118 self.parallelism
119 }
120 }
121
122 pub trait Strategy: Clone + Send + Sync + fmt::Debug + 'static {
128 fn manual(&self) -> Manual<Self>
130 where
131 Self: Sized;
132
133 fn spawn<F, T>(&self, f: F) -> impl core::future::Future<Output = T> + Send + 'static
143 where
144 F: FnOnce(Self) -> T + Send + 'static,
145 T: Send + 'static;
146
147 #[track_caller]
149 fn run<R, SEQ, PAR>(&self, len: usize, serial: SEQ, parallel: PAR) -> R
150 where
151 R: Send,
152 SEQ: FnOnce() -> R + Send,
153 PAR: FnOnce() -> R + Send;
154
155 #[track_caller]
161 fn try_run<R, E, SEQ, PAR>(&self, len: usize, serial: SEQ, parallel: PAR) -> Result<R, E>
162 where
163 R: Send,
164 E: Send,
165 SEQ: FnOnce() -> Result<R, E> + Send,
166 PAR: FnOnce() -> Result<R, E> + Send;
167
168 #[track_caller]
209 fn fold_init<I, INIT, T, R, ID, F, RD>(
210 &self,
211 iter: I,
212 init: INIT,
213 identity: ID,
214 fold_op: F,
215 reduce_op: RD,
216 ) -> R
217 where
218 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
219 INIT: Fn() -> T + Send + Sync,
220 T: Send,
221 R: Send,
222 ID: Fn() -> R + Send + Sync,
223 F: Fn(R, &mut T, I::Item) -> R + Send + Sync,
224 RD: Fn(R, R) -> R + Send + Sync;
225
226 #[track_caller]
258 fn fold<I, R, ID, F, RD>(&self, iter: I, identity: ID, fold_op: F, reduce_op: RD) -> R
259 where
260 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
261 R: Send,
262 ID: Fn() -> R + Send + Sync,
263 F: Fn(R, I::Item) -> R + Send + Sync,
264 RD: Fn(R, R) -> R + Send + Sync,
265 {
266 self.fold_init(
267 iter,
268 || (),
269 identity,
270 |acc, _, item| fold_op(acc, item),
271 reduce_op,
272 )
273 }
274
275 #[track_caller]
291 fn try_fold<I, R, E, ID, F, RD>(
292 &self,
293 iter: I,
294 identity: ID,
295 fold_op: F,
296 reduce_op: RD,
297 ) -> Result<R, E>
298 where
299 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
300 R: Send,
301 E: Send,
302 ID: Fn() -> R + Send + Sync,
303 F: Fn(R, I::Item) -> Result<R, E> + Send + Sync,
304 RD: Fn(R, R) -> R + Send + Sync;
305
306 #[track_caller]
330 fn map_collect_vec<I, F, T>(&self, iter: I, map_op: F) -> Vec<T>
331 where
332 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
333 F: Fn(I::Item) -> T + Send + Sync,
334 T: Send,
335 {
336 self.fold(
337 iter,
338 Vec::new,
339 |mut acc, item| {
340 acc.push(map_op(item));
341 acc
342 },
343 |mut a, b| {
344 a.extend(b);
345 a
346 },
347 )
348 }
349
350 #[track_caller]
378 fn try_map_collect_vec<I, F, T, E>(&self, iter: I, map_op: F) -> Result<Vec<T>, E>
379 where
380 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
381 F: Fn(I::Item) -> Result<T, E> + Send + Sync,
382 T: Send,
383 E: Send,
384 {
385 self.try_fold(
386 iter,
387 Vec::new,
388 |mut acc, item| {
389 acc.push(map_op(item)?);
390 Ok(acc)
391 },
392 |mut a, b| {
393 a.extend(b);
394 a
395 },
396 )
397 }
398
399 #[track_caller]
433 fn map_init_collect_vec<I, INIT, T, F, R>(&self, iter: I, init: INIT, map_op: F) -> Vec<R>
434 where
435 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
436 INIT: Fn() -> T + Send + Sync,
437 T: Send,
438 F: Fn(&mut T, I::Item) -> R + Send + Sync,
439 R: Send,
440 {
441 self.fold_init(
442 iter,
443 init,
444 Vec::new,
445 |mut acc, init_val, item| {
446 acc.push(map_op(init_val, item));
447 acc
448 },
449 |mut a, b| {
450 a.extend(b);
451 a
452 },
453 )
454 }
455
456 #[track_caller]
458 fn map_init_collect_vec_with_multiplier<I, INIT, T, F, R>(
459 &self,
460 iter: I,
461 _multiplier: usize,
462 init: INIT,
463 map_op: F,
464 ) -> Vec<R>
465 where
466 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
467 INIT: Fn() -> T + Send + Sync,
468 T: Send,
469 F: Fn(&mut T, I::Item) -> R + Send + Sync,
470 R: Send,
471 {
472 self.map_init_collect_vec(iter, init, map_op)
473 }
474
475 #[track_caller]
510 fn map_partition_collect_vec<I, F, K, U>(&self, iter: I, map_op: F) -> (Vec<U>, Vec<K>)
511 where
512 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
513 F: Fn(I::Item) -> (K, Option<U>) + Send + Sync,
514 K: Send,
515 U: Send,
516 {
517 self.fold(
518 iter,
519 || (Vec::new(), Vec::new()),
520 |(mut results, mut filtered), item| {
521 let (key, value) = map_op(item);
522 match value {
523 Some(v) => results.push(v),
524 None => filtered.push(key),
525 }
526 (results, filtered)
527 },
528 |(mut r1, mut f1), (r2, f2)| {
529 r1.extend(r2);
530 f1.extend(f2);
531 (r1, f1)
532 },
533 )
534 }
535
536 fn join<A, B, RA, RB>(&self, a: A, b: B) -> (RA, RB)
562 where
563 A: FnOnce() -> RA + Send,
564 B: FnOnce() -> RB + Send,
565 RA: Send,
566 RB: Send;
567
568 #[track_caller]
581 fn sort_by<T, C>(&self, items: &mut [T], compare: C)
582 where
583 T: Send,
584 C: Fn(&T, &T) -> Ordering + Send + Sync;
585 }
586
587 impl<S: Strategy> Strategy for Manual<S> {
588 fn manual(&self) -> Manual<Self> {
589 Manual {
590 strategy: self.clone(),
591 parallelism: self.parallelism,
592 }
593 }
594
595 fn spawn<F, T>(&self, f: F) -> impl core::future::Future<Output = T> + Send + 'static
596 where
597 F: FnOnce(Self) -> T + Send + 'static,
598 T: Send + 'static,
599 {
600 let s = self.clone();
601 self.strategy.spawn(|_| f(s))
602 }
603
604 #[track_caller]
605 fn run<R, SEQ, PAR>(
606 &self,
607 len: usize,
608 serial: SEQ,
609 parallel: PAR,
610 ) -> R
611 where
612 R: Send,
613 SEQ: FnOnce() -> R + Send,
614 PAR: FnOnce() -> R + Send,
615 {
616 self.strategy.run(len, serial, parallel)
617 }
618
619 #[track_caller]
620 fn try_run<R, E, SEQ, PAR>(
621 &self,
622 len: usize,
623 serial: SEQ,
624 parallel: PAR,
625 ) -> Result<R, E>
626 where
627 R: Send,
628 E: Send,
629 SEQ: FnOnce() -> Result<R, E> + Send,
630 PAR: FnOnce() -> Result<R, E> + Send,
631 {
632 self.strategy.try_run(len, serial, parallel)
633 }
634
635 #[track_caller]
636 fn fold_init<I, INIT, T, R, ID, F, RD>(
637 &self,
638 iter: I,
639 init: INIT,
640 identity: ID,
641 fold_op: F,
642 reduce_op: RD,
643 ) -> R
644 where
645 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
646 INIT: Fn() -> T + Send + Sync,
647 T: Send,
648 R: Send,
649 ID: Fn() -> R + Send + Sync,
650 F: Fn(R, &mut T, I::Item) -> R + Send + Sync,
651 RD: Fn(R, R) -> R + Send + Sync,
652 {
653 self.strategy
654 .fold_init(iter, init, identity, fold_op, reduce_op)
655 }
656
657 #[track_caller]
658 fn try_fold<I, R, E, ID, F, RD>(
659 &self,
660 iter: I,
661 identity: ID,
662 fold_op: F,
663 reduce_op: RD,
664 ) -> Result<R, E>
665 where
666 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
667 R: Send,
668 E: Send,
669 ID: Fn() -> R + Send + Sync,
670 F: Fn(R, I::Item) -> Result<R, E> + Send + Sync,
671 RD: Fn(R, R) -> R + Send + Sync,
672 {
673 self.strategy.try_fold(iter, identity, fold_op, reduce_op)
674 }
675
676 #[track_caller]
677 fn map_collect_vec<I, F, T>(&self, iter: I, map_op: F) -> Vec<T>
678 where
679 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
680 F: Fn(I::Item) -> T + Send + Sync,
681 T: Send,
682 {
683 self.strategy.map_collect_vec(iter, map_op)
684 }
685
686 #[track_caller]
687 fn try_map_collect_vec<I, F, T, E>(&self, iter: I, map_op: F) -> Result<Vec<T>, E>
688 where
689 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
690 F: Fn(I::Item) -> Result<T, E> + Send + Sync,
691 T: Send,
692 E: Send,
693 {
694 self.strategy.try_map_collect_vec(iter, map_op)
695 }
696
697 #[track_caller]
698 fn map_init_collect_vec<I, INIT, T, F, R>(&self, iter: I, init: INIT, map_op: F) -> Vec<R>
699 where
700 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
701 INIT: Fn() -> T + Send + Sync,
702 T: Send,
703 F: Fn(&mut T, I::Item) -> R + Send + Sync,
704 R: Send,
705 {
706 self.strategy.map_init_collect_vec(iter, init, map_op)
707 }
708
709 #[track_caller]
710 fn map_init_collect_vec_with_multiplier<I, INIT, T, F, R>(
711 &self,
712 iter: I,
713 multiplier: usize,
714 init: INIT,
715 map_op: F,
716 ) -> Vec<R>
717 where
718 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
719 INIT: Fn() -> T + Send + Sync,
720 T: Send,
721 F: Fn(&mut T, I::Item) -> R + Send + Sync,
722 R: Send,
723 {
724 self.strategy
725 .map_init_collect_vec_with_multiplier(iter, multiplier, init, map_op)
726 }
727
728 #[track_caller]
729 fn map_partition_collect_vec<I, F, K, U>(&self, iter: I, map_op: F) -> (Vec<U>, Vec<K>)
730 where
731 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
732 F: Fn(I::Item) -> (K, Option<U>) + Send + Sync,
733 K: Send,
734 U: Send,
735 {
736 self.strategy.map_partition_collect_vec(iter, map_op)
737 }
738
739 fn join<A, B, RA, RB>(&self, a: A, b: B) -> (RA, RB)
740 where
741 A: FnOnce() -> RA + Send,
742 B: FnOnce() -> RB + Send,
743 RA: Send,
744 RB: Send,
745 {
746 self.strategy.join(a, b)
747 }
748
749 #[track_caller]
750 fn sort_by<T, C>(&self, items: &mut [T], compare: C)
751 where
752 T: Send,
753 C: Fn(&T, &T) -> Ordering + Send + Sync,
754 {
755 self.strategy.sort_by(items, compare)
756 }
757 }
758
759 #[derive(Default, Debug, Clone)]
781 pub struct Sequential;
782
783 impl Strategy for Sequential {
784 fn manual(&self) -> Manual<Self> {
785 Manual::new(Self, NonZeroUsize::new(1).unwrap())
786 }
787
788 fn spawn<F, T>(&self, f: F) -> impl core::future::Future<Output = T> + Send + 'static
789 where
790 F: FnOnce(Self) -> T + Send + 'static,
791 T: Send + 'static,
792 {
793 let result = f(self.clone());
794 async move { result }
795 }
796
797 fn run<R, SEQ, PAR>(&self, _len: usize, serial: SEQ, _parallel: PAR) -> R
798 where
799 R: Send,
800 SEQ: FnOnce() -> R + Send,
801 PAR: FnOnce() -> R + Send,
802 {
803 serial()
804 }
805
806 fn try_run<R, E, SEQ, PAR>(&self, _len: usize, serial: SEQ, _parallel: PAR) -> Result<R, E>
807 where
808 R: Send,
809 E: Send,
810 SEQ: FnOnce() -> Result<R, E> + Send,
811 PAR: FnOnce() -> Result<R, E> + Send,
812 {
813 serial()
814 }
815
816 fn fold_init<I, INIT, T, R, ID, F, RD>(
817 &self,
818 iter: I,
819 init: INIT,
820 identity: ID,
821 fold_op: F,
822 _reduce_op: RD,
823 ) -> R
824 where
825 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
826 INIT: Fn() -> T + Send + Sync,
827 T: Send,
828 R: Send,
829 ID: Fn() -> R + Send + Sync,
830 F: Fn(R, &mut T, I::Item) -> R + Send + Sync,
831 RD: Fn(R, R) -> R + Send + Sync,
832 {
833 let mut init_val = init();
834 iter.into_iter()
835 .fold(identity(), |acc, item| fold_op(acc, &mut init_val, item))
836 }
837
838 fn try_fold<I, R, E, ID, F, RD>(
839 &self,
840 iter: I,
841 identity: ID,
842 fold_op: F,
843 _reduce_op: RD,
844 ) -> Result<R, E>
845 where
846 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
847 R: Send,
848 E: Send,
849 ID: Fn() -> R + Send + Sync,
850 F: Fn(R, I::Item) -> Result<R, E> + Send + Sync,
851 RD: Fn(R, R) -> R + Send + Sync,
852 {
853 iter.into_iter().try_fold(identity(), fold_op)
854 }
855
856 fn join<A, B, RA, RB>(&self, a: A, b: B) -> (RA, RB)
857 where
858 A: FnOnce() -> RA + Send,
859 B: FnOnce() -> RB + Send,
860 RA: Send,
861 RB: Send,
862 {
863 (a(), b())
864 }
865
866 fn sort_by<T, C>(&self, items: &mut [T], compare: C)
867 where
868 T: Send,
869 C: Fn(&T, &T) -> Ordering + Send + Sync,
870 {
871 items.sort_by(compare);
872 }
873
874 }
875});
876commonware_macros::stability_scope!(BETA, cfg(any(feature = "std", test)) {
877 pub type ThreadPool = Arc<RThreadPool>;
879
880 #[derive(Debug, Clone)]
920 pub struct Rayon {
921 thread_pool: ThreadPool,
922 parallelism: usize,
925 policy: Option<policy::Policy>,
928 }
929
930 impl Rayon {
931 pub fn new(num_threads: NonZeroUsize) -> Result<Self, ThreadPoolBuildError> {
934 ThreadPoolBuilder::new()
935 .num_threads(num_threads.get())
936 .build()
937 .map(|pool| Self::with_pool(Arc::new(pool)))
938 }
939
940 pub fn with_pool(thread_pool: ThreadPool) -> Self {
942 let parallelism = thread_pool.current_num_threads().max(1);
943 Self {
944 thread_pool,
945 parallelism,
946 policy: Some(policy::Policy::default()),
947 }
948 }
949
950 pub const fn with_parallelism(mut self, parallelism: NonZeroUsize) -> Self {
956 self.parallelism = parallelism.get();
957 self
958 }
959
960 #[track_caller]
961 fn execute<R>(
962 &self,
963 len: usize,
964 multiplier: usize,
965 run: impl FnOnce(policy::Execution) -> R,
966 ) -> R {
967 match self.try_execute(len, multiplier, |execution| {
968 Ok::<_, Infallible>(run(execution))
969 }) {
970 Ok(result) => result,
971 Err(e) => match e {},
972 }
973 }
974
975 #[track_caller]
976 fn try_execute<R, E>(
977 &self,
978 len: usize,
979 multiplier: usize,
980 run: impl FnOnce(policy::Execution) -> Result<R, E>,
981 ) -> Result<R, E> {
982 let Some(policy) = &self.policy else {
983 let execution = if self.parallelism <= 1 {
984 policy::Execution::Serial
985 } else {
986 policy::Execution::Parallel
987 };
988 return run(execution);
989 };
990
991 let work = len.saturating_mul(multiplier);
992 policy.try_run(Location::caller(), len, work, self.parallelism, run)
993 }
994 }
995
996 impl Strategy for Rayon {
997 fn manual(&self) -> Manual<Self> {
998 Manual {
999 strategy: Self {
1000 thread_pool: self.thread_pool.clone(),
1001 parallelism: self.parallelism,
1002 policy: None,
1003 },
1004 parallelism: self.parallelism,
1005 }
1006 }
1007
1008 fn spawn<F, T>(&self, f: F) -> impl core::future::Future<Output = T> + Send + 'static
1009 where
1010 F: FnOnce(Self) -> T + Send + 'static,
1011 T: Send + 'static,
1012 {
1013 if self.thread_pool.current_num_threads() <= 1 {
1014 return Either::Left(future::ready(f(self.clone())));
1015 }
1016
1017 let (tx, mut rx) = oneshot::channel();
1018 let s = self.clone();
1019 let pool = self.thread_pool.clone();
1020 self.thread_pool.spawn(move || {
1021 let result = panic::catch_unwind(AssertUnwindSafe(|| f(s)));
1024 let _ = tx.send(result);
1025 });
1026 Either::Right(async move {
1027 loop {
1033 if let Ok(Some(result)) = rx.try_recv() {
1034 return match result {
1035 Ok(value) => value,
1036 Err(payload) => panic::resume_unwind(payload),
1037 };
1038 }
1039 if !matches!(pool.yield_now(), Some(Yield::Executed)) {
1040 break;
1041 }
1042 }
1043 match rx.await {
1044 Ok(Ok(value)) => value,
1045 Ok(Err(payload)) => panic::resume_unwind(payload),
1046 Err(_) => panic!("strategy job dropped before completion"),
1047 }
1048 })
1049 }
1050
1051 #[track_caller]
1052 fn run<R, SEQ, PAR>(
1053 &self,
1054 len: usize,
1055 serial: SEQ,
1056 parallel: PAR,
1057 ) -> R
1058 where
1059 R: Send,
1060 SEQ: FnOnce() -> R + Send,
1061 PAR: FnOnce() -> R + Send,
1062 {
1063 self.execute(len, 1, |execution| match execution {
1064 policy::Execution::Serial => serial(),
1065 policy::Execution::Parallel => parallel(),
1066 })
1067 }
1068
1069 #[track_caller]
1070 fn try_run<R, E, SEQ, PAR>(
1071 &self,
1072 len: usize,
1073 serial: SEQ,
1074 parallel: PAR,
1075 ) -> Result<R, E>
1076 where
1077 R: Send,
1078 E: Send,
1079 SEQ: FnOnce() -> Result<R, E> + Send,
1080 PAR: FnOnce() -> Result<R, E> + Send,
1081 {
1082 self.try_execute(len, 1, |execution| match execution {
1083 policy::Execution::Serial => serial(),
1084 policy::Execution::Parallel => parallel(),
1085 })
1086 }
1087
1088 #[track_caller]
1089 fn fold_init<I, INIT, T, R, ID, F, RD>(
1090 &self,
1091 iter: I,
1092 init: INIT,
1093 identity: ID,
1094 fold_op: F,
1095 reduce_op: RD,
1096 ) -> R
1097 where
1098 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
1099 INIT: Fn() -> T + Send + Sync,
1100 T: Send,
1101 R: Send,
1102 ID: Fn() -> R + Send + Sync,
1103 F: Fn(R, &mut T, I::Item) -> R + Send + Sync,
1104 RD: Fn(R, R) -> R + Send + Sync,
1105 {
1106 let items: Vec<I::Item> = iter.into_iter().collect();
1107 self.execute(items.len(), 1, |execution| match execution {
1108 policy::Execution::Serial => Sequential.fold_init(items, init, identity, fold_op, reduce_op),
1109 policy::Execution::Parallel => self.thread_pool.install(|| {
1110 items
1111 .into_par_iter()
1112 .fold(
1113 || (init(), identity()),
1114 |(mut init_val, acc), item| {
1115 let new_acc = fold_op(acc, &mut init_val, item);
1116 (init_val, new_acc)
1117 },
1118 )
1119 .map(|(_, acc)| acc)
1120 .reduce(&identity, reduce_op)
1121 }),
1122 })
1123 }
1124
1125 #[track_caller]
1126 fn map_collect_vec<I, F, T>(&self, iter: I, map_op: F) -> Vec<T>
1127 where
1128 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
1129 F: Fn(I::Item) -> T + Send + Sync,
1130 T: Send,
1131 {
1132 let items: Vec<I::Item> = iter.into_iter().collect();
1133 self.execute(
1134 items.len(),
1135 1,
1136 |execution| {
1137 match execution {
1138 policy::Execution::Serial => Sequential.map_collect_vec(items, map_op),
1139 policy::Execution::Parallel => self
1140 .thread_pool
1141 .install(|| items.into_par_iter().map(map_op).collect()),
1142 }
1143 },
1144 )
1145 }
1146
1147 #[track_caller]
1148 fn try_map_collect_vec<I, F, T, E>(&self, iter: I, map_op: F) -> Result<Vec<T>, E>
1149 where
1150 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
1151 F: Fn(I::Item) -> Result<T, E> + Send + Sync,
1152 T: Send,
1153 E: Send,
1154 {
1155 let items: Vec<I::Item> = iter.into_iter().collect();
1156 self.try_execute(
1157 items.len(),
1158 1,
1159 |execution| {
1160 match execution {
1161 policy::Execution::Serial => Sequential.try_map_collect_vec(items, map_op),
1162 policy::Execution::Parallel => self
1163 .thread_pool
1164 .install(|| items.into_par_iter().map(map_op).collect()),
1165 }
1166 },
1167 )
1168 }
1169
1170 #[track_caller]
1171 fn map_init_collect_vec<I, INIT, T, F, R>(&self, iter: I, init: INIT, map_op: F) -> Vec<R>
1172 where
1173 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
1174 INIT: Fn() -> T + Send + Sync,
1175 T: Send,
1176 F: Fn(&mut T, I::Item) -> R + Send + Sync,
1177 R: Send,
1178 {
1179 let items: Vec<I::Item> = iter.into_iter().collect();
1180 self.execute(
1181 items.len(),
1182 1,
1183 |execution| {
1184 match execution {
1185 policy::Execution::Serial => Sequential.map_init_collect_vec(items, init, map_op),
1186 policy::Execution::Parallel => self
1187 .thread_pool
1188 .install(|| items.into_par_iter().map_init(init, map_op).collect()),
1189 }
1190 },
1191 )
1192 }
1193
1194 #[track_caller]
1195 fn map_init_collect_vec_with_multiplier<I, INIT, T, F, R>(
1196 &self,
1197 iter: I,
1198 multiplier: usize,
1199 init: INIT,
1200 map_op: F,
1201 ) -> Vec<R>
1202 where
1203 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
1204 INIT: Fn() -> T + Send + Sync,
1205 T: Send,
1206 F: Fn(&mut T, I::Item) -> R + Send + Sync,
1207 R: Send,
1208 {
1209 let items: Vec<I::Item> = iter.into_iter().collect();
1210 self.execute(
1211 items.len(),
1212 multiplier,
1213 |execution| {
1214 match execution {
1215 policy::Execution::Serial => {
1216 Sequential.map_init_collect_vec(items, init, map_op)
1217 }
1218 policy::Execution::Parallel => self
1219 .thread_pool
1220 .install(|| items.into_par_iter().map_init(init, map_op).collect()),
1221 }
1222 },
1223 )
1224 }
1225
1226 #[track_caller]
1227 fn try_fold<I, R, E, ID, F, RD>(
1228 &self,
1229 iter: I,
1230 identity: ID,
1231 fold_op: F,
1232 reduce_op: RD,
1233 ) -> Result<R, E>
1234 where
1235 I: IntoIterator<IntoIter: Send, Item: Send> + Send,
1236 R: Send,
1237 E: Send,
1238 ID: Fn() -> R + Send + Sync,
1239 F: Fn(R, I::Item) -> Result<R, E> + Send + Sync,
1240 RD: Fn(R, R) -> R + Send + Sync,
1241 {
1242 let items: Vec<I::Item> = iter.into_iter().collect();
1243 self.try_execute(items.len(), 1, |execution| match execution {
1244 policy::Execution::Serial => Sequential.try_fold(items, identity, fold_op, reduce_op),
1245 policy::Execution::Parallel => self.thread_pool.install(|| {
1246 items
1247 .into_par_iter()
1248 .try_fold(&identity, &fold_op)
1249 .try_reduce(&identity, |a, b| Ok(reduce_op(a, b)))
1250 }),
1251 })
1252 }
1253
1254 fn join<A, B, RA, RB>(&self, a: A, b: B) -> (RA, RB)
1255 where
1256 A: FnOnce() -> RA + Send,
1257 B: FnOnce() -> RB + Send,
1258 RA: Send,
1259 RB: Send,
1260 {
1261 self.thread_pool.install(|| rayon::join(a, b))
1262 }
1263
1264 #[track_caller]
1265 fn sort_by<T, C>(&self, items: &mut [T], compare: C)
1266 where
1267 T: Send,
1268 C: Fn(&T, &T) -> Ordering + Send + Sync,
1269 {
1270 self.execute(
1271 items.len(),
1272 1,
1273 |execution| match execution {
1274 policy::Execution::Serial => Sequential.sort_by(items, compare),
1275 policy::Execution::Parallel => self.thread_pool.install(|| items.par_sort_by(compare)),
1276 },
1277 );
1278 }
1279
1280 }
1281});
1282
1283#[cfg(test)]
1284mod test {
1285 use crate::{Rayon, Sequential, Strategy};
1286 use core::num::NonZeroUsize;
1287 use futures::FutureExt;
1288 use proptest::prelude::*;
1289 use rayon::ThreadPoolBuilder;
1290 use std::sync::{
1291 atomic::{AtomicUsize, Ordering},
1292 Arc,
1293 };
1294
1295 fn parallel_strategy() -> Rayon {
1296 Rayon::new(NonZeroUsize::new(4).unwrap()).unwrap()
1297 }
1298
1299 fn policy_len(strategy: &Rayon) -> usize {
1300 strategy.policy.as_ref().map_or(0, |policy| policy.len())
1301 }
1302
1303 fn map_from_same_callsite(strategy: &Rayon, len: usize) {
1304 let _: Vec<_> = strategy.map_collect_vec(0..len, |x| x);
1305 }
1306
1307 fn map_init_with_multiplier_from_same_callsite(
1308 strategy: &Rayon,
1309 len: usize,
1310 multiplier: usize,
1311 ) {
1312 let _: Vec<_> =
1313 strategy.map_init_collect_vec_with_multiplier(0..len, multiplier, || (), |_, x| x);
1314 }
1315
1316 fn run_from_same_callsite(strategy: &Rayon, len: usize) {
1317 let _: usize = strategy.run(len, || 1, || 2);
1318 }
1319
1320 fn map_partition_from_same_callsite(strategy: &Rayon, len: usize) {
1321 let _: (Vec<_>, Vec<_>) = strategy.map_partition_collect_vec(0..len, |x| {
1322 if x % 2 == 0 {
1323 (x, Some(x))
1324 } else {
1325 (x, None)
1326 }
1327 });
1328 }
1329
1330 #[test]
1331 fn adaptive_policy_is_scoped_to_rayon() {
1332 let strategy = parallel_strategy();
1333 let other = parallel_strategy();
1334
1335 let _: Vec<_> = strategy.map_collect_vec(0..16, |x| x);
1336
1337 assert_eq!(policy_len(&strategy), 1);
1338 assert_eq!(policy_len(&other), 0);
1339 }
1340
1341 #[test]
1346 fn spawn_driven_inline_on_member_thread() {
1347 let pool = ThreadPoolBuilder::new()
1348 .num_threads(2)
1349 .use_current_thread()
1350 .spawn_handler(|_| Ok(()))
1351 .build()
1352 .unwrap();
1353 let strategy = Rayon::with_pool(Arc::new(pool));
1354
1355 let result = strategy
1356 .spawn(|strategy| strategy.map_collect_vec(0..2, |i| i + 1))
1357 .now_or_never()
1358 .expect("spawn should complete on first poll via the yield loop");
1359 assert_eq!(result, vec![1, 2]);
1360 }
1361
1362 #[test]
1363 fn with_parallelism_overrides_planning_parallelism() {
1364 let strategy = Rayon::new(NonZeroUsize::new(1).unwrap())
1365 .unwrap()
1366 .with_parallelism(NonZeroUsize::new(4).unwrap());
1367 let strategy = strategy.manual();
1368 assert_eq!(strategy.parallelism(), 4);
1369 assert_eq!(strategy.run(2, || "serial", || "parallel"), "parallel");
1370 }
1371
1372 #[test]
1373 fn adaptive_policy_is_shared_by_clones() {
1374 let strategy = parallel_strategy();
1375 let clone = strategy.clone();
1376
1377 let _: Vec<_> = clone.map_collect_vec(0..16, |x| x);
1378
1379 assert_eq!(policy_len(&strategy), 1);
1380 assert_eq!(policy_len(&clone), 1);
1381 }
1382
1383 #[test]
1384 fn adaptive_policy_records_all_adaptive_operations() {
1385 let strategy = parallel_strategy();
1386
1387 let _: Vec<_> = strategy.fold_init(
1388 0..16,
1389 || (),
1390 Vec::new,
1391 |mut acc, _, x| {
1392 acc.push(x);
1393 acc
1394 },
1395 |mut a, b| {
1396 a.extend(b);
1397 a
1398 },
1399 );
1400 let _: i32 = strategy.fold(0..16, || 0, |acc, x| acc + x, |a, b| a + b);
1401 let _: Result<i32, ()> = strategy.try_fold(0..16, || 0, |acc, x| Ok(acc + x), |a, b| a + b);
1402 let _: Vec<_> = strategy.map_collect_vec(0..16, |x| x);
1403 let _: Result<Vec<_>, ()> = strategy.try_map_collect_vec(0..16, Ok);
1404 let _: Vec<_> = strategy.map_init_collect_vec(
1405 0..16,
1406 || AtomicUsize::new(0),
1407 |counter, x| {
1408 counter.fetch_add(1, Ordering::Relaxed);
1409 x
1410 },
1411 );
1412 let _: Vec<_> = strategy.map_init_collect_vec_with_multiplier(
1413 0..16,
1414 2,
1415 || AtomicUsize::new(0),
1416 |counter, x| {
1417 counter.fetch_add(1, Ordering::Relaxed);
1418 x
1419 },
1420 );
1421 let _: usize = strategy.run(16, || 1, || 2);
1422 let _: (Vec<_>, Vec<_>) = strategy.map_partition_collect_vec(0..16, |x| {
1423 if x % 2 == 0 {
1424 (x, Some(x))
1425 } else {
1426 (x, None)
1427 }
1428 });
1429 let _: (i32, i32) = strategy.join(|| 1, || 2);
1430 let mut sortable = vec![3, 2, 1];
1431 strategy.sort_by(&mut sortable, |a, b| a.cmp(b));
1432
1433 assert_eq!(sortable, vec![1, 2, 3]);
1434 assert_eq!(policy_len(&strategy), 10);
1435 }
1436
1437 #[test]
1438 fn adaptive_policy_buckets_by_input_size() {
1439 let strategy = parallel_strategy();
1440
1441 map_from_same_callsite(&strategy, 1);
1442 map_from_same_callsite(&strategy, 2);
1443 map_from_same_callsite(&strategy, 3);
1444
1445 assert_eq!(policy_len(&strategy), 2);
1446 }
1447
1448 #[test]
1449 fn adaptive_policy_buckets_by_work_multiplier() {
1450 let strategy = parallel_strategy();
1451
1452 map_init_with_multiplier_from_same_callsite(&strategy, 16, 1);
1453 map_init_with_multiplier_from_same_callsite(&strategy, 16, 2);
1454 map_init_with_multiplier_from_same_callsite(&strategy, 16, 3);
1455
1456 assert_eq!(policy_len(&strategy), 2);
1457 }
1458
1459 #[test]
1460 fn adaptive_run_buckets_by_input_size() {
1461 let strategy = parallel_strategy();
1462
1463 run_from_same_callsite(&strategy, 1);
1464 run_from_same_callsite(&strategy, 2);
1465 run_from_same_callsite(&strategy, 3);
1466
1467 assert_eq!(policy_len(&strategy), 2);
1468 }
1469
1470 #[test]
1471 fn manual_strategy_does_not_use_adaptive_policy() {
1472 let strategy = parallel_strategy();
1473 let manual = strategy.manual();
1474
1475 let _: usize = manual.fold(0..4, || 0, |acc, x| acc + x, |a, b| a + b);
1476 assert_eq!(manual.run(4, || 1, || 2), 2);
1477
1478 assert_eq!(policy_len(&strategy), 0);
1479 assert_eq!(policy_len(&manual.strategy), 0);
1480 }
1481
1482 #[test]
1483 fn sequential_run_uses_serial_body() {
1484 assert_eq!(Sequential.run(4, || 1, || 2), 1);
1485 }
1486
1487 #[test]
1488 fn adaptive_policy_keys_default_methods_by_external_callsite() {
1489 let strategy = parallel_strategy();
1490
1491 let _: i32 = strategy.fold(0..16, || 0, |acc, x| acc + x, |a, b| a + b);
1495 let _: i32 = strategy.fold(0..16, || 0, |acc, x| acc + x, |a, b| a + b);
1496
1497 assert_eq!(policy_len(&strategy), 2);
1498 }
1499
1500 #[test]
1501 fn adaptive_policy_keys_partition_map_by_external_callsite() {
1502 let strategy = parallel_strategy();
1503
1504 map_partition_from_same_callsite(&strategy, 16);
1505 let _: (Vec<_>, Vec<_>) = strategy.map_partition_collect_vec(0..16, |x| {
1506 if x % 2 == 0 {
1507 (x, Some(x))
1508 } else {
1509 (x, None)
1510 }
1511 });
1512
1513 assert_eq!(policy_len(&strategy), 2);
1514 }
1515
1516 #[test]
1517 fn join_does_not_use_adaptive_policy() {
1518 let strategy = parallel_strategy();
1519
1520 let result = strategy.join(|| 1, || 2);
1521
1522 assert_eq!(result, (1, 2));
1523 assert_eq!(policy_len(&strategy), 0);
1524 }
1525
1526 #[test]
1527 fn sequential_spawn_runs_job() {
1528 let result = futures::executor::block_on(Sequential.spawn(|_| 7));
1529
1530 assert_eq!(result, 7);
1531 }
1532
1533 #[test]
1534 fn rayon_spawn_runs_job_on_pool() {
1535 let strategy = parallel_strategy();
1536
1537 let result = futures::executor::block_on(strategy.spawn(|_| {
1538 assert!(rayon::current_thread_index().is_some());
1539 7
1540 }));
1541
1542 assert_eq!(result, 7);
1543 assert_eq!(policy_len(&strategy), 0);
1544 }
1545
1546 #[test]
1547 fn rayon_spawn_runs_inline_on_current_thread_single_worker_pool() {
1548 let pool = ThreadPoolBuilder::new()
1549 .num_threads(1)
1550 .use_current_thread()
1551 .build()
1552 .unwrap();
1553 let strategy =
1554 Rayon::with_pool(Arc::new(pool)).with_parallelism(NonZeroUsize::new(4).unwrap());
1555
1556 assert_eq!(strategy.manual().parallelism(), 4);
1557
1558 let result = strategy.spawn(|_| 7).now_or_never();
1559
1560 assert_eq!(result, Some(7));
1561 assert_eq!(policy_len(&strategy), 0);
1562 }
1563
1564 #[test]
1565 #[should_panic(expected = "boom")]
1566 fn rayon_spawn_propagates_job_panic() {
1567 let strategy = parallel_strategy();
1569
1570 let _: () = futures::executor::block_on(strategy.spawn(|_| panic!("boom")));
1571 }
1572
1573 #[test]
1574 #[should_panic(expected = "boom")]
1575 fn sequential_spawn_propagates_job_panic() {
1576 let _: () = futures::executor::block_on(Sequential.spawn(|_| panic!("boom")));
1577 }
1578
1579 proptest! {
1580 #[test]
1581 fn parallel_fold_init_matches_sequential(data in prop::collection::vec(any::<i32>(), 0..500)) {
1582 let sequential = Sequential;
1583 let parallel = parallel_strategy();
1584
1585 let seq_result: Vec<i32> = sequential.fold_init(
1586 &data,
1587 || (),
1588 Vec::new,
1589 |mut acc, _, &x| { acc.push(x.wrapping_mul(2)); acc },
1590 |mut a, b| { a.extend(b); a },
1591 );
1592
1593 let par_result: Vec<i32> = parallel.fold_init(
1594 &data,
1595 || (),
1596 Vec::new,
1597 |mut acc, _, &x| { acc.push(x.wrapping_mul(2)); acc },
1598 |mut a, b| { a.extend(b); a },
1599 );
1600
1601 prop_assert_eq!(seq_result, par_result);
1602 }
1603
1604 #[test]
1605 fn fold_equals_fold_init(data in prop::collection::vec(any::<i32>(), 0..500)) {
1606 let s = Sequential;
1607
1608 let via_fold: Vec<i32> = s.fold(
1609 &data,
1610 Vec::new,
1611 |mut acc, &x| { acc.push(x); acc },
1612 |mut a, b| { a.extend(b); a },
1613 );
1614
1615 let via_fold_init: Vec<i32> = s.fold_init(
1616 &data,
1617 || (),
1618 Vec::new,
1619 |mut acc, _, &x| { acc.push(x); acc },
1620 |mut a, b| { a.extend(b); a },
1621 );
1622
1623 prop_assert_eq!(via_fold, via_fold_init);
1624 }
1625
1626 #[test]
1627 fn parallel_try_fold_matches_sequential(data in prop::collection::vec(any::<i32>(), 0..500)) {
1628 let sequential: Result<i32, ()> = Sequential.try_fold(
1629 &data,
1630 || 0i32,
1631 |acc, &x| Ok(acc.wrapping_add(x)),
1632 |a, b| a.wrapping_add(b),
1633 );
1634 let parallel: Result<i32, ()> = parallel_strategy().try_fold(
1635 &data,
1636 || 0i32,
1637 |acc, &x| Ok(acc.wrapping_add(x)),
1638 |a, b| a.wrapping_add(b),
1639 );
1640
1641 prop_assert_eq!(sequential, parallel);
1642 }
1643
1644 #[test]
1645 fn map_collect_vec_equals_fold(data in prop::collection::vec(any::<i32>(), 0..500)) {
1646 let s = Sequential;
1647 let map_op = |&x: &i32| x.wrapping_mul(3);
1648
1649 let via_map: Vec<i32> = s.map_collect_vec(&data, map_op);
1650
1651 let via_fold: Vec<i32> = s.fold(
1652 &data,
1653 Vec::new,
1654 |mut acc, item| { acc.push(map_op(item)); acc },
1655 |mut a, b| { a.extend(b); a },
1656 );
1657
1658 prop_assert_eq!(via_map, via_fold);
1659 }
1660
1661 #[test]
1662 fn try_map_collect_vec_collects_successes(data in prop::collection::vec(any::<i32>(), 0..500)) {
1663 let expected: Vec<i32> = data.iter().map(|x| x.wrapping_mul(5)).collect();
1664
1665 let sequential: Result<Vec<i32>, ()> =
1666 Sequential.try_map_collect_vec(&data, |&x| Ok(x.wrapping_mul(5)));
1667 prop_assert_eq!(sequential, Ok(expected.clone()));
1668
1669 let parallel: Result<Vec<i32>, ()> =
1670 parallel_strategy().try_map_collect_vec(&data, |&x| Ok(x.wrapping_mul(5)));
1671 prop_assert_eq!(parallel, Ok(expected));
1672 }
1673
1674 #[test]
1675 fn try_map_collect_vec_returns_first_error(data in prop::collection::vec(any::<i32>(), 0..500)) {
1676 let expected_error = data.iter().position(|x| x % 7 == 0);
1677 let result: Result<Vec<i32>, usize> =
1678 Sequential.try_map_collect_vec(data.iter().enumerate(), |(i, &x)| {
1679 if x % 7 == 0 {
1680 Err(i)
1681 } else {
1682 Ok(x)
1683 }
1684 });
1685
1686 match expected_error {
1687 Some(i) => prop_assert_eq!(result, Err(i)),
1688 None => prop_assert_eq!(result, Ok(data)),
1689 }
1690 }
1691
1692 #[test]
1693 fn map_init_collect_vec_equals_fold_init(data in prop::collection::vec(any::<i32>(), 0..500)) {
1694 let s = Sequential;
1695
1696 let via_map: Vec<i32> = s.map_init_collect_vec(
1697 &data,
1698 || 0i32,
1699 |counter, &x| { *counter += 1; x.wrapping_add(*counter) },
1700 );
1701
1702 let via_fold_init: Vec<i32> = s.fold_init(
1703 &data,
1704 || 0i32,
1705 Vec::new,
1706 |mut acc, counter, &x| {
1707 *counter += 1;
1708 acc.push(x.wrapping_add(*counter));
1709 acc
1710 },
1711 |mut a, b| { a.extend(b); a },
1712 );
1713
1714 prop_assert_eq!(via_map, via_fold_init);
1715 }
1716
1717 #[test]
1718 fn map_partition_collect_vec_returns_valid_results(data in prop::collection::vec(any::<i32>(), 0..500)) {
1719 let s = Sequential;
1720
1721 let map_op = |&x: &i32| {
1722 let value = if x % 2 == 0 { Some(x.wrapping_mul(2)) } else { None };
1723 (x, value)
1724 };
1725
1726 let (results, filtered) = s.map_partition_collect_vec(data.iter(), map_op);
1727
1728 let expected_results: Vec<i32> = data.iter().filter(|&&x| x % 2 == 0).map(|&x| x.wrapping_mul(2)).collect();
1730 prop_assert_eq!(results, expected_results);
1731
1732 let expected_filtered: Vec<i32> = data.iter().filter(|&&x| x % 2 != 0).copied().collect();
1734 prop_assert_eq!(filtered, expected_filtered);
1735 }
1736 }
1737
1738 #[test]
1739 fn try_map_collect_vec_sequential_short_circuits() {
1740 let calls = AtomicUsize::new(0);
1741 let result: Result<Vec<usize>, usize> = Sequential.try_map_collect_vec(0..10, |i| {
1742 calls.fetch_add(1, Ordering::Relaxed);
1743 if i == 3 {
1744 Err(i)
1745 } else {
1746 Ok(i)
1747 }
1748 });
1749
1750 assert_eq!(result, Err(3));
1751 assert_eq!(calls.load(Ordering::Relaxed), 4);
1752 }
1753
1754 #[test]
1755 fn try_map_collect_vec_parallel_returns_an_error() {
1756 let result: Result<Vec<usize>, usize> =
1757 parallel_strategy().try_map_collect_vec(0..128, |i| {
1758 if i == 17 || i == 42 {
1759 Err(i)
1760 } else {
1761 Ok(i)
1762 }
1763 });
1764
1765 assert!(matches!(result, Err(17 | 42)));
1766 }
1767}