Speed up Day 12 by the power of MANAGING YOUR OWN MEMORY (#17)

This commit is contained in:
Patrick Stevens
2022-12-11 15:06:53 +00:00
committed by GitHub
parent d323984a12
commit e73c352fa7
2 changed files with 100 additions and 36 deletions

View File

@@ -41,15 +41,6 @@ Monkey 3:
let ``Part 1, given`` () =
Day11.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 10605
[<Test>]
let ``Part 1, single round, given`` () =
let monkeys = Day11.parse (StringSplitEnumerator.make '\n' input)
let inspections = Array.zeroCreate monkeys.Length
Day11.oneRoundDivThree monkeys inspections
inspections |> shouldEqual [| 2 ; 4 ; 3 ; 5 |]
[<Test>]
let ``Part 1`` () =
let input = Assembly.readResource "Day11.txt"
@@ -58,7 +49,7 @@ Monkey 3:
[<Test>]
let ``Part 2, given 1`` () =
let ``Part 2, given`` () =
Day11.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 2713310158L
[<Test>]

View File

@@ -2,10 +2,14 @@ namespace AdventOfCode2022
open System.Collections.Generic
open System
open Microsoft.FSharp.NativeInterop
#if DEBUG
open Checked
#endif
#nowarn "9"
[<Measure>]
type monkey
@@ -14,8 +18,6 @@ module Day11 =
type Monkey =
{
Number : int<monkey>
StartingItems : int64 ResizeArray
OperationIsPlus : bool
/// Negative is None
Argument : int64
@@ -24,9 +26,10 @@ module Day11 =
FalseCase : int<monkey>
}
let parse (lines : StringSplitEnumerator) : Monkey array =
let parse (memory : nativeptr<int64>) (lines : StringSplitEnumerator) : Monkey array * nativeptr<int64>[] * int[] =
use mutable enum = lines
let output = ResizeArray ()
let startingItems = ResizeArray ()
while enum.MoveNext () do
let monkey =
@@ -160,9 +163,9 @@ module Day11 =
if ifFalse = monkey then
failwith "assumption broken: throws to self"
startingItems.Add startItems
{
Number = monkey
StartingItems = startItems
OperationIsPlus = operationIsPlus
Argument = arg
TestDivisibleBy = test
@@ -171,14 +174,36 @@ module Day11 =
}
|> output.Add
output.ToArray ()
let totalItemCount = startingItems |> Seq.sumBy (fun x -> x.Count)
let oneRoundDivThree (monkeys : IReadOnlyList<Monkey>) (inspections : int64 array) =
let items =
Array.init
startingItems.Count
(fun i ->
for j in 0 .. startingItems.[i].Count - 1 do
NativePtr.write (NativePtr.add memory (totalItemCount * i + j)) startingItems.[i].[j]
NativePtr.add memory (totalItemCount * i)
)
let counts = Array.init startingItems.Count (fun i -> startingItems.[i].Count)
output.ToArray (), items, counts
let oneRoundDivThree
(monkeys : IReadOnlyList<Monkey>)
(items : nativeptr<int64>[])
(counts : nativeptr<int>)
(inspections : int64 array)
=
for i in 0 .. monkeys.Count - 1 do
let monkey = monkeys.[i]
inspections.[i] <- inspections.[i] + int64 monkey.StartingItems.Count
let countsI = NativePtr.get counts i
inspections.[i] <- inspections.[i] + int64 countsI
for worryIndex in 0 .. countsI - 1 do
let worry = NativePtr.get items.[i] worryIndex
for worry in monkey.StartingItems do
let newWorry =
let arg =
match monkey.Argument with
@@ -195,28 +220,55 @@ module Day11 =
else
monkey.FalseCase
monkeys.[target / 1<monkey>].StartingItems.Add newWorry
let target = target / 1<monkey>
let targetCount = NativePtr.get counts target
NativePtr.write (NativePtr.add items.[target] targetCount) newWorry
NativePtr.write (NativePtr.add counts target) (targetCount + 1)
monkey.StartingItems.Clear ()
NativePtr.write (NativePtr.add counts i) 0
let inline maxTwo (arr : int64 array) : struct (int64 * int64) =
let mutable best = max arr.[1] arr.[0]
let mutable secondBest = min arr.[1] arr.[0]
for i in 2 .. arr.Length - 1 do
if arr.[i] > best then
secondBest <- best
best <- arr.[i]
elif arr.[i] > secondBest then
secondBest <- arr.[i]
struct (secondBest, best)
let part1 (lines : StringSplitEnumerator) : int64 =
let monkeys = parse lines
let memory = NativePtr.stackalloc<int64> 1000
let monkeys, items, counts = parse memory lines
use counts = fixed counts
let mutable inspections = Array.zeroCreate<int64> monkeys.Length
for _round in 1..20 do
oneRoundDivThree monkeys inspections
oneRoundDivThree monkeys items counts inspections
inspections |> Array.sortInPlace
inspections.[inspections.Length - 1] * inspections.[inspections.Length - 2]
let struct (a, b) = maxTwo inspections
let inline oneRound (modulus : int64) (monkeys : Monkey array) (inspections : int64 array) =
a * b
let oneRound
(modulus : int64)
(monkeys : Monkey array)
(items : nativeptr<nativeptr<int64>>)
(counts : nativeptr<int>)
(inspections : nativeptr<int64>)
=
for i in 0 .. monkeys.Length - 1 do
let monkey = monkeys.[i]
inspections.[i] <- inspections.[i] + int64 monkey.StartingItems.Count
let entry = NativePtr.add inspections i
let countI = NativePtr.get counts i
NativePtr.write entry (NativePtr.read entry + int64 countI)
for worryIndex in 0 .. monkey.StartingItems.Count - 1 do
let worry = monkey.StartingItems.[worryIndex]
for worryIndex in 0 .. countI - 1 do
let worry = NativePtr.get (NativePtr.get items i) worryIndex
let newWorry =
let arg =
@@ -234,21 +286,42 @@ module Day11 =
else
monkey.FalseCase
monkeys.[target / 1<monkey>].StartingItems.Add newWorry
let target = target / 1<monkey>
NativePtr.write (NativePtr.add (NativePtr.get items target) (NativePtr.get counts target)) newWorry
NativePtr.write (NativePtr.add counts target) (NativePtr.get counts target + 1)
monkey.StartingItems.Clear ()
NativePtr.write (NativePtr.add counts i) 0
let inline unsafeMaxTwo (len : int) (arr : nativeptr<int64>) : struct (int64 * int64) =
let arr0 = NativePtr.read arr
let arr1 = NativePtr.get arr 1
let mutable best = max arr0 arr1
let mutable secondBest = min arr0 arr1
for i in 2 .. len - 1 do
let arrI = NativePtr.get arr i
if arrI > best then
secondBest <- best
best <- arrI
elif arrI > secondBest then
secondBest <- arrI
struct (secondBest, best)
let part2 (lines : StringSplitEnumerator) : int64 =
let monkeys = parse lines
let memory = NativePtr.stackalloc<int64> 1000
let monkeys, items, counts = parse memory lines
use counts = fixed counts
use items = fixed items
let mutable inspections = Array.zeroCreate<int64> monkeys.Length
let inspections = NativePtr.stackalloc<int64> monkeys.Length
let modulus =
(1L, monkeys) ||> Seq.fold (fun i monkey -> i * monkey.TestDivisibleBy)
for _round in 1..10000 do
oneRound modulus monkeys inspections
oneRound modulus monkeys items counts inspections
inspections |> Array.sortInPlace
inspections.[inspections.Length - 1] * inspections.[inspections.Length - 2]
let struct (a, b) = unsafeMaxTwo monkeys.Length inspections
a * b