I set myself a few goals to keep things interesting/challenging:
- No discussion of or reading about solutions before I consider this finished.
- Solve all problems.
- Solve all problems in under 12 ms total.
- Solve all problems in under 1 ms each.
- Solve all of a day's problems in under 1 ms combined.
- Solve all problems in under 1 ms combined.
Clearly the main goal was: make things as fast as possible. The total runtime is 897 μs 🎉.
I'm sure there's room for improvement in many places, but since I reached all my goals, and already spend way too much time on this, this will have to do for now.
Note
None of the implementations are tuned to my inputs. I.e. any input conforming to what is given as an example on the Advent of Code's website should work. Of course I can't test that, since I don't have access to other input files.
The runtimes below were measured using Google Benchmark. The setup used for these results was:
- Intel Core i5-12400
- 64 GiB PC3200 DDR4
- Ubuntu 24.04 LTS
- GCC 14.2.0
- Executed with
nice --adjustment=-10 - Using
mimalloc 2.1.2instead ofmalloc
Compilation was done in CMake's Release mode, with link-time optimization (but profile-guided optimization disabled, because it actually resulted in slower runtimes).
The runtimes were measured for each day individually. GCC clearly had an easier time optimizing multiple small binaries instead of a single big one that computed the solutions for all days.
Important
The measured times include parsing the input text, and solving the problem. They do not include reading the input text from a file or standard input.
╭────────────────────────────────────────────────────────────╮
│ Benchmark Time CPU Iterations │
╰────────────────────────────────────────────────────────────╯
day 01 - part 1 14.2 us 14.2 us 48453
day 01 - part 2 17.8 us 17.8 us 39272
day 02 - part 1 0.625 us 0.625 us 1072479
day 02 - part 2 1.12 us 1.12 us 626535
day 03 - part 1 1.92 us 1.92 us 365210
day 03 - part 2 4.80 us 4.80 us 145468
day 04 - part 1 1.85 us 1.85 us 375385
day 04 - part 2 90.9 us 90.8 us 7730
day 05 - part 1 15.3 us 15.3 us 43774
day 05 - part 2 3.29 us 3.29 us 212310
day 06 - part 1 5.22 us 5.22 us 132612
day 06 - part 2 5.29 us 5.28 us 132633
day 07 - part 1 0.754 us 0.753 us 898532
day 07 - part 2 7.31 us 7.31 us 96019
day 08 - part 1 262 us 262 us 2662
day 08 - part 2 174 us 174 us 4020
day 09 - part 1 5.49 us 5.48 us 121090
day 09 - part 2 52.3 us 52.3 us 13392
day 10 - part 1 19.2 us 19.2 us 36649
day 10 - part 2 193 us 193 us 3614
day 11 - part 1 5.44 us 5.42 us 121944
day 11 - part 2 9.77 us 9.77 us 70804
day 12 - part 1 5.59 us 5.59 us 124677
───────────────────────────────────────────────────────────────
Total 897 us 897 us
To build everything, the following is required at minimum:
- CMake >= 3.28
- GCC >= 14.2.0
For convenience, there's also a justfile included (which requires just to be installed). Just run just to see the available commands.
Here are a few common uses:
- Configure
Releasebuilds:just configure Release - Build & run the benchmark:
just run aoc25-all_days-benchmark - Build & verify day 3's solutions:
just run aoc25-day_03-verify
All binaries expect input data to be available in a folder inputs below the current working directory. Since the Advent of Code website explicitly requests not to share one's input data, you'll have to provide your own.
The files are expected to have the following naming format (in PCRE2 syntax): day_\d{2}-part_\d+\.txt. So, these are all valid filenames:
day_01-part_1.txtday_03-part_2.txt
This is not a valid filename: day_1-part_1.txt, because there's only one digit for the day.
All binaries will automatically scan for files matching this pattern, and run the solver for each of them.
The ...-verify binaries will also search for example files (pattern: day_\d{2}-part_\d+-example_\d+\.txt). For all files it finds, it expects a matching ...-solution.txt file to exist (i.e. day_\d{2}-part_\d+(?:-example_\d+)?-solution.txt). These solution files are used to check the output of the solver.
All output is done via spdlog, which means the default log levels can be overriden by setting the SPDLOG_LEVEL enviroment variable to one of [trace, debug, info, warning, or error].
Note that depending on the build type all log calls equal to and below a certain level are completely removed during compilation (i.e. not just their output disabled). For Debug builds all trace calls are removed, and for Release/RelWithDebInfo builds all debug (and lower) calls are removed. So e.g. setting SPDLOG_LEVEL=debug won't result in any debug-level logs in a Release build.