Optimize day 07 part 2 with a thread pool
authorUmmon <greg.burri@gmail.com>
Sat, 14 Dec 2019 12:01:31 +0000 (13:01 +0100)
committerUmmon <greg.burri@gmail.com>
Sat, 14 Dec 2019 12:01:31 +0000 (13:01 +0100)
Cargo.toml
src/day07.rs

index c7438f7..c456a17 100644 (file)
@@ -7,4 +7,5 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-itertools = "0.8"
\ No newline at end of file
+itertools = "0.8"
+threadpool = "1.7"
\ No newline at end of file
index 0d00d1c..61a370a 100644 (file)
@@ -1,7 +1,7 @@
 use super::intcode;\r
 use itertools::Itertools;\r
-use std::sync::mpsc::{ self, Sender, Receiver };\r
-use std::thread::{ self, JoinHandle };\r
+use std::sync::{ Arc, Barrier, mpsc::{ self, Sender, Receiver }, atomic::{ AtomicI64, Ordering } };\r
+use threadpool::ThreadPool;\r
 \r
 fn last_thruster_signal(code: &[i64], phase_setting: &[i64]) -> i64 {\r
     phase_setting.iter().fold(0, |last_output, input| intcode::execute_op_code(&code, &[*input, last_output])[0])\r
@@ -29,11 +29,11 @@ impl intcode::IO for Stage {
     // Send to the output channel.\r
     fn write(&mut self, value: i64) {\r
         self.last_produced_value = value;\r
-        self.output_channel.send(value);\r
+        self.output_channel.send(value).unwrap_or_default();\r
     }\r
 }\r
 \r
-fn last_thruster_signal_with_feedback_loop(code: &[i64], phase_setting: &[i64]) -> i64 {\r
+fn last_thruster_signal_with_feedback_loop(code: &[i64], phase_setting: &[i64], pool: &ThreadPool) -> i64 {\r
     let n = phase_setting.len();\r
 \r
     let mut senders = Vec::<Sender<i64>>::new();\r
@@ -41,8 +41,8 @@ fn last_thruster_signal_with_feedback_loop(code: &[i64], phase_setting: &[i64])
 \r
     for (i, (s, r)) in (0 .. n).map(|i| (i, mpsc::channel::<i64>())) {\r
         // Initial values.\r
-        s.send(phase_setting[i]);\r
-        if i == 0 { s.send(0); }\r
+        s.send(phase_setting[i]).unwrap_or_default();\r
+        if i == 0 { s.send(0).unwrap_or_default(); }\r
 \r
         senders.insert(if i == 0 { 0 } else { i - 1 }, s);\r
         receivers.push(r);\r
@@ -51,28 +51,33 @@ fn last_thruster_signal_with_feedback_loop(code: &[i64], phase_setting: &[i64])
     // Prepare each pair of received and sender for the each stages.\r
     let mut channels: Vec<(Receiver<i64>, Sender<i64>)> = receivers.drain(..).zip(senders.drain(..)).collect();\r
 \r
-    let mut join_handles: Vec<JoinHandle<i64>> =\r
-        channels\r
-            .drain(..)\r
-            .map(\r
-                |(receiver, sender)| {\r
-                    let code_copy = Vec::<i64>::from(code);\r
-                    thread::spawn(\r
-                        move || {\r
-                            let mut stage = Stage { input_channel: receiver, output_channel: sender, last_produced_value: 0 };\r
-                            intcode::execute_op_code_with_custom_io(&code_copy, &mut stage);\r
-                            stage.last_produced_value\r
-                        }\r
-                    )\r
+    let result = Arc::new(AtomicI64::new(0));\r
+    let barrier = Arc::new(Barrier::new(2));\r
+\r
+    for (i, (receiver, sender)) in channels.drain(..).enumerate() {\r
+        let code_copy = Vec::<i64>::from(code);\r
+        let barrier = barrier.clone();\r
+        let result = result.clone();\r
+\r
+        pool.execute(\r
+            move || {\r
+                let mut stage = Stage { input_channel: receiver, output_channel: sender, last_produced_value: 0 };\r
+                intcode::execute_op_code_with_custom_io(&code_copy, &mut stage);\r
+                if i == 4 {\r
+                    result.store(stage.last_produced_value, Ordering::Relaxed);\r
+                    barrier.wait();\r
                 }\r
-            )\r
-            .collect();\r
+            }\r
+        )\r
+    }\r
 \r
-    join_handles.pop().unwrap().join().unwrap()\r
+    barrier.wait();\r
+    result.load(Ordering::Relaxed)\r
 }\r
 \r
 pub fn find_largest_last_thruster_signal_with_feedback_loop(code: &[i64]) -> i64 {\r
-    (5i64 ..= 9i64).permutations(5).map(|phase_setting| last_thruster_signal_with_feedback_loop(&code, &phase_setting)).max().unwrap()\r
+    let pool = ThreadPool::new(5);\r
+    (5i64 ..= 9i64).permutations(5).map(|phase_setting| last_thruster_signal_with_feedback_loop(&code, &phase_setting, &pool)).max().unwrap()\r
 }\r
 \r
 #[cfg(test)]\r
@@ -104,13 +109,15 @@ mod tests {
     fn part2_sample_1() {\r
         let code = vec![3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5];\r
         let phase_setting = [9,8,7,6,5];\r
-        assert_eq!(last_thruster_signal_with_feedback_loop(&code, &phase_setting), 139_629_729);\r
+        let pool = ThreadPool::new(5);\r
+        assert_eq!(last_thruster_signal_with_feedback_loop(&code, &phase_setting, &pool), 139_629_729);\r
     }\r
 \r
     #[test]\r
     fn part2_sample_2() {\r
         let code = vec![3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10];\r
         let phase_setting = [9,7,8,5,6];\r
-        assert_eq!(last_thruster_signal_with_feedback_loop(&code, &phase_setting), 18_216);\r
+        let pool = ThreadPool::new(5);\r
+        assert_eq!(last_thruster_signal_with_feedback_loop(&code, &phase_setting, &pool), 18_216);\r
     }\r
 }
\ No newline at end of file