Bläddra i källkod

Intcode optimisations.

- Work out parameters inline and pass them, to save overhead of
  calling a function
- Reduce number of cases we check for memory bounds, and don't
  add quite so much each time
- Change CSV reading function to just use ASCII chars not a full
  unicode decode
master
Chris Smith 4 år sedan
förälder
incheckning
9247dc5e13
Signerad av: Chris Smith <chris@chameth.com> GPG-nyckel ID: 3A2D4BBDC4A3C9A9
4 ändrade filer med 91 tillägg och 90 borttagningar
  1. 1
    0
      09/.intcode
  2. 4
    10
      common/io.go
  3. 39
    26
      intcode/ops.go
  4. 47
    54
      intcode/vm.go

+ 1
- 0
09/.intcode Visa fil

1
+201912152221

+ 4
- 10
common/io.go Visa fil

3
 import (
3
 import (
4
 	"bufio"
4
 	"bufio"
5
 	"os"
5
 	"os"
6
-	"unicode"
7
-	"unicode/utf8"
8
 )
6
 )
9
 
7
 
10
 // ReadFileAsInts reads all lines from the given path and returns them in a slice of ints.
8
 // ReadFileAsInts reads all lines from the given path and returns them in a slice of ints.
84
 	// Skip leading spaces.
82
 	// Skip leading spaces.
85
 	start := 0
83
 	start := 0
86
 	for width := 0; start < len(data); start += width {
84
 	for width := 0; start < len(data); start += width {
87
-		var r rune
88
-		r, width = utf8.DecodeRune(data[start:])
89
-		if !unicode.IsSpace(r) {
85
+		if data[start] != ' ' {
90
 			break
86
 			break
91
 		}
87
 		}
92
 	}
88
 	}
93
 
89
 
94
 	// Scan until comma, marking end of word.
90
 	// Scan until comma, marking end of word.
95
-	for width, i := 0, start; i < len(data); i += width {
96
-		var r rune
97
-		r, width = utf8.DecodeRune(data[i:])
98
-		if r == ',' || unicode.IsSpace(r) {
99
-			return i + width, data[start:i], nil
91
+	for i := start; i < len(data); i++ {
92
+		if data[i] == ',' || data[i] == ' ' || data[i] == '\r' || data[i] == '\n' {
93
+			return i + 1, data[start:i], nil
100
 		}
94
 		}
101
 	}
95
 	}
102
 	// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
96
 	// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.

+ 39
- 26
intcode/ops.go Visa fil

1
 package intcode
1
 package intcode
2
 
2
 
3
 // opcodeFunc is a function that describes an opcode implemented in the VM.
3
 // opcodeFunc is a function that describes an opcode implemented in the VM.
4
-type opcodeFunc = func(vm *VirtualMachine)
4
+type opcodeFunc = func(vm *VirtualMachine, arg1, arg2, arg3 *int)
5
 
5
 
6
 var opcodes = [100]opcodeFunc{
6
 var opcodes = [100]opcodeFunc{
7
 	1:  addOpcode,
7
 	1:  addOpcode,
16
 	99: haltOpcode,
16
 	99: haltOpcode,
17
 }
17
 }
18
 
18
 
19
+var opcodeArity = [100]int{
20
+	1:  3,
21
+	2:  3,
22
+	3:  1,
23
+	4:  1,
24
+	5:  2,
25
+	6:  2,
26
+	7:  3,
27
+	8:  3,
28
+	9:  1,
29
+	99: 0,
30
+}
31
+
19
 // addOpcode takes the values specified by args 1 and 2, adds them together, and stores at the memory address given
32
 // addOpcode takes the values specified by args 1 and 2, adds them together, and stores at the memory address given
20
 // by arg 3.
33
 // by arg 3.
21
-func addOpcode(vm *VirtualMachine) {
22
-	*vm.arg(2) = *vm.arg(0) + *vm.arg(1)
34
+func addOpcode(vm *VirtualMachine, arg1, arg2, arg3 *int) {
35
+	*arg3 = *arg1 + *arg2
23
 	vm.ip += 4
36
 	vm.ip += 4
24
 }
37
 }
25
 
38
 
26
 // mulOpcode takes the values specified by args 1 and 2, multiplies them together, and stores at the memory address
39
 // mulOpcode takes the values specified by args 1 and 2, multiplies them together, and stores at the memory address
27
 // given by arg 3.
40
 // given by arg 3.
28
-func mulOpcode(vm *VirtualMachine) {
29
-	*vm.arg(2) = *vm.arg(0) * *vm.arg(1)
41
+func mulOpcode(vm *VirtualMachine, arg1, arg2, arg3 *int) {
42
+	*arg3 = *arg1 * *arg2
30
 	vm.ip += 4
43
 	vm.ip += 4
31
 }
44
 }
32
 
45
 
33
 // readOpCode reads a value from the input stream and stores it at the memory address given by arg 1.
46
 // readOpCode reads a value from the input stream and stores it at the memory address given by arg 1.
34
-func readOpCode(vm *VirtualMachine) {
35
-	*vm.arg(0) = <-vm.Input
47
+func readOpCode(vm *VirtualMachine, arg1, _, _ *int) {
48
+	*arg1 = <-vm.Input
36
 	vm.ip += 2
49
 	vm.ip += 2
37
 }
50
 }
38
 
51
 
39
 // writeOpCode writes the value specified by the first argument to the output stream.
52
 // writeOpCode writes the value specified by the first argument to the output stream.
40
-func writeOpCode(vm *VirtualMachine) {
41
-	vm.Output <- *vm.arg(0)
53
+func writeOpCode(vm *VirtualMachine, arg1, _, _ *int) {
54
+	vm.Output <- *arg1
42
 	vm.ip += 2
55
 	vm.ip += 2
43
 }
56
 }
44
 
57
 
45
 // jumpIfTrueOpCode checks if the first argument is not zero, and if so jumps to the second argument.
58
 // jumpIfTrueOpCode checks if the first argument is not zero, and if so jumps to the second argument.
46
-func jumpIfTrueOpCode(vm *VirtualMachine) {
47
-	if *vm.arg(0) != 0 {
48
-		vm.ip = *vm.arg(1)
59
+func jumpIfTrueOpCode(vm *VirtualMachine, arg1, arg2, _ *int) {
60
+	if *arg1 != 0 {
61
+		vm.ip = *arg2
49
 	} else {
62
 	} else {
50
 		vm.ip += 3
63
 		vm.ip += 3
51
 	}
64
 	}
52
 }
65
 }
53
 
66
 
54
 // jumpIfFalseOpCode checks if the first argument is zero, and if so jumps to the second argument.
67
 // jumpIfFalseOpCode checks if the first argument is zero, and if so jumps to the second argument.
55
-func jumpIfFalseOpCode(vm *VirtualMachine) {
56
-	if *vm.arg(0) == 0 {
57
-		vm.ip = *vm.arg(1)
68
+func jumpIfFalseOpCode(vm *VirtualMachine, arg1, arg2, _ *int) {
69
+	if *arg1 == 0 {
70
+		vm.ip = *arg2
58
 	} else {
71
 	} else {
59
 		vm.ip += 3
72
 		vm.ip += 3
60
 	}
73
 	}
62
 
75
 
63
 // lessThanOpCode checks if the first argument is less than the second, and stores the result at the address given
76
 // lessThanOpCode checks if the first argument is less than the second, and stores the result at the address given
64
 // by the third argument.
77
 // by the third argument.
65
-func lessThanOpCode(vm *VirtualMachine) {
66
-	if *vm.arg(0) < *vm.arg(1) {
67
-		*vm.arg(2) = 1
78
+func lessThanOpCode(vm *VirtualMachine, arg1, arg2, arg3 *int) {
79
+	if *arg1 < *arg2 {
80
+		*arg3 = 1
68
 	} else {
81
 	} else {
69
-		*vm.arg(2) = 0
82
+		*arg3 = 0
70
 	}
83
 	}
71
 	vm.ip += 4
84
 	vm.ip += 4
72
 }
85
 }
73
 
86
 
74
 // equalsOpCode checks if the first argument is equal to the second, and stores the result at the address given
87
 // equalsOpCode checks if the first argument is equal to the second, and stores the result at the address given
75
 // by the third argument.
88
 // by the third argument.
76
-func equalsOpCode(vm *VirtualMachine) {
77
-	if *vm.arg(0) == *vm.arg(1) {
78
-		*vm.arg(2) = 1
89
+func equalsOpCode(vm *VirtualMachine, arg1, arg2, arg3 *int) {
90
+	if *arg1 == *arg2 {
91
+		*arg3 = 1
79
 	} else {
92
 	} else {
80
-		*vm.arg(2) = 0
93
+		*arg3 = 0
81
 	}
94
 	}
82
 	vm.ip += 4
95
 	vm.ip += 4
83
 }
96
 }
84
 
97
 
85
 // relativeBaseOffsetOpCode increases the relative base by the given argument.
98
 // relativeBaseOffsetOpCode increases the relative base by the given argument.
86
-func relativeBaseOffsetOpCode(vm *VirtualMachine) {
87
-	vm.rb += *vm.arg(0)
99
+func relativeBaseOffsetOpCode(vm *VirtualMachine, arg1, _, _ *int) {
100
+	vm.rb += *arg1
88
 	vm.ip += 2
101
 	vm.ip += 2
89
 }
102
 }
90
 
103
 
91
 // haltOpcode halts the VM and takes no arguments.
104
 // haltOpcode halts the VM and takes no arguments.
92
-func haltOpcode(vm *VirtualMachine) {
105
+func haltOpcode(vm *VirtualMachine, _, _, _ *int) {
93
 	vm.Halted = true
106
 	vm.Halted = true
94
 }
107
 }

+ 47
- 54
intcode/vm.go Visa fil

1
 package intcode
1
 package intcode
2
 
2
 
3
+type parameterMode int
4
+
5
+const (
6
+	position  parameterMode = 0
7
+	immediate parameterMode = 1
8
+	relative  parameterMode = 2
9
+)
10
+
3
 // VirtualMachine is an IntCode virtual machine.
11
 // VirtualMachine is an IntCode virtual machine.
4
 type VirtualMachine struct {
12
 type VirtualMachine struct {
5
-	ip             int
6
-	rb             int
7
-	parameterModes uint8
8
-	relativeModes  uint8
9
-	Memory         []int
10
-	Halted         bool
11
-	Input          chan int
12
-	Output         chan int
13
+	ip     int
14
+	rb     int
15
+	Memory []int
16
+	Halted bool
17
+	Input  chan int
18
+	Output chan int
13
 }
19
 }
14
 
20
 
15
 // NewVirtualMachine creates a new IntCode virtual machine, initialised to the given slice of memory.
21
 // NewVirtualMachine creates a new IntCode virtual machine, initialised to the given slice of memory.
30
 	copy(memory, vm.Memory)
36
 	copy(memory, vm.Memory)
31
 
37
 
32
 	return &VirtualMachine{
38
 	return &VirtualMachine{
33
-		ip:             vm.ip,
34
-		rb:             vm.rb,
35
-		parameterModes: vm.parameterModes,
36
-		relativeModes:  vm.relativeModes,
37
-		Memory:         memory,
38
-		Halted:         vm.Halted,
39
-		Input:          make(chan int, 1),
40
-		Output:         make(chan int, 1),
41
-	}
42
-}
43
-
44
-// arg Returns the value of the given argument for the current instruction.
45
-func (vm *VirtualMachine) arg(pos int) *int {
46
-	mask := uint8(1) << uint8(pos)
47
-	if vm.parameterModes&mask == mask {
48
-		// Parameter mode - the value of the argument is just treated as an int
49
-		for vm.ip+1+pos > len(vm.Memory) {
50
-			vm.Memory = append(vm.Memory, make([]int, 1024)...)
51
-		}
52
-
53
-		return &vm.Memory[vm.ip+1+pos]
54
-	} else if vm.relativeModes&mask == mask {
55
-		// Relative mode - the value of the argument is treated as a memory offset from the relative base
56
-		for vm.ip+1+pos > len(vm.Memory) || vm.rb+vm.Memory[vm.ip+1+pos] > len(vm.Memory) {
57
-			vm.Memory = append(vm.Memory, make([]int, 1024)...)
58
-		}
59
-
60
-		return &vm.Memory[vm.rb+vm.Memory[vm.ip+1+pos]]
61
-	} else {
62
-		// Position mode - the value of the argument is treated as a memory offset from the start of the memory
63
-		for vm.ip+1+pos > len(vm.Memory) || vm.Memory[vm.ip+1+pos] > len(vm.Memory) {
64
-			vm.Memory = append(vm.Memory, make([]int, 1024)...)
65
-		}
66
-
67
-		return &vm.Memory[vm.Memory[vm.ip+1+pos]]
39
+		ip:     vm.ip,
40
+		rb:     vm.rb,
41
+		Memory: memory,
42
+		Halted: vm.Halted,
43
+		Input:  make(chan int, 1),
44
+		Output: make(chan int, 1),
68
 	}
45
 	}
69
 }
46
 }
70
 
47
 
71
 // Run repeatedly executes instructions until the VM halts.
48
 // Run repeatedly executes instructions until the VM halts.
72
 func (vm *VirtualMachine) Run() {
49
 func (vm *VirtualMachine) Run() {
50
+	var args [3]*int
73
 	for !vm.Halted {
51
 	for !vm.Halted {
74
 		instruction := vm.Memory[vm.ip]
52
 		instruction := vm.Memory[vm.ip]
75
 		opcode := instruction % 100
53
 		opcode := instruction % 100
76
 
54
 
77
-		vm.parameterModes = 0
78
-		vm.relativeModes = 0
79
-		mask := uint8(1)
80
-		for i := instruction / 100; i > 0; i /= 10 {
81
-			mode := i % 10
82
-			if mode == 1 {
83
-				vm.parameterModes = vm.parameterModes | mask
84
-			} else if mode == 2 {
85
-				vm.relativeModes = vm.relativeModes | mask
55
+		param := instruction / 100
56
+		for i := 0; i < opcodeArity[opcode]; i++ {
57
+
58
+			switch parameterMode(param % 10) {
59
+
60
+			// The argument is the actual value
61
+			case immediate:
62
+				args[i] = &vm.Memory[vm.ip+1+i]
63
+
64
+			// The argument is a memory reference
65
+			case position:
66
+				for vm.Memory[vm.ip+1+i] >= len(vm.Memory) {
67
+					vm.Memory = append(vm.Memory, make([]int, 128)...)
68
+				}
69
+				args[i] = &vm.Memory[vm.Memory[vm.ip+1+i]]
70
+
71
+			// The argument is a memory reference offset by the relative base
72
+			case relative:
73
+				for vm.rb+vm.Memory[vm.ip+1+i] >= len(vm.Memory) {
74
+					vm.Memory = append(vm.Memory, make([]int, 128)...)
75
+				}
76
+				args[i] = &vm.Memory[vm.rb+vm.Memory[vm.ip+1+i]]
77
+
86
 			}
78
 			}
87
-			mask = mask << 1
79
+
80
+			param /= 10
88
 		}
81
 		}
89
 
82
 
90
-		opcodes[opcode](vm)
83
+		opcodes[opcode](vm, args[0], args[1], args[2])
91
 	}
84
 	}
92
 	if vm.Output != nil {
85
 	if vm.Output != nil {
93
 		close(vm.Output)
86
 		close(vm.Output)

Laddar…
Avbryt
Spara