chore: add multi-worker version of day 5 #2

Merged
xuu merged 1 commits from day05 into main 2023-12-05 15:18:48 -07:00
2 changed files with 87 additions and 32 deletions

View File

@ -3,7 +3,6 @@ package main
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"log/slog"
"os" "os"
"sort" "sort"
"strconv" "strconv"
@ -11,11 +10,6 @@ import (
) )
func main() { func main() {
var level slog.Level
if err := level.UnmarshalText([]byte(os.Getenv("DEBUG_LEVEL"))); err == nil && level != 0 {
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level})))
}
if len(os.Args) != 2 { if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: day05 FILE") fmt.Fprintln(os.Stderr, "Usage: day05 FILE")
} }
@ -31,21 +25,24 @@ func main() {
fmt.Println("min location:", minLocation) fmt.Println("min location:", minLocation)
fmt.Println("min range location:", minRangeLocation) fmt.Println("min range location:", minRangeLocation)
} }
func run(scan *bufio.Scanner) (int, int) { func run(scan *bufio.Scanner) (int, int) {
log("begin...")
var seeds []int var seeds []int
var seedRanges []int var seedRanges [][2]int
lookup := map[string]*Lookup{} lookup := map[string]*Lookup{}
for scan.Scan() { for scan.Scan() {
text := scan.Text() text := scan.Text()
if strings.HasPrefix(text, "seeds:") && len(seeds) == 0 { if strings.HasPrefix(text, "seeds:") && len(seeds) == 0 {
seeds, seedRanges = readSeeds(text) seeds, seedRanges = readSeeds(text)
log("seeds", len(seeds), "ranges", len(seedRanges))
} }
lookup = readMaps(scan) lookup = readMaps(scan)
log("lookups", len(lookup))
} }
find := NewFinder( find := NewFinder(
@ -58,38 +55,21 @@ func run(scan *bufio.Scanner) (int, int) {
lookup["humidity-to-location"], lookup["humidity-to-location"],
) )
seedLocations := make([]int, len(seeds)) return findMinLocation(seeds, find), FindMinRangeLocationMulti(seedRanges, find)
for i, s := range seeds {
seedLocations[i] = find.Find(s)
}
minLocation := min(seedLocations...)
seedRangeLocations := make([]int, len(seedRanges))
for i, s := range seedRanges {
seedRangeLocations[i] = find.Find(s)
}
minRangeLocation := min(seedRangeLocations...)
return minLocation, minRangeLocation
} }
func readSeeds(text string) ([]int, [][2]int) {
func readSeeds(text string) ([]int, []int) { var seeds [] int
var seeds, seedRanges []int var seedRanges [][2]int
sp := strings.Fields(strings.TrimPrefix(text, "seeds: ")) sp := strings.Fields(strings.TrimPrefix(text, "seeds: "))
for i, s := range sp { for i, s := range sp {
n, _ := strconv.Atoi(s) n, _ := strconv.Atoi(s)
seeds = append(seeds, n) seeds = append(seeds, n)
if i%2 == 0 { if i%2 == 0 {
seedRanges = append(seedRanges, n) seedRanges = append(seedRanges, [2]int{n, 0})
} else { } else {
lastN := seedRanges[len(seedRanges)-1] seedRanges[len(seedRanges)-1][1] = n
r := make([]int, n-1)
for i := range r {
r[i] = lastN + i + 1
}
seedRanges = append(seedRanges, r...)
} }
} }
return seeds, seedRanges return seeds, seedRanges
@ -123,6 +103,74 @@ func readMaps(scan *bufio.Scanner) map[string]*Lookup {
return lookup return lookup
} }
func findMinLocation(seeds []int, find *Finder) int {
seedLocations := make([]int, len(seeds))
for i, s := range seeds {
seedLocations[i] = find.Find(s)
}
return min(seedLocations...)
}
func FindMinRangeLocation(ranges [][2]int, find *Finder) int {
results := 0
for _, r := range ranges {
results += r[1]
}
seedLocations := make([]int, 0, results)
for _, s := range ranges {
for i := 0; i < s[1]; i++ {
seedLocations = append(seedLocations, find.Find(s[0] + i))
}
}
return min(seedLocations...)
}
func FindMinRangeLocationMulti(ranges [][2]int, find *Finder) int {
worker := func(id int, jobs <-chan [2]int, results chan<- []int) {
for s := range jobs {
res := make([]int, s[1])
for i := 0; i < s[1]; i++ {
res[i] = find.Find(s[0] + i)
}
results <- res
}
}
numWorkers := 16
jobsCh := make(chan [2]int, numWorkers)
resultsCh := make(chan []int, len(ranges))
for w := 0; w < numWorkers; w++ {
go worker(w, jobsCh, resultsCh)
}
log("started workers", numWorkers)
go func() {
for i, s := range ranges {
log("job", i, "send", s)
jobsCh <- s
}
close(jobsCh)
}()
results := 0
for _, r := range ranges {
results += r[1]
}
log("expecting results", results)
seedLocations := make([]int, 0, results)
expectResults := make([]struct{}, len(ranges))
for range expectResults {
r := <- resultsCh
seedLocations = append(seedLocations, r...)
}
return min(seedLocations...)
}
type Range struct { type Range struct {
src int src int
dest int dest int
@ -179,6 +227,9 @@ func (f *Finder) Find(n int) int {
} }
func min(arr ...int) int { func min(arr ...int) int {
if len(arr) == 0 {
return 0
}
m := arr[0] m := arr[0]
for _, a := range arr[1:] { for _, a := range arr[1:] {
if m > a { if m > a {
@ -187,3 +238,7 @@ func min(arr ...int) int {
} }
return m return m
} }
func log(v ...any) {
fmt.Fprintln(os.Stderr, v...)
}

View File

@ -22,7 +22,7 @@ func TestExample(t *testing.T) {
minLocation, minRangeLocation := run(scan) minLocation, minRangeLocation := run(scan)
is.Equal(minLocation, 35) is.Equal(minLocation, 35)
is.Equal(minRangeLocation, 47) is.Equal(minRangeLocation, 46)
} }
func SkipTestSolution(t *testing.T) { func SkipTestSolution(t *testing.T) {