Преглед изворни кода

Various intcode optimisations

- Store opcodes in an array rather than a map for faster lookup
  (this means storing them as interface{} as otherwise the types
  form a circular dependency, which is icky but worth the
  performance boost).
- Don't dynamically create an 'Arg' function every step of the vm.
  This was *super* expensive.
- Calculate a bitmask of the argument modes instead of relying
  on on-demand div-modding.
- Only buffer a single value in the I/O buffers to reduce the
  size of allocations.
master
Chris Smith пре 4 година
родитељ
комит
764d3e886e
Signed by: Chris Smith <chris@chameth.com> GPG Key ID: 3A2D4BBDC4A3C9A9
3 измењених фајлова са 46 додато и 45 уклоњено
  1. 2
    2
      05/main.go
  2. 19
    22
      intcode/ops.go
  3. 25
    21
      intcode/vm.go

+ 2
- 2
05/main.go Прегледај датотеку

24
 
24
 
25
 	vm := intcode.NewVirtualMachine(memory)
25
 	vm := intcode.NewVirtualMachine(memory)
26
 	vm.Input <- 1
26
 	vm.Input <- 1
27
-	vm.Run()
27
+	go vm.Run()
28
 	fmt.Println(last(vm.Output))
28
 	fmt.Println(last(vm.Output))
29
 
29
 
30
 	vm.Reset(input)
30
 	vm.Reset(input)
31
 	vm.Input <- 5
31
 	vm.Input <- 5
32
-	vm.Run()
32
+	go vm.Run()
33
 	fmt.Println(last(vm.Output))
33
 	fmt.Println(last(vm.Output))
34
 }
34
 }

+ 19
- 22
intcode/ops.go Прегледај датотеку

1
 package intcode
1
 package intcode
2
 
2
 
3
-// ArgFunc provides the value of an argument for an opcode
4
-type ArgFunc = func(pos int) int
5
-
6
 // 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.
7
-type OpcodeFunc = func(vm *VirtualMachine, param ArgFunc)
4
+type OpcodeFunc = func(vm *VirtualMachine)
8
 
5
 
9
 // AddOpcode takes the values specified by args 1 and 2, adds them together, and stores at the memory address given
6
 // AddOpcode takes the values specified by args 1 and 2, adds them together, and stores at the memory address given
10
 // by arg 3.
7
 // by arg 3.
11
-func AddOpcode(vm *VirtualMachine, arg ArgFunc) {
12
-	vm.Memory[vm.Memory[vm.ip+3]] = arg(0) + arg(1)
8
+func AddOpcode(vm *VirtualMachine) {
9
+	vm.Memory[vm.Memory[vm.ip+3]] = vm.arg(0) + vm.arg(1)
13
 	vm.ip += 4
10
 	vm.ip += 4
14
 }
11
 }
15
 
12
 
16
 // MulOpcode takes the values specified by args 1 and 2, multiplies them together, and stores at the memory address
13
 // MulOpcode takes the values specified by args 1 and 2, multiplies them together, and stores at the memory address
17
 // given by arg 3.
14
 // given by arg 3.
18
-func MulOpcode(vm *VirtualMachine, arg ArgFunc) {
19
-	vm.Memory[vm.Memory[vm.ip+3]] = arg(0) * arg(1)
15
+func MulOpcode(vm *VirtualMachine) {
16
+	vm.Memory[vm.Memory[vm.ip+3]] = vm.arg(0) * vm.arg(1)
20
 	vm.ip += 4
17
 	vm.ip += 4
21
 }
18
 }
22
 
19
 
23
 // ReadOpCode reads a value from the input stream and stores it at the memory address given by arg 1.
20
 // ReadOpCode reads a value from the input stream and stores it at the memory address given by arg 1.
24
-func ReadOpCode(vm *VirtualMachine, arg ArgFunc) {
21
+func ReadOpCode(vm *VirtualMachine) {
25
 	vm.Memory[vm.Memory[vm.ip+1]] = <-vm.Input
22
 	vm.Memory[vm.Memory[vm.ip+1]] = <-vm.Input
26
 	vm.ip += 2
23
 	vm.ip += 2
27
 }
24
 }
