Skip to content

Commit

Permalink
Refactor IntCode to much faster single threaded design
Browse files Browse the repository at this point in the history
  • Loading branch information
maneatingape committed Sep 2, 2023
1 parent fc09196 commit c5c6b43
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 47 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions src/year2019/day05.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
28 changes: 15 additions & 13 deletions src/year2019/day07.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
}
}
}

Expand Down
56 changes: 29 additions & 27 deletions src/year2019/day09.rs
Original file line number Diff line number Diff line change
@@ -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<i64>,
input_rx: Receiver<i64>,
output_tx: Sender<i64>,
input: VecDeque<i64>,
}

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<i64>, Receiver<i64>) {
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
Expand All @@ -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 => {
Expand Down Expand Up @@ -91,7 +90,7 @@ pub mod intcode {
self.base += value;
self.pc += 2;
}
_ => break,
_ => break State::Halted,
}
}
}
Expand Down Expand Up @@ -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!(),
}
}

0 comments on commit c5c6b43

Please sign in to comment.