Skip to content

Commit

Permalink
perf(2024): parallelize day 7 for major speed improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
believer committed Dec 7, 2024
1 parent 07b2dbc commit a91d614
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 101 deletions.
35 changes: 18 additions & 17 deletions go/2024/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,29 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day

Using Go's built-in benchmarking with the [testing](https://pkg.go.dev/testing#hdr-Benchmarks) package. Computer is a 2021 MacBook Pro M1 Pro, 32 GB RAM.

| Day | #1 | #2 | Improvement\* |
| --- | --------------: | ---------------: | ------------------- |
| 1 | 116264 ns/op | 131233 ns/op | `3.53%` / `68.43%` |
| 2 | 310935 ns/op | 723512 ns/op | |
| 3 | 336448 ns/op | 785320 ns/op | - / `36.98%` |
| 4 | 523315 ns/op | 217584 ns/op | `81.73%` / `26.09%` |
| 5 | 778880 ns/op | 3129873 ns/op | `53.34%` / `81.91%` |
| 6 | 312461 ns/op | 1153391125 ns/op | |
| 7 | 110164692 ns/op | 7135839625 ns/op | `13.18%` / `29.52%` |
| Day | #1 | #2 | Improvement\* |
| --- | -------------: | ---------------: | ------------------- |
| 1 | 116264 ns/op | 131233 ns/op | `3.53%` / `68.43%` |
| 2 | 310935 ns/op | 723512 ns/op | |
| 3 | 336448 ns/op | 785320 ns/op | - / `36.98%` |
| 4 | 523315 ns/op | 217584 ns/op | `81.73%` / `26.09%` |
| 5 | 778880 ns/op | 3129873 ns/op | `53.34%` / `81.91%` |
| 6 | 312461 ns/op | 1153391125 ns/op | |
| 7 | 16480300 ns/op | 842853000 ns/op | `87.01%` / `91.67%` |

\* compared to first solution

### Previous solutions

