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

Further performance improvements

master
Chris Smith пре 5 година
родитељ
комит
6e97ab6839
1 измењених фајлова са 37 додато и 18 уклоњено
  1. 37
    18
      day09.nim

+ 37
- 18
day09.nim Прегледај датотеку

1
 import strscans
1
 import strscans
2
 
2
 
3
+# For performance, we only use pointers to Marbles (and manually allocate)
4
+# memory required for them. This stops the GC tracking them, saving a fair
5
+# chunk of time. Removed marbles will leak, but it doesn't really matter for
6
+# the scope of this program.
7
+
3
 type
8
 type
4
-    Marble = object
5
-        next, prev: ptr Marble
9
+    Marble = ptr object
10
+        next: Marble
6
         value: int32
11
         value: int32
7
 
12
 
8
-proc insertAfter(node: ptr Marble, value: int) {.inline.} =
9
-    var newNode = cast[ptr Marble](alloc0(sizeof(Marble)))
13
+proc insertAfter(node: Marble, value: int) {.inline.} =
14
+    var newNode = cast[Marble](alloc0(sizeof(Marble)))
10
     newNode.value = cast[int32](value)
15
     newNode.value = cast[int32](value)
11
     newNode.next = node.next
16
     newNode.next = node.next
12
-    newNode.prev = node
13
-    newNode.next.prev = newNode
14
-    newNode.prev.next = newNode
17
+    node.next = newNode
15
 
18
 
16
-proc remove(node: ptr Marble) {.inline.} =
17
-    node.prev.next = node.next
18
-    node.next.prev = node.prev
19
+proc removeNext(node: Marble) {.inline.} =
20
+    node.next = node.next.next
19
 
21
 
20
-proc newSingleNode(value: int): ptr Marble =
21
-    result = cast[ptr Marble](alloc0(sizeof(Marble)))
22
+proc newSingleNode(value: int): Marble =
23
+    result = cast[Marble](alloc0(sizeof(Marble)))
22
     result.value = cast[int32](value)
24
     result.value = cast[int32](value)
23
     result.next = result
25
     result.next = result
24
-    result.prev = result
25
 
26
 
26
 var
27
 var
27
     input = readFile("data/09.txt")
28
     input = readFile("data/09.txt")
31
 if not input.scanf("$i players; last marble is worth $i points", players, marbles):
32
 if not input.scanf("$i players; last marble is worth $i points", players, marbles):
32
     raise newException(Defect,  "Invalid input line: " & input)
33
     raise newException(Defect,  "Invalid input line: " & input)
33
 
34
 
35
+# Instead of using a doubly-linked list, we keep a current pointer and a
36
+# separate one trailing behind it. The trail will expand to 8 marbles and is
37
+# used when a multiple of 23 is played (so we can remove the N-7th marble). The
38
+# trail then catches up to the current pointer and drifts back to 8 again over
39
+# the next few moves. This saves a 64 bit memory allocation per marble, which
40
+# gives a small but noticable speed bump.
41
+
34
 var
42
 var
35
     player = 0
43
     player = 0
36
     scores = newSeq[int](players)
44
     scores = newSeq[int](players)
37
     current = newSingleNode(0)
45
     current = newSingleNode(0)
46
+    currentTrail = current
47
+    currentTrailDrift = 0
38
     specialCountdown = 23
48
     specialCountdown = 23
39
     hundredMarbles = marbles * 100
49
     hundredMarbles = marbles * 100
40
 
50
 
41
 for i in 1 .. hundredMarbles:
51
 for i in 1 .. hundredMarbles:
52
+    # Instead of testing each marble number to see if it's a multiple of 23, we
53
+    # keep a counter that we just loop over and over again.
42
     specialCountdown.dec
54
     specialCountdown.dec
43
     if specialCountdown == 0:
55
     if specialCountdown == 0:
44
-        specialCountdown = 23
56
+        # The current player is only relevant when a 23nth marble is played, so
57
+        # we can just update the player here instead of every turn.
45
         player = (player + 23) mod players
58
         player = (player + 23) mod players
46
-        current = current.prev.prev.prev.prev.prev.prev.prev
47
-        scores[player] += i + current.value
48
-        current.remove
49
-        current = current.next
59
+        scores[player] += i + currentTrail.next.value
60
+        currentTrail.removeNext
61
+        current = currentTrail.next
62
+        currentTrail = current
63
+        currentTrailDrift = 0
64
+        specialCountdown = 23
50
     else:
65
     else:
51
         current.next.insertAfter(i)
66
         current.next.insertAfter(i)
52
         current = current.next.next
67
         current = current.next.next
68
+        if currentTrailDrift == 8:
69
+            currentTrail = currentTrail.next.next
70
+        else:
71
+            currentTrailDrift += 2
53
 
72
 
54
     if i == marbles or i == hundredMarbles:
73
     if i == marbles or i == hundredMarbles:
55
         echo scores.max
74
         echo scores.max