28
 
25
 
29
 // WriteOpCode writes the value specified by the first argument to the output stream.
26
 // WriteOpCode writes the value specified by the first argument to the output stream.
30
-func WriteOpCode(vm *VirtualMachine, arg ArgFunc) {
31
-	vm.Output <- arg(0)
27
+func WriteOpCode(vm *VirtualMachine) {
28
+	vm.Output <- vm.arg(0)
32
 	vm.ip += 2
29
 	vm.ip += 2
33
 }
30
 }
34
 
31
 
35
 // JumpIfTrueOpCode checks if the first argument is not zero, and if so jumps to the second argument.
32
 // JumpIfTrueOpCode checks if the first argument is not zero, and if so jumps to the second argument.
36
-func JumpIfTrueOpCode(vm *VirtualMachine, arg ArgFunc) {
37
-	if arg(0) != 0 {
38
-		vm.ip = arg(1)
33
+func JumpIfTrueOpCode(vm *VirtualMachine) {
34
+	if vm.arg(0) != 0 {
35
+		vm.ip = vm.arg(1)
39
 	} else {
36
 	} else {
40
 		vm.ip += 3
37
 		vm.ip += 3
41
 	}
38
 	}
42
 }
39
 }
43
 
40
 
44
 // JumpIfFalseOpCode checks if the first argument is zero, and if so jumps to the second argument.
41
 // JumpIfFalseOpCode checks if the first argument is zero, and if so jumps to the second argument.
45
-func JumpIfFalseOpCode(vm *VirtualMachine, arg ArgFunc) {
46
-	if arg(0) == 0 {
47
-		vm.ip = arg(1)
42
+func JumpIfFalseOpCode(vm *VirtualMachine) {
43
+	if vm.arg(0) == 0 {
44
+		vm.ip = vm.arg(1)
48
 	} else {
45
 	} else {
49
 		vm.ip += 3
46
 		vm.ip += 3
50
 	}
47
 	}
52
 
49
 
53
 // LessThanOpCode checks if the first argument is less than the second, and stores the result at the address given
50
 // LessThanOpCode checks if the first argument is less than the second, and stores the result at the address given
54
 // by the third argument.
51
 // by the third argument.
55
-func LessThanOpCode(vm *VirtualMachine, arg ArgFunc) {
56
-	if arg(0) < arg(1) {
52
+func LessThanOpCode(vm *VirtualMachine) {
53
+	if vm.arg(0) < vm.arg(1) {
57
 		vm.Memory[vm.Memory[vm.ip+3]] = 1
54
 		vm.Memory[vm.Memory[vm.ip+3]] = 1
58
 	} else {
55
 	} else {
59
 		vm.Memory[vm.Memory[vm.ip+3]] = 0
56
 		vm.Memory[vm.Memory[vm.ip+3]] = 0
63
 
60
 
64
 // EqualsOpCode checks if the first argument is equal to the second, and stores the result at the address given
61
 // EqualsOpCode checks if the first argument is equal to the second, and stores the result at the address given
65
 // by the third argument.
62
 // by the third argument.
66
-func EqualsOpCode(vm *VirtualMachine, arg ArgFunc) {
67
-	if arg(0) == arg(1) {
63
+func EqualsOpCode(vm *VirtualMachine) {
64
+	if vm.arg(0) == vm.arg(1) {
68
 		vm.Memory[vm.Memory[vm.ip+3]] = 1
65
 		vm.Memory[vm.Memory[vm.ip+3]] = 1
69
 	} else {
66
 	} else {
70
 		vm.Memory[vm.Memory[vm.ip+3]] = 0
67
 		vm.Memory[vm.Memory[vm.ip+3]] = 0
73
 }
70
 }
74
 
71
 
75
 // HaltOpcode halts the VM and takes no arguments.
72
 // HaltOpcode halts the VM and takes no arguments.
76
-func HaltOpcode(vm *VirtualMachine, arg ArgFunc) {
73
+func HaltOpcode(vm *VirtualMachine) {
77
 	vm.Halted = true
74
 	vm.Halted = true
78
 }
75
 }

+ 25
- 21
intcode/vm.go Прегледај датотеку

1
 package intcode
1
 package intcode
2
 
2
 
3
-import (
4
-	"fmt"
5
-	"math"
6
-)
7
-
8
 // VirtualMachine is an IntCode virtual machine.
3
 // VirtualMachine is an IntCode virtual machine.
9
 type VirtualMachine struct {
4
 type VirtualMachine struct {
10
 	ip      int
5
 	ip      int
11
-	opcodes map[int]OpcodeFunc
6
+	modes   uint8
7
+	opcodes [100]interface{}
12
 	Memory  []int
8
 	Memory  []int
13
 	Halted  bool
9
 	Halted  bool
14
 	Input   chan int
10
 	Input   chan int
22
 		ip:     0,
18
 		ip:     0,
23
 		Memory: memory,
19
 		Memory: memory,
24
 		Halted: false,
20
 		Halted: false,
25
-		Input:  make(chan int, 100),
26
-		Output: make(chan int, 100),
27
-		opcodes: map[int]OpcodeFunc{
21
+		Input:  make(chan int, 1),
22
+		Output: make(chan int, 1),
23
+		opcodes: [100]interface{}{
28
 			1:  AddOpcode,
24
 			1:  AddOpcode,
29
 			2:  MulOpcode,
25
 			2:  MulOpcode,
30
 			3:  ReadOpCode,
26
 			3:  ReadOpCode,
38
 	}
34
 	}
39
 }
35
 }
40
 
36
 
37
+func (vm *VirtualMachine) arg(pos int) int {
38
+	mask := uint8(1) << uint8(pos)
39
+	if vm.modes&mask == mask {
40
+		return vm.Memory[vm.ip+1+pos]
41
+	} else {
42
+		return vm.Memory[vm.Memory[vm.ip+1+pos]]
43
+	}
44
+}
45
+
41
 // Run repeatedly executes instructions until the VM halts.
46
 // Run repeatedly executes instructions until the VM halts.
42
 func (vm *VirtualMachine) Run() {
47
 func (vm *VirtualMachine) Run() {
43
 	for !vm.Halted {
48
 	for !vm.Halted {
44
 		instruction := vm.Memory[vm.ip]
49
 		instruction := vm.Memory[vm.ip]
45
 		opcode := instruction % 100
50
 		opcode := instruction % 100
46
 
51
 
47
-		vm.opcodes[opcode](vm, func(pos int) int {
48
-			mode := (instruction / int(math.Pow10(2+pos))) % 10
49
-			switch mode {
50
-			case 0:
51
-				return vm.Memory[vm.Memory[vm.ip+1+pos]]
52
-			case 1:
53
-				return vm.Memory[vm.ip+1+pos]
54
-			default:
55
-				panic(fmt.Sprintf("Unknown parameter mode: %d", mode))
52
+		vm.modes = 0
53
+		mask := uint8(1)
54
+		for i := instruction / 100; i > 0; i /= 10 {
55
+			if i%10 == 1 {
56
+				vm.modes = vm.modes | mask
56
 			}
57
 			}
57
-		})
58
+			mask = mask << 1
59
+		}
60
+
61
+		vm.opcodes[opcode].(OpcodeFunc)(vm)
58
 	}
62
 	}
59
 	close(vm.Input)
63
 	close(vm.Input)
60
 	close(vm.Output)
64
 	close(vm.Output)
65
 	copy(vm.Memory, memory)
69
 	copy(vm.Memory, memory)
66
 	vm.ip = 0
70
 	vm.ip = 0
67
 	vm.Halted = false
71
 	vm.Halted = false
68
-	vm.Input = make(chan int, 100)
69
-	vm.Output = make(chan int, 100)
72
+	vm.Input = make(chan int, 1)
73
+	vm.Output = make(chan int, 1)
70
 }
74
 }

Loading…
Откажи
Сачувај