| Day | #1 | #2 | Improvement | Link |
| --: | --------------: | ----------------: | -----------: | ------------------------------------------------------------------------------------------------------------------------------ |
| 1 | 120513 ns/op | 415683 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/47447cc17fffe6994d4b54c4cb815e698b3f5605/go/2024/puzzles/day01/main.go) |
| 1 | 120513 ns/op | 155479 ns/op | - / `62,59%` | [Link](https://github.com/believer/advent-of-code/blob/ea42592462771b74de87eae6bea9c0ca892a4499/go/2024/puzzles/day01/main.go) |
| 3 | 336448 ns/op | 1246155 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/461c2dd40039c27102aa1790c650decb79d4f549/go/2024/puzzles/day03/main.go) |
| 4 | 2864606 ns/op | 294413 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/99909bb30f82cda079471134452d886a0eb6266f/go/2024/puzzles/day04/main.go) |
| 5 | 1669175 ns/op | 17299190 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/1db858ae3d391319511787d8935c76eecdf6b22f/go/2024/puzzles/day05/main.go) |
| 7 | 126892714 ns/op | 10124683583 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/dd735747021ce43ca3a7427c529813139737271e/go/2024/puzzles/day07/main.go) |
| Day | #1 | #2 | Improvement | Link |
| --: | --------------: | ----------------: | ------------------: | ------------------------------------------------------------------------------------------------------------------------------ |
| 1 | 120513 ns/op | 415683 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/47447cc17fffe6994d4b54c4cb815e698b3f5605/go/2024/puzzles/day01/main.go) |
| 1 | 120513 ns/op | 155479 ns/op | - / `62,59%` | [Link](https://github.com/believer/advent-of-code/blob/ea42592462771b74de87eae6bea9c0ca892a4499/go/2024/puzzles/day01/main.go) |
| 3 | 336448 ns/op | 1246155 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/461c2dd40039c27102aa1790c650decb79d4f549/go/2024/puzzles/day03/main.go) |
| 4 | 2864606 ns/op | 294413 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/99909bb30f82cda079471134452d886a0eb6266f/go/2024/puzzles/day04/main.go) |
| 5 | 1669175 ns/op | 17299190 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/1db858ae3d391319511787d8935c76eecdf6b22f/go/2024/puzzles/day05/main.go) |
| 7 | 126892714 ns/op | 10124683583 ns/op | Baseline | [Link](https://github.com/believer/advent-of-code/blob/dd735747021ce43ca3a7427c529813139737271e/go/2024/puzzles/day07/main.go) |
| 7 | 110164692 ns/op | 7135839625 ns/op | `13.18%` / `29.52%` | [Link](https://github.com/believer/advent-of-code/blob/640d9604dfefa71f7bfef876750f378bd1a58a8b/go/2024/puzzles/day07/main.go) |

## Running

Expand Down
155 changes: 71 additions & 84 deletions go/2024/puzzles/day07/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,137 +4,124 @@ import (
"fmt"
"strconv"
"strings"
"sync"

"github.com/believer/aoc-2024/utils"
"github.com/believer/aoc-2024/utils/files"
)

// Parallelization in Go is so nice and easy and provided a major speed-up
// in this case.

// Also reducing the amount of type conversions in the permutation
// generation made some improvement. We used to give it a []string and get back []string.
// The strings needed to be broken down into individual parts for calculations, so we
// could instead return a [][]string directly from the permutations.
func main() {
fmt.Println("Part 1: ", part1("input.txt"))
fmt.Println("Part 2: ", part2("input.txt"))
}

func part1(name string) int {
lines := files.ReadLines(name)
calibrationResult := 0
operators := []string{"+", "*"}

expression:
for _, l := range lines {
result, valuesAsString, _ := strings.Cut(l, ":")
values := strings.Split(strings.TrimSpace(valuesAsString), " ")
testValue := utils.MustIntFromString(result)
return processLines(lines, operators)
}

permutations := allPermutations(values, operators)
func part2(name string) int {
lines := files.ReadLines(name)
operators := []string{"+", "*", "||"}

for _, expression := range permutations {
total := 0
operation := "+"
return processLines(lines, operators)
}

for _, v := range strings.Split(expression, " ") {
if v == "+" {
operation = "+"
continue
}
// Each line is an independent expression, we can calculate them in parallel
func processLines(lines, operators []string) int {
results := make(chan int, len(lines))

if v == "*" {
operation = "*"
continue
}
var wg sync.WaitGroup

if operation == "+" {
total += utils.MustIntFromString(v)
}
for _, line := range lines {
wg.Add(1)

if operation == "*" {
total *= utils.MustIntFromString(v)
}
}
go func(line string) {
defer wg.Done()
calibrationResult := evaluateLine(line, operators)
results <- calibrationResult
}(line)
}

if total == testValue {
calibrationResult += total
continue expression
}
}
go func() {
wg.Wait()
close(results)
}()

total := 0

for result := range results {
total += result
}

return calibrationResult
return total
}

func part2(name string) int {
lines := files.ReadLines(name)
calibrationResult := 0
operators := []string{"+", "*", "||"}
func evaluateLine(line string, operators []string) int {
resultAsString, valuesAsString, _ := strings.Cut(line, ":")
values := strings.Split(strings.TrimSpace(valuesAsString), " ")
result := utils.MustIntFromString(resultAsString)

expression:
for _, l := range lines {
result, valuesAsString, _ := strings.Cut(l, ":")
values := strings.Split(strings.TrimSpace(valuesAsString), " ")
testValue := utils.MustIntFromString(result)

permutations := allPermutations(values, operators)

for _, expression := range permutations {
total := 0
operation := "+"

for _, v := range strings.Split(expression, " ") {
if v == "+" {
operation = "+"
continue
}

if v == "*" {
operation = "*"
continue
}

if v == "||" {
operation = "||"
continue
}

if operation == "+" {
total += utils.MustIntFromString(v)
}

if operation == "*" {
total *= utils.MustIntFromString(v)
}

if operation == "||" {
total = utils.MustIntFromString(strconv.Itoa(total) + v)
}
permutations := allPermutations(values, operators)

for _, expression := range permutations {
total := 0
operation := "+"

for _, v := range expression {
switch v {
case "+", "*", "||":
operation = v
continue
}

if total == testValue {
calibrationResult += total
continue expression
num := utils.MustIntFromString(v)

switch operation {
case "+":
total += num
case "*":
total *= num
case "||":
total = utils.MustIntFromString(strconv.Itoa(total) + v)
}
}

if total == result {
return total
}
}

return calibrationResult
return 0
}

func allPermutations(nums []string, ops []string) []string {
func allPermutations(nums []string, ops []string) [][]string {
if len(nums) == 0 {
return nil
}

result := []string{}
generateExpressions(nums, ops, nums[0], 1, &result)
result := [][]string{}
generateExpressions(nums, ops, []string{nums[0]}, 1, &result)
return result
}

func generateExpressions(nums []string, ops []string, current string, index int, result *[]string) {
func generateExpressions(nums []string, ops []string, current []string, index int, result *[][]string) {
if index == len(nums) {
*result = append(*result, current)
return
}

for _, op := range ops {
next := current + " " + op + " " + nums[index]
next := append(current, op, nums[index])
generateExpressions(nums, ops, next, index+1, result)
}
}

0 comments on commit a91d614

Please sign in to comment.