paulistrings/engine/partitioned/
runtime.rs1use std::sync::Arc;
5
6use super::in_process::{InProcessTransport, DEFAULT_WAIT_TIMEOUT};
7use super::topology::{
8 bind_current_thread_memory, build_pool, pin_current_thread, PartitionConfig, PartitionSlot,
9 TopologyError,
10};
11use super::PLACEMENT_LOG_TARGET as LOG_TARGET;
12
13pub struct PartitionRuntime {
15 slots: Vec<PartitionSlot>,
17 pools: Vec<rayon::ThreadPool>,
19 bind_memory: bool,
20 wait_timeout: std::time::Duration,
22}
23
24impl PartitionRuntime {
25 pub fn new(config: &PartitionConfig) -> Result<Arc<Self>, TopologyError> {
31 let slots = config.resolve()?;
32 let mut pools = Vec::with_capacity(slots.len());
33 for (rank, slot) in slots.iter().enumerate() {
34 pools.push(build_pool(slot, config.bind_memory, &format!("ps-part{rank}"))?);
35 }
36 #[cfg(feature = "cuda")]
38 let wait_timeout = if slots.iter().any(|s| s.device.is_some()) {
39 crate::engine::gpu::wire_timeout()
40 } else {
41 DEFAULT_WAIT_TIMEOUT
42 };
43 #[cfg(not(feature = "cuda"))]
44 let wait_timeout = DEFAULT_WAIT_TIMEOUT;
45 Ok(Arc::new(Self { slots, pools, bind_memory: config.bind_memory, wait_timeout }))
46 }
47
48 pub fn num_partitions(&self) -> usize {
50 self.slots.len()
51 }
52
53 #[cfg(feature = "cuda")]
55 pub(crate) fn slots(&self) -> &[PartitionSlot] {
56 &self.slots
57 }
58
59 pub(crate) fn partition_bits(&self) -> u8 {
61 debug_assert!(self.slots.len().is_power_of_two());
62 self.slots.len().trailing_zeros() as u8
63 }
64
65 pub(crate) fn placement_summary(&self) -> String {
67 let mut summary = String::new();
68 for (rank, slot) in self.slots.iter().enumerate() {
69 if rank > 0 {
70 summary.push_str(", ");
71 }
72 match (&slot.cpus, slot.device) {
73 (_, Some(device)) => {
74 summary.push_str(&format!("{rank}:gpu{device} x{}", slot.threads))
75 }
76 (Some(cpus), None) => summary.push_str(&format!("{rank}:{cpus}x{}", slot.threads)),
77 (None, None) => summary.push_str(&format!("{rank}:unpinned x{}", slot.threads)),
78 }
79 }
80 summary
81 }
82
83 pub(crate) fn install<R: Send>(&self, f: impl FnOnce() -> R + Send) -> R {
85 self.pools[0].install(f)
86 }
87
88 pub(crate) fn map_partitions<I, O, F>(&self, items: Vec<I>, f: F) -> Vec<O>
90 where
91 I: Send,
92 O: Send,
93 F: Fn(usize, I, &InProcessTransport) -> O + Send + Sync,
94 {
95 let size = self.num_partitions();
96 assert_eq!(items.len(), size, "map_partitions: one item per partition");
97 let transports = InProcessTransport::group_with_timeout(size as u32, self.wait_timeout);
99 let f = &f;
100
101 let mut items = items.into_iter();
102 let mut transports = transports.into_iter();
103 let item0 = items.next().expect("at least one partition");
104 let transport0 = transports.next().expect("at least one transport");
105
106 std::thread::scope(|scope| {
107 let handles: Vec<_> = items
108 .zip(transports)
109 .enumerate()
110 .map(|(i, (item, transport))| {
111 let rank = i + 1;
112 let slot = &self.slots[rank];
113 let pool = &self.pools[rank];
114 let bind_memory = self.bind_memory;
115 scope.spawn(move || {
116 place_current_thread(slot, bind_memory);
117 pool.install(move || f(rank, item, &transport))
118 })
119 })
120 .collect();
121
122 let mut out = Vec::with_capacity(size);
124 out.push(self.pools[0].install(|| f(0, item0, &transport0)));
125 for handle in handles {
126 match handle.join() {
127 Ok(o) => out.push(o),
128 Err(payload) => std::panic::resume_unwind(payload),
129 }
130 }
131 out
132 })
133 }
134}
135
136fn place_current_thread(slot: &PartitionSlot, bind_memory: bool) {
138 #[cfg(feature = "cuda")]
139 if let Some(device) = slot.device {
140 crate::engine::cuda_context::bind_device_context(device);
141 }
142 if let Some(cpus) = &slot.cpus {
143 if let Err(err) = pin_current_thread(cpus) {
144 log::warn!(target: LOG_TARGET, "failed to pin partition driver to {cpus}: {err}");
145 }
146 }
147 if bind_memory {
148 if let Err(err) = bind_current_thread_memory(slot.node) {
149 log::warn!(
150 target: LOG_TARGET,
151 "failed to bind partition driver memory to {:?}: {err}",
152 slot.node,
153 );
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests;