From c5c6b43d2a2b27c61a0c14504f1ca9061ecfd77d Mon Sep 17 00:00:00 2001 From: maneatingape <44142177+maneatingape@users.noreply.github.com> Date: Sat, 2 Sep 2023 23:20:34 +0200 Subject: [PATCH] Refactor IntCode to much faster single threaded design --- README.md | 6 ++--- src/year2019/day05.rs | 8 +++---- src/year2019/day07.rs | 28 ++++++++++++---------- src/year2019/day09.rs | 56 ++++++++++++++++++++++--------------------- 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 50577ca..fbd40ef 100644 --- a/README.md +++ b/README.md @@ -197,11 +197,11 @@ pie | 2 | [1202 Program Alarm](https://adventofcode.com/2019/day/2) | [Source](src/year2019/day02.rs) | 1 | | 3 | [Crossed Wires](https://adventofcode.com/2019/day/3) | [Source](src/year2019/day03.rs) | 19 | | 4 | [Secure Container](https://adventofcode.com/2019/day/4) | [Source](src/year2019/day04.rs) | 7 | -| 5 | [Sunny with a Chance of Asteroids](https://adventofcode.com/2019/day/5) | [Source](src/year2019/day05.rs) | 34 | +| 5 | [Sunny with a Chance of Asteroids](https://adventofcode.com/2019/day/5) | [Source](src/year2019/day05.rs) | 3 | | 6 | [Universal Orbit Map](https://adventofcode.com/2019/day/6) | [Source](src/year2019/day06.rs) | 28 | -| 7 | [Amplification Circuit](https://adventofcode.com/2019/day/7) | [Source](src/year2019/day07.rs) | 30000 | +| 7 | [Amplification Circuit](https://adventofcode.com/2019/day/7) | [Source](src/year2019/day07.rs) | 347 | | 8 | [Space Image Format](https://adventofcode.com/2019/day/8) | [Source](src/year2019/day08.rs) | 5 | -| 9 | [Sensor Boost](https://adventofcode.com/2019/day/9) | [Source](src/year2019/day09.rs) | 3088 | +| 9 | [Sensor Boost](https://adventofcode.com/2019/day/9) | [Source](src/year2019/day09.rs) | 3095 | ## 2015 diff --git a/src/year2019/day05.rs b/src/year2019/day05.rs index d00238b..f12a0ba 100644 --- a/src/year2019/day05.rs +++ b/src/year2019/day05.rs @@ -17,12 +17,12 @@ pub fn part2(input: &[i64]) -> i64 { /// Start `IntCode` computer in its own thread, sending a single initial value. /// Receives multiple values from the output channel returning only the last one. fn run(input: &[i64], value: i64) -> i64 { - let (tx, rx) = Computer::spawn(input); - let _ = tx.send(value); + let mut computer = Computer::new(input); + computer.input(&[value]); let mut result = 0; - while let Ok(output) = rx.recv() { - result = output; + while let State::Output(next) = computer.run() { + result = next; } result } diff --git a/src/year2019/day07.rs b/src/year2019/day07.rs index 3142136..bc22541 100644 --- a/src/year2019/day07.rs +++ b/src/year2019/day07.rs @@ -26,10 +26,12 @@ pub fn part1(input: &[i64]) -> i64 { // Send exactly 2 inputs and receive exactly 1 output per amplifier. for &phase in slice { - let (tx, rx) = Computer::spawn(input); - let _ = tx.send(phase); - let _ = tx.send(signal); - signal = rx.recv().unwrap(); + let mut computer = Computer::new(input); + computer.input(&[phase, signal]); + match computer.run() { + State::Output(next) => signal = next, + _ => unreachable!(), + } } result = result.max(signal); @@ -43,23 +45,23 @@ pub fn part2(input: &[i64]) -> i64 { let mut result = 0; let feedback = |slice: &[i64]| { - let (senders, receivers): (Vec<_>, Vec<_>) = (0..5).map(|_| Computer::spawn(input)).unzip(); + let mut computers: Vec<_> = (0..5).map(|_| Computer::new(input)).collect(); // Send each initial phase setting exactly once. - for (tx, &phase) in senders.iter().zip(slice.iter()) { - let _ = tx.send(phase); + for (computer, &phase) in computers.iter_mut().zip(slice.iter()) { + computer.input(&[phase]); } // Chain amplifier inputs and ouputs in a loop until all threads finish. let mut signal = 0; 'outer: loop { - for (tx, rx) in senders.iter().zip(receivers.iter()) { - let _ = tx.send(signal); - let Ok(next) = rx.recv() else { - break 'outer; - }; - signal = next; + for computer in &mut computers { + computer.input(&[signal]); + match computer.run() { + State::Output(next) => signal = next, + _ => break 'outer, + } } } diff --git a/src/year2019/day09.rs b/src/year2019/day09.rs index 4e93015..b647149 100644 --- a/src/year2019/day09.rs +++ b/src/year2019/day09.rs @@ -1,40 +1,37 @@ //! # Sensor Boost //! -//! This problem is essentially an unit test for the canonical full intcode computer -//! used heavily by other days. +//! This problem is essentially a unit test for the full intcode computer used by other days. use crate::util::parse::*; use intcode::*; pub mod intcode { - use std::sync::mpsc::*; - use std::thread; + use std::collections::VecDeque; + + pub enum State { + Input, + Output(i64), + Halted, + } pub struct Computer { pc: usize, base: i64, code: Vec, - input_rx: Receiver, - output_tx: Sender, + input: VecDeque, } impl Computer { - /// Spawns an `IntCode` computer in a new thread, returning an input and output channel - /// for communicating asynchronously with the computer via the opcodes 3 and 4. - pub fn spawn(code: &[i64]) -> (Sender, Receiver) { - let pc = 0; - let base = 0; - let code = code.to_vec(); - let (input_tx, input_rx) = channel(); - let (output_tx, output_rx) = channel(); - - let mut computer = Computer { pc, base, code, input_rx, output_tx }; - thread::spawn(move || computer.run()); - - (input_tx, output_rx) + pub fn new(code: &[i64]) -> Computer { + Computer { pc: 0, base: 0, code: code.to_vec(), input: VecDeque::new() } } - /// Runs until a `99` opcode instruction is encountered. - fn run(&mut self) { + pub fn input(&mut self, slice: &[i64]) { + self.input.extend(slice); + } + + // Runs until either the program needs input, outputs a value or encounters the halt opcode. + // In the first two cases, the computer can be restarted by calling `run` again. + pub fn run(&mut self) -> State { loop { match self.code[self.pc] % 100 { // Add @@ -51,15 +48,17 @@ pub mod intcode { } // Read input channel 3 => { - let value = self.input_rx.recv().unwrap(); + let Some(value) = self.input.pop_front() else { + break State::Input; + }; self.write(1, value); self.pc += 2; } // Write output channel 4 => { let value = self.read(1); - let _ = self.output_tx.send(value); self.pc += 2; + break State::Output(value); } // Jump if true 5 => { @@ -91,7 +90,7 @@ pub mod intcode { self.base += value; self.pc += 2; } - _ => break, + _ => break State::Halted, } } } @@ -143,7 +142,10 @@ pub fn part2(input: &[i64]) -> i64 { } fn run(input: &[i64], value: i64) -> i64 { - let (tx, rx) = Computer::spawn(input); - let _ = tx.send(value); - rx.recv().unwrap() + let mut computer = Computer::new(input); + computer.input(&[value]); + match computer.run() { + State::Output(result) => result, + _ => unreachable!(), + } }