Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatization for fetching inputs, use nightly, logic simplifications #1

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BENCHMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mojo 24.5.0 (e8aacb95)
```

```
Task Python Pypy3 Mojo parallel * speedups
Day01 part1 0.74 ms 0.15 ms 7.70 μs 2.95 μs * 49 - 252
Day01 part2 3.03 ms 0.33 ms 0.05 ms 7.11 μs * 46 - 426
Day02 part1 0.32 ms 0.11 ms 0.11 ms 0.02 ms * 4 - 12
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ The final newline should be stripped from the inputs (sorry about this, I actual

Only the standard library is required for Mojo solutions. Python solutions may require numpy.

You can download all the inputs for all days with the `get-inputs.sh` script. For this to work you need to have cookies.txt in the current directory, which you can get by inspecting your session cookie in the browser and saving it. There is a `cookies.txt.template` file for this purpose so you can:

```
$ cp cookies.txt.template cookies.txt
```

And add your session cookie to the file, and then run the script:

```
$ ./get-inputs.sh
```

Benchmarking
============

Expand All @@ -37,7 +49,7 @@ This will create temporary `all_{python3|pypy3|mojo}_{pc|mac}.txt` files. Only P
Visualisations
==============

The vis/ directory contains visualisations code - written in Pythin, with PyGame. You can run each one directly:
The vis/ directory contains visualisations code - written in Python, with PyGame. You can run each one directly:


```
Expand Down
6 changes: 3 additions & 3 deletions array.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct Array[AType: DType](CollectionElement):
fn fill(inout self, value: SIMD[AType, 1] = 0):
initializer = SIMD[AType, Self.simd_width](value)
for i in range((self.size + Self.simd_width - 1) // Self.simd_width):
self.data.store[width=Self.simd_width](i * Self.simd_width, initializer)
self.data.store(i * Self.simd_width, initializer)

fn swap(inout self, inout other: Self):
(self.data, other.data) = (other.data, self.data)
Expand All @@ -54,10 +54,10 @@ struct Array[AType: DType](CollectionElement):
return self.data.load[width=width](ofs)

fn store[width: Int, T: IntLike](self, ofs: T, val: SIMD[AType, width]):
self.data.store[width=width](ofs, val)
self.data.store(ofs, val)

fn load[T: IntLike](self, ofs: T) -> SIMD[AType, 1]:
return self.data.load[width=1](ofs)

fn store[T: IntLike](self, ofs: T, val: SIMD[AType, 1]):
self.data.store[width=1](ofs, val)
self.data.store(ofs, val)
3 changes: 3 additions & 0 deletions cookies.txt.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

# Netscape HTTP Cookie File
.adventofcode.com TRUE / FALSE 0 session YOUR_SESSION_COOKIE
2 changes: 1 addition & 1 deletion day00.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser

struct Solution:
var parse: Parser
Expand Down
35 changes: 15 additions & 20 deletions day01.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from os.atomic import Atomic
from collections import List
from wrappers import run_multiline_task
Expand All @@ -23,24 +23,19 @@ struct MultiMatcher:
var pfx: List[Int32]
var msk: List[Int32]

fn __init__(inout self, words: VariadicList[StringLiteral]):
fn __init__(inout self, words: List[String]) raises:
self.fcv = List[UInt8]()
self.pfx = List[Int32]()
self.msk = List[Int32]()
for i in range(len(words)):
self.add(words[i])
for w in words:
self.add(w[])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - I fixed the language problems when upgrading to 24.5 but never got around to removing these relics.


fn add(inout self, s: String):
l = len(s)
# s._buffer[l - 1] == ord(s[i]); but works faster it seems
# Not sure if accessing _buffer is discouraged? Probably will break one day.
self.fcv.append(s._buffer[l - 1])
fn add(inout self, s: String) raises:
self.fcv.append(ord(s[-1]))
var r: Int32 = 0
var m: Int32 = 0
# While iterators are supported in Mojo, none of the standard library
# types implement them, have to use range(), which does work.
for i in range(l - 1):
r = (r << 8) + int(s._buffer[i])
for c in s[:-1]:
r = (r << 8) + ord(c)
m = (m << 8) + 0xFF
self.pfx.append(r)
self.msk.append(m)
Expand Down Expand Up @@ -89,11 +84,11 @@ fn main() raises:
a1 += lsum

# Construct matchers for all words. When looking backwards, the words have to be reversed.
# Fun fact - VariadicList apparently can hold literals, but cannot hold Strings.
# Fun fact - List apparently can hold literals, but cannot hold Strings.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is no longer true (used to be way back when) -> please remove comment if migrating to List<> works now

# Variadic since other list variants only make sense in some very specific contexts
# like when you only use predetermined list sizes and don't iterate over the list.
m = MultiMatcher(VariadicList[StringLiteral]("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"))
r = MultiMatcher(VariadicList[StringLiteral]("orez", "eno", "owt", "eerht", "ruof", "evif", "xis", "neves", "thgie", "enin"))
m = MultiMatcher(List[String]("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"))
r = MultiMatcher(List[String]("orez", "eno", "owt", "eerht", "ruof", "evif", "xis", "neves", "thgie", "enin"))

# Similar to the part 1, this does the digits checks and also uses the multi-matchers
# to find words.
Expand All @@ -106,8 +101,8 @@ fn main() raises:
var d2 = 0
# last four characters code
var l4: Int32 = 0
for i in range(s.size):
c = int(s[i])
for c_ref in s.as_bytes_span():
c = int(c_ref[])
var d = -1
if c >= zero and c <= nine:
d = c - zero
Expand All @@ -119,8 +114,8 @@ fn main() raises:
d1 = d
break
l4 = 0
for i in range(s.size - 1, -1, -1):
c = int(s[i])
for c_ref in s.as_bytes_span()[::-1]:
c = int(c_ref[])
var d = -1
if c >= zero and c <= nine:
d = c - zero
Expand Down
50 changes: 18 additions & 32 deletions day02.mojo
Original file line number Diff line number Diff line change
@@ -1,40 +1,26 @@
from parser import *
from parser import make_parser
from collections import Counter
from os.atomic import Atomic
from wrappers import run_multiline_task

# Replaces ord('a')
alias ord_a = ord("a")


fn maxdict(s: StringSlice) raises -> Tuple[Int, Int, Int]:
fn maxballs(line: String) raises -> Counter[String]:
"""
Parse a single line and return a dictionary with maximum values for each ball color
across all the draws. Internally uses hierarchical parsing to split off the header,
split draws, and then split colors.
"""
# Skip header. Game IDs are sequential, anyway.
alias cOlon = ord(":")
alias cR = ord("r")
alias cG = ord("g")
start = s.find(cOlon) + 2 # ':'
# Top-level parser for draws - semicolon separated
draws = make_parser[";"](s[start:])
red = green = blue = 0
for d in range(draws.length()):
# Secondary level parser for comma-separated colors
colors = make_parser[","](draws.get(d))
for b in range(colors.length()):
# split color name and value
tok = make_parser[" "](colors.get(b))
v = int(atoi(tok.get(0)))
col = tok.get(1)
if col[0] == cR:
red = max(red, v)
elif col[0] == cG:
green = max(green, v)
else:
blue = max(blue, v)
return (red, green, blue)
var games = line.split(": ")[1].split("; ")
counter = Counter[String]()
for game_ref in games:
game = game_ref[]
game = game.replace(",", "")
toks = game.split(" ")
for i in range(len(toks) // 2):
cnt = int(toks[i * 2])
col = toks[i * 2 + 1]
counter[col] = max(counter[col], cnt)
return counter
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I appreciate the direction, this repo's point is to build fast solutions. While it's nice that Mojo now has some collection builtins, split() was there before and I'm not using it since it's abysmally slow.

This task is a perfect example of that problem - with this approach it's about 8 times slower than the 'hacky but fast' solution was (which was on par with PyPy), and it's now ~2.5 times slower than Python.

The hand-crafted parsers that work over raw memory are not really easy (or efficient) in Python, so this is still some way in which I was able to squeeze some performance of Mojo. OTOH sure, this approach shows performance gaps in the std library. Perhaps add this code as a _slow variant of this solution to highlight this, but keep this version as-is (or modified to adjust for some QoL improvements in recent Mojo as in other bits.



fn main() raises:
Expand All @@ -48,8 +34,8 @@ fn main() raises:
@parameter
fn step1(l: Int):
try:
(r, g, b) = maxdict(lines.get(l))
if r <= 12 and g <= 13 and b <= 14:
var colors = maxballs(str(lines.get(l)))
if colors["red"] <= 12 and colors["green"] <= 13 and colors["blue"] <= 14:
sum1 += l + 1
except:
pass
Expand All @@ -58,8 +44,8 @@ fn main() raises:
@parameter
fn step2(l: Int):
try:
(r, g, b) = maxdict(lines.get(l))
sum2 += r * g * b
var colors = maxballs(str(lines.get(l)))
sum2 += colors["red"] * colors["green"] * colors["blue"]
except:
pass

Expand Down
2 changes: 1 addition & 1 deletion day03.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from collections import List
from wrappers import minibench
from array import Array
Expand Down
11 changes: 9 additions & 2 deletions day03.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ def find_nums():
r = 0
q = 0
for x in range(dimx):
c = ord(lines[y][x])
try:
c = ord(lines[y][x])
except IndexError:
c = 0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is probably more of an effect of assumptions on what is contained in lines and how they are loaded - the code in Python is rather simple and does not do the trailing newline check. Rather than a try/catch here you could fix this by removing the last element of lines[] after it's loaded, if it's empty.

if c >= 48 and c <= 57:
d = c - 48
r = r * 10 + d
Expand Down Expand Up @@ -56,7 +59,11 @@ def part2():
ly = min(y + 1, dimy - 1)
for gy in range(sy, ly + 1):
for gx in range(sx, lx + 1):
if lines[gy][gx] == "*":
try:
c = lines[gy][gx]
except IndexError:
c = ''
if c == "*":
gk = gy * dimx + gx
if gc[gk] != 0:
s2 += gc[gk] * v
Expand Down
2 changes: 1 addition & 1 deletion day04.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from array import Array
from wrappers import minibench
from bit import pop_count
Expand Down
2 changes: 1 addition & 1 deletion day05.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from collections import List
from wrappers import minibench

Expand Down
2 changes: 1 addition & 1 deletion day06.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from math import sqrt
from wrappers import minibench

Expand Down
2 changes: 1 addition & 1 deletion day07.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from math import sqrt
from wrappers import minibench
from quicksort import qsort
Expand Down
2 changes: 1 addition & 1 deletion day08.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from algorithm import parallelize
from memory import memset
Expand Down
2 changes: 1 addition & 1 deletion day09.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from memory import memset
from array import Array
Expand Down
2 changes: 1 addition & 1 deletion day09sym.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
2 changes: 1 addition & 1 deletion day10.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from algorithm import parallelize
from wrappers import minibench
from array import Array
Expand Down
2 changes: 1 addition & 1 deletion day11.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
2 changes: 1 addition & 1 deletion day11hv.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
2 changes: 1 addition & 1 deletion day12.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import run_multiline_task
from array import Array
from memory import memcpy, memset
Expand Down
2 changes: 1 addition & 1 deletion day13.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array
from bit import pop_count
Expand Down
2 changes: 1 addition & 1 deletion day14.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array
from collections import List
Expand Down
2 changes: 1 addition & 1 deletion day15.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
2 changes: 1 addition & 1 deletion day16.mojo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from algorithm import parallelize
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array
from os.atomic import Atomic
Expand Down
2 changes: 1 addition & 1 deletion day17.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array
from math import sqrt
Expand Down
2 changes: 1 addition & 1 deletion day18.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
2 changes: 1 addition & 1 deletion day19.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
2 changes: 1 addition & 1 deletion day20.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
2 changes: 1 addition & 1 deletion day21.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array
from utils.loop import unroll
Expand Down
2 changes: 1 addition & 1 deletion day22.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array
from array import Array
Expand Down
2 changes: 1 addition & 1 deletion day23.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from algorithm import parallelize
from wrappers import minibench
from array import Array
Expand Down
2 changes: 1 addition & 1 deletion day24.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array
from algorithm import parallelize
Expand Down
2 changes: 1 addition & 1 deletion day25.mojo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from parser import *
from parser import make_parser
from wrappers import minibench
from array import Array

Expand Down
Loading