Skip to content
◆ Speed Demon

32,050 souls, one per millisecond of the reference implementation / 32050

Speed Demon

Every millisecond is a soul.

Same task, same data, same machine. We run your code ourselves and rank what comes back.

72-hour performance championship · Halloween weekend

track A · ingest · 3.1M log lines · 239 MB

fsn1 · i5-13500 · 12 threads · 8 GB cap

reference implementation

32.05 s · 228 MB

souls 32,050 souls

what a serious submission looks like

1.28 s · 40 MB

souls 1,280 souls

✓ sha256 · identical output

25.0× faster · 5.7× leaner · illustrative efficiency coefficient K = 6.41

The reference implementation takes 25.0 times longer than a strong submission on the same task and the same machine.

00 before the freeze

Changes before freeze (Oct 26)

These rules changed ahead of the October 28 freeze. The rest of this page already reflects every line below.

  1. 01

    Four tracks, A to D. Track E (Compress) is cut: storing the input raw outscored a real codec.

  2. 02

    Track B is timed as one total wall time, and its queries are readable from the start. The 40/60 build/query split is gone.

  3. 03

    One naive Python reference per track, instead of Go, Python and Rust.

  4. 04

    The scoring machine is in Falkenstein (fsn1), not Helsinki: the same Intel Core i5-13500, the same 64 GB and the same limits.

  5. 05

    New formula: K = (T_ref/T)^0.50 × (M_ref/M)^0.20 × (S_ref/S)^0.15 × (D_ref/D)^0.15. The scaling axis X is gone, and its weight moved to wall time.

  6. 06

    Wall time has no cap. Memory is capped at 50×. Source size and dependencies can cost you but never pay.

  7. 07

    Source size is code lines counted by scc after your formatter, across the whole repo minus registry packages first published before Oct 1, 2026 by people outside the team. Thanks to a participant report, this closes the library-as-dependency loophole.

  8. 08

    D = 1 + the number of direct non-stdlib runtime dependencies in your manifest. The vendored-bytes term is gone.

  9. 09

    Speed score = 50 × log10(K), clamped to 0 to 100: K = 10 is half marks and K = 100 is full marks.

  10. 10

    The speed floor gate is gone. Three gates remain: correctness, the timeout and the sandbox.

  11. 11

    The overall title is the geometric mean of your best three track totals, from three or more tracks entered.

  12. 12

    Prizes total $1,800: $800, $400 and $200 overall, $200 for two write-ups, and $50 for the highest K on each track. The lean award is gone.

  13. 13

    Judges see each team's live-board run history, and PERF.md steps cite run IDs. Gate pass/fail comes out by Nov 4; final scores and full breakdowns come with the winners on Nov 13.

  14. 14

    Every FROM must name an image from speeddemon.dev/allowlist.json, pinned by digest, and nothing else may enter the image: no COPY --from another image, no ADD of a URL. Builds stay offline and are capped at 10 minutes, images at 2 GB.

  15. 15

    To use the live board, install the Speed Demon Board GitHub App on your repo (https://github.com/apps/speed-demon-board). It reads only the repos you pick, and repos can stay private until code freeze.

  16. 16

    Build-time training (PGO, a JVM AOT cache, native-image) is allowed on generator output. /data is not mounted at build time, and a byte-identical copy of our generator in your repo costs nothing in source size.

  17. 17

    io_uring stays blocked by Docker's default seccomp profile. mmap works.

  18. 18

    The live board runs on the full public dataset released at kickoff, on the scoring machine itself. It closes at code freeze, so final scoring has the machine to itself, on a holdout seed nobody has seen.

  19. 19

    Track D's hints now match its data: queries are local, so parsing and memory layout decide most of the time.

  20. 20

    The Oct 31 teardown of the reference moves into the results write-up.

  21. 21

    The worked example is recomputed under the new formula and marked illustrative.

  22. 22

    Reference numbers are now measured on the scoring machine, five runs each: Track A 32.05 s, B 56.35 s, C 14.44 s, D 72.69 s. Timeouts are 10 times those.

01 the premise

You can't argue with a stopwatch

Most hackathons ask a panel whether they liked your idea. This one starts by measuring whether your code is fast, and only then asks whether it's any good.

Every track hands you the same three things: a task specification, a dataset, and a deliberately naive reference implementation that solves the task correctly and slowly. Your job is to solve it again — correctly and not slowly. Any language. Any approach. The output has to hash identical to the reference, byte for byte, or you don't get a score at all.

Then we run it. Not you. Every submission is built from your Dockerfile and executed on one dedicated machine in Falkenstein, pinned to the same cores, capped at the same memory, five runs each, serialized so nothing competes for cache. Your laptop's numbers are for your own iteration. Ours are the ones on the board.

What comes out is an efficiency coefficient: a single number that folds wall time, peak memory, source size and dependency weight into one ranking. It is published in full below, with a worked example, before you write a line. That coefficient decides 65% of your score. Human judges decide the rest, because a submission nobody can read is not an engineering achievement.

There is a particular kind of engineer this event is built for: the one who has spent a week making something 40% faster and then had to explain to a room why that mattered. Here it doesn't need explaining. It's the score.

the shape of it

  • task specgiven
  • datasetgiven
  • naive referencegiven
  • a faster oneyours

65%

of your score is measured by machine

on a formula published before kickoff

5

runs per submission

highest and lowest discarded, mean of the middle three

1

machine runs every submission

cores pinned, turbo off, serialized

0

self-reported timings accepted

ever

02 A through D

Four tracks

Each track is one fixed task against one fixed dataset. They stress different muscles on purpose — a parsing specialist and a graph specialist should not be optimising the same loop. Enter as many as you like; three is the minimum to be eligible for the overall title, which counts your best three.

A · The parsing track

Ingest

Aggregate 3.1M structured log lines into per-endpoint request counts, p50/p95/p99 latency, and error rates bucketed by hour.

The parsing track, and the one most teams should start with.

The naive version reads lines, splits on delimiters, parses timestamps with a date library, and pushes latencies into a growing list per endpoint before sorting each one at the end. Every part of that is a target.

  • Custom line parsing beats any general-purpose reader, by a lot
  • Percentiles without sorting the whole thing
  • Where most teams discover their allocator
  • Timestamp parsing is usually the second-biggest cost and nobody expects it

239 MB records 3.1M lines reference 32.05 s timeout 321 s

live data preview

space-delimited, one request per line

field type note
ts RFC3339 microsecond precision, monotonic per shard but interleaved across shards
method enum 7 values
endpoint string 1,842 distinct, heavy Zipf tail
status uint16 5xx counts toward the error rate
latency_us uint32 microseconds, not milliseconds

sample rows

ts method endpoint status latency_us
2026-10-30T18:07:18.569411Z GET /api/v2/products 200 8177
2026-10-30T18:07:21.136274Z GET /beta/regions 503 81573
2026-10-30T18:07:27.963350Z PUT /v2/ab_tests 200 6381

Three consecutive lines from the sample set, unmodified.

B · The trade-off track

Index

Answer 50,000 boolean and phrase queries over a 250 MB text corpus.

Timed as one total wall time, build and queries together. The queries are readable from the start, and the task is to answer these 50,000.

Two phases means two optimisation problems that pull against each other: an index that builds fast is rarely one that queries fast. Choosing where to spend is the whole track.

  • Posting-list compression actually pays at this corpus size
  • Intersection order is most of the win on boolean queries
  • Phrase queries need positions, and positions are where your memory goes
  • The queries are readable up front, so you can plan the index around them

256 MB records 50,000 queries reference 56.35 s timeout 564 s

live data preview

queries.txt — one query per line, prefixed by its kind

field type note
BOOL 62% of queries operators are left-associative, no parentheses
PHRASE "..." 38% of queries adjacent tokens, exact order, stopwords retained

sample bytes

BOOL bond NOT tebaickplea
PHRASE "in the matter of trixclar"
BOOL ceeproust AND drolslemdio
PHRASE "fyslut cliceen surety"

Four lines from the seed-1 query file, unmodified. Most words come from a synthetic vocabulary; the legal terms are real. Boolean queries use AND / OR / NOT; phrase queries must respect token positions.

C · The correctness track

Join

Parse a hostile multi-megabyte CSV — quoted commas, escaped quotes, embedded newlines, mixed line endings, a UTF-8 BOM on some chunks and not others — join it against a lookup table, emit sorted aggregates.

The fast path here is easy and the edge cases are the actual task.

This is the track where the most submissions will fail the hash gate, and we are telling you that in advance so you budget time for verification rather than discovering it at hour 68.

  • Hash join versus sort-merge, on data where the answer isn't obvious
  • Quoting rules are in the spec and they are not negotiable
  • A parser that's 3× faster and wrong scores zero
  • Most disqualifications will happen here

204 MB records 3.32M records reference 14.44 s timeout 144 s

live data preview

records.csv — bytes as they appear on disk

field type note
id,name,note,region_id,amount 5 columns column count is fixed; everything else about the encoding is not

sample bytes

⟨BOM⟩id,name,note,region_id,amount␍␊
1,"Kaphar, Titus","said ""no"" twice",7,1420.00␍␊
2,"Betts","a note that␊spans two lines",7,88.50␊
3,,"",12,0.00␍␊
4,"trailing space ","comma, quote "" and newline␊together",3,-5.25␊

From the spec's worked example. Every hostile feature in the file is visible in these five lines. ␍␊ marks CRLF, ␊ marks a bare LF, and ⟨BOM⟩ marks a U+FEFF that appears mid-file at some chunk boundaries.

D · The algorithms track

Traverse

A 5M-edge directed weighted graph and 2,000 source-target shortest-path queries.

Most of the work is getting five million edges off disk and into a layout the CPU likes. The searches themselves are short, with a long tail.

Preprocessing is allowed and counted inside your timed run. Queries are drawn at a bounded search distance, so a heavy index has little time to pay for itself: fast parsing, a compact layout and a lean search loop decide this track.

  • A fast submission spends much of its time parsing the edge list
  • Memory layout beats asymptotics at this size, consistently
  • Most searches are short; the tail and the unreachable ones are not
  • Contraction hierarchies are legal, but this query mix rarely repays them

92 MB records 5M edges reference 72.69 s timeout 727 s

live data preview

graph.txt — header, then one edge per line: u v w

field type note
n m header node count, edge count
u v w uint32 ×3 tail, head, weight — not sorted by u

sample bytes

1740512 5000000
1188446 437285 120
64107 865467 170
472413 359585 122
429961 196139 82

Node ids are dense and 0-indexed. Weights are uint32. The graph is directed; a reverse edge, where one exists, is listed separately.

03 what you get, and when

Nothing about the scoring is a surprise

You will not be handed a spec on the day. Everything that decides your score is published before kickoff and frozen. The only thing held back is bytes.

  1. LIVE

    on this page, now

    • schema and real sample rows for all four tracks
    • measured reference timings and dataset sizes
    • the docker build and docker run commands, in illustrative form
    • the full efficiency coefficient with an illustrative worked example
  2. FROZEN

    Oct 28 · 18:00 UTC · T-48h

    • all four track specifications
    • one naive Python reference per track
    • the complete scoring harness, with its exact command, to run locally
    • measured reference numbers replace the estimates on this page
    • nothing about the scoring changes after this moment
  3. SCHEDULED

    Oct 30 · 06:00 UTC · T-12h

    • 10% sample datasets
    • their expected output hashes, so you can verify locally
    • twelve hours is deliberate — enough to check, not enough to overfit to sample bytes
  4. KICKOFF

    Oct 30 · 18:00 UTC · T-0

    • full datasets released
    • the live leaderboard opens
    • clocks start

The final scoring dataset is generated from the same distribution at a seed you have never seen. You can tune. You cannot overfit to specific bytes.

04 how winners are decided

The efficiency coefficient

Raw speed is a blunt instrument. A submission that runs in 0.4 seconds by loading 40 GB into RAM, pulling in six libraries and sprawling across 4,000 lines has not beaten a 0.9-second solution that fits in 200 MB and reads cleanly. K is how we say that with arithmetic instead of opinion.

Everything is a ratio against the reference

Nothing is measured in absolute seconds, and nothing is normalised against the current leader. Every axis is your number divided into the reference implementation's number, on the same machine, in the same batch. A ratio of 1.0 means you matched the naive version. 10.0 means you beat it tenfold.

This matters more than it looks. Because the reference is fixed and published, your score never changes when somebody else submits. The leaderboard reorders around you. Your number does not move.

Every other design we considered — normalising to the field leader, percentile ranking, z-scores — has the property that a stranger's submission at hour 70 silently rewrites your result. That is intolerable in a competition where people are making trade decisions all weekend based on where they stand.

K = (Tref/T)0.50 × (Mref/M)0.20 × (Sref/S)0.15 × (Dref/D)0.15

T 0.50 + M 0.20 + S 0.15 + D 0.15 = 1.00

Four factors, four exponents, and the exponents sum to exactly 1.0.

That last property is what keeps K readable: K = 6 means "six times the reference, all things considered," and nothing else. It is the weighted geometric mean of your four ratios, which is the correct way to combine normalised ratios. An arithmetic combination would let one axis with a wide natural spread dominate the others regardless of weight.

The exponents are also what stop any single axis from steamrolling the rest. A 100× speed win contributes 100^0.50 = 10.00 to the product. A 100× memory win stops at the 50× cap and contributes 50^0.20 = 2.19. The ordering of importance is enforced by the mathematics, not by a promise in the rules.

Four axes, four weights

Each card prints what a win on that axis is actually worth to the product, after its cap. Source size and dependencies can only cost, so their cards print what going over the reference costs instead. That is the formula telling you where to spend the weekend, and it tells you before kickoff.

T

wall time

weight

0.50

no cap

50% of the exponent budget

reference 32.05 s

Mean of the middle three of five runs, highest and lowest discarded. Timed by the harness from process exec to exit, not by any clock inside your program.

This is a performance event, so this is the biggest weight and it is not close.

what a win here buys

2×
1.41
10×
3.16
100×
10.00

a 100× win here contributes 10.00 to the product

M

peak memory

weight

0.20

capped at 50×

20% of the exponent budget

reference 228 MB

Peak memory of the run, read from the kernel's peak counter after the run ends. Not self-reported, and not sampled: the kernel's high-water mark, which catches transient spikes your own instrumentation would miss.

Buying speed with unbounded RAM is a real strategy and it should cost you something.

what a win here buys

2×
1.15
10×
1.58
100×
2.19

a 100× win here contributes 2.19 to the product

S

source size

weight

0.15

capped at 1.0×

15% of the exponent budget

reference 56 lines

Code lines counted by scc after your language's canonical formatter, where one exists. It covers everything in the repo at the recorded commit, minus registry packages first published before October 1, 2026 by people outside the team, which are spot-checked at scoring. Byte-identical copies of our published generators and references are free, so copying the generator in for build-time training costs nothing.

Line counts do not care how long your names are, so you can write readable code without paying for it. Expect a strong submission to be larger than the reference here. That is the trade, and it is priced rather than punished.

what going over costs

2×
0.90
10×
0.71
100×
0.50

twice the reference here multiplies K by 0.90

D

dependency weight

weight

0.15

capped at 1.0×

15% of the exponent budget

reference 1.00

D = 1 + the number of direct non-stdlib runtime dependencies declared in your manifest. The reference uses none, so D_ref = 1 and every dependency is a straight cost.

Importing the crate that already solves half the task is permitted. It is simply not free, and this is where it gets billed. A zero-dependency submission keeps a factor of 1.0 here. One dependency gives 2^-0.15 = 0.90, about 10% off K before any other work.

what going over costs

2×
0.90
10×
0.71
100×
0.50

twice the reference here multiplies K by 0.90

Memory is capped at 50×. Wall time is not.

Wall time has no cap. It is never pathological, so a faster program always earns a higher K.

Memory is the axis where a sloppy naive reference produces absurd ratios. Allocating 4 MB where the reference lazily allocates 3 GB gives a 750× memory ratio, a number that would drown out all the actual performance work in the field. The 50× cap keeps the ranking about engineering rather than about finding the one dimension where the reference happened to be sloppiest.

Source size and dependencies can cost you but never pay. The S factor is capped at 1.0, so code shorter than the reference earns nothing extra, and D never drops below the reference's 1, so every dependency is a cost.

raw memory ratio

750×

after the cap

50×

contribution at 0.20

2.19

K becomes points on a fixed curve

K maps to a 0–100 speed score on a logarithmic curve, against an anchor published now and not adjusted afterwards. K = 1 or below, no better than the naive reference, earns nothing. K = 10 earns exactly half. K = 100 or better earns full marks; past that, raw K still breaks ties and decides the highest-K prize on each track.

The curve is logarithmic because performance work is. Going from 1× to 2× is an afternoon. Going from 12× to 24× is the rest of the weekend and a rewrite. A linear scale would make the first hours worth as much as the last thirty, which is not how any of this works.

50 × log10(K)

clamped to [0, 100]

K = 100100 pts

K = 1050 pts

K = 1nothing

0 25 50 75 100 1 2 5 10 20 35 60 100
K → · every point plotted from the scoring function itself
1 2 5 10 20 35 60 100+
speed score 0.0 15.1 34.9 50.0 65.1 77.2 88.9 100.0

Run your own numbers

Every ratio below is your number divided into the reference's. Change one and watch what it buys you. Loaded with Team Nightshade's illustrative run.

Efficiency coefficient calculator

^0.50 = 5.004

^0.20 = 1.416

^0.15 = 0.905

^0.15 = 1.000

efficiency coefficient

K = 6.41

speed score · 50 × log10(K)

40.4 / 100

Worked example, start to finish

Team Nightshade, Track A. An imagined team, scored against the measured reference. Every number that touches their score, in order. Nothing else feeds in.

axis reference Nightshade ratio exponent contribution
T · wall time 32.05 s 1.28 s 25.04× 0.50 5.004
M · peak memory 228 MB 40 MB 5.70× 0.20 1.416
S · source size 56 lines 109 lines 0.51× 0.15 0.905
D · dependency weight 1.00 1.00 1.00× 0.15 1.000
K = product 6.41

how K assembles · running product

  1. — 1.00
  2. T ×5.004
  3. M ×1.416
  4. S ×0.905
  5. D ×1.000

S is the only step that costs them — 95% over the reference on source size, paid for a 25× win on wall time. Final K = 6.41

K is computed from raw ratios. The table shows each factor rounded to two places; the product cell shows the raw-product K, not the product of the rounded factors. Those differ in the third decimal.

Note the third row. Nightshade wrote 95% more code than the reference and lost points for it, because the optimised parser is genuinely longer than the naive one. They accepted that cost to buy a 25× speedup, which is exactly the trade the coefficient exists to price.

Note also the second row. 5.7× less memory sounds like a lot and contributes 1.416, less than a third of what the time ratio contributed. If they had spent those hours on the parser instead, they would have scored higher. That is the formula telling you where to work, and it tells you before kickoff rather than after results.

now the full score

final score raw weight points
Speed score — from K = 6.41 40.4 / 100 0.65 26.26
Judge score — panel average 82.0 / 100 0.35 28.70
Track A total 54.96

What the judges score

35% of every track total, on a 5-point scale across three criteria, averaged across every judge who reviewed the submission.

65% machine 35% judges

15%

Code quality

Would you merge this? Optimised code is allowed to be dense. It is not allowed to be unexplainable. Clear structure, honest error handling, and a comment wherever the trick is not self-evident. A judge should be able to follow why the fast version is fast.

12%

Technique

Is the approach genuinely considered, or brute force that happened to land? A well-chosen data layout scores above hand-written SIMD bolted onto the wrong algorithm. We are looking for evidence of decisions, not effort.

8%

The optimisation log

A required PERF.md narrating how you got there. "Naive was 41 s. Flat array instead of hashmap took it to 8 s. Manual line splitting got 2.1 s. Tried SIMD for the timestamp parse, made it slower, reverted." Measurements, not adjectives, and each step cites the live-board run ID that measured it. Judges see your run history and check that the cited runs exist and that each claimed change moved in the claimed direction. The failed experiments count — they are often the most interesting part and judges are told to reward them.

Judges see your code, your write-up and your live-board run history. They never see the final measured scores before they submit their forms. The live board is public during the event, gate pass/fail for every team is published by November 4, and full per-axis breakdowns come only with the winners on November 13. That order keeps a final number from colouring the read of the code.

The overall championship

Per-track winners are decided on that track's total. The overall Speed Demon title requires at least three tracks entered and is decided on the geometric mean of your best three track totals, not the average. Enter all four and your weakest track is dropped.

This is deliberate and it is standard practice in benchmarking. The geometric mean refuses to let one spectacular track carry two mediocre ones. To win overall you have to be good everywhere, which is what "Speed Demon" is supposed to mean.

one spectacular track

90 / 40 / 40 · 30 dropped

52.4

good everywhere

60 / 60 / 60 · 55 dropped

60.0

geometric mean of the best three · three tracks minimum

05 before K is computed at all

Three gates

These are pass/fail. A submission that fails any of them scores zero on that track, regardless of how fast it ran. They are checked automatically, and they are checked on every one of the five runs.

1

Correctness

SHA-256 of your output file must match the expected hash exactly

Not "close enough", not "same values in a different order" — the spec pins the output format down to line ordering and float formatting precisely so this check can be binary. The 10% sample dataset ships with its expected hash so you can verify locally before you ever submit.

If your submission produces the right answer four times out of five, it fails. Nondeterminism is a correctness bug.

2

The timeout

hard kill at 10× the reference implementation's time on that track

A container that hangs is a failed run, not a slow one. The exact ceiling is published per track alongside the reference, so you always know how much rope you have.

Track A: 321 s. Track D: 727 s.

3

The sandbox

no network · dataset read-only · root filesystem read-only

Scratch space is a 1 GB tmpfs at /tmp. We diff the container filesystem after every run.

Writing precomputed answers into the image, phoning home, or attempting to touch the dataset mount is disqualification from the event — not just the track.

06 we run it, not you

One machine, and you know its name

Machine variance between a desktop Ryzen and a fanless laptop is comfortably 5×, before you even reach thermal throttling and background processes. So participants never report a number. You submit a Dockerfile. We build it and run it.

The box

A Hetzner dedicated server in Falkenstein, Germany (fsn1), with the same Intel Core i5-13500 as the EX44. Dedicated, not shared — no noisy neighbours, no burst credits, no hypervisor surprises. It is the same physical machine for every submission across the entire event.

fsn1

Hetzner · dedicated

CPU
Intel Core i5-13500 · 14 cores / 20 threads · 6 P-cores @ 4.80 GHz, 8 E-cores @ 3.50 GHz
Cache
544 KiB L1d · 704 KiB L1i · 11.5 MiB L2 · 24 MiB L3
Memory
64 GB DDR4 (2 × 32 GB) · non-ECC
Storage
2 × 1.92 TB datacenter NVMe (U.2) · mdadm RAID1
Network
1 Gbit/s (disabled during scoring)
OS
Debian 13 (trixie)
Runtime
Docker · cgroups v2
Location
Falkenstein, Germany

Two things we change before scoring starts

no_turbo=1

The CPU governor goes to performance and turbo is disabled

Turbo alone introduces 10–20% variance depending on how long the box has been idle, which is more than the gap between third and seventh place. Sustained clocks, deterministic results. You are optimising against a fixed 2.5 GHz base rather than a boost ceiling that depends on the weather.

--cpuset-cpus=0-11

Containers are pinned to the six P-cores only

This is a hybrid CPU. Mixing P-cores and E-cores in one run makes results depend on how the scheduler felt that second, and on Raptor Lake the P/E performance gap is large enough to swamp real optimisation work. So you get six physical performance cores, twelve hardware threads, and nothing else. Design your parallelism for exactly that. Spawning 20 workers will hurt you.

The run command

This is the shape of a scored run, and it is illustrative. The exact harness command and its timing wrapper ship with the harness on October 28.

scoring harness · illustrative
# build, from your repo root: offline, at most 10 minutes and a 2 GB image
docker build --network=none -t speeddemon/$TEAM:$COMMIT .

# pre-warm the dataset into page cache so we measure your code, not the NVMe
vmtouch -t /srv/speeddemon/data/track-$TRACK

# the scored run — repeated 5x, serialized, nothing else running on the box
docker run --rm \
  --cpuset-cpus="0-11" \
  --cpuset-mems="0" \
  --memory=8g --memory-swap=8g \
  --pids-limit=512 \
  --network=none \
  --read-only \
  --tmpfs /tmp:rw,size=1g,exec \
  --ulimit nofile=8192:8192 \
  -v /srv/speeddemon/data/track-$TRACK:/data:ro \
  -v /srv/speeddemon/out/$RUN:/out:rw \
  -e TRACK=$TRACK \
  speeddemon/$TEAM:$COMMIT

# memory: the kernel's peak counter for the run, read after it ends
--cpuset-cpus="0-11"
Twelve threads on six physical P-cores. Hard. Twenty workers will cost you more than they buy.
--memory=8g --memory-swap=8g
Equal values disable swap entirely rather than allowing an equal amount of it — the Docker behaviour people most often get wrong. There is 32 GB of swap on the host and none of it is available to you. Exceeding 8 GB is an OOM kill and a failed run, not a slow one.
--network=none
At build time and at run time. Vendor everything into the image.
--read-only + --tmpfs /tmp
Read-only root with 1 GB of scratch at /tmp if you need spill space.
-v ...:/data:ro
The dataset, mounted read-only. Filenames are fixed by the track spec.
peak memory
Read from the kernel's peak counter for the run, after it ends. Not sampled and not self-reported, so it catches transient spikes your own instrumentation would miss.

Your Dockerfile contract

Five rules. Nothing else about your image matters to us.

  1. 1

    Read from /data

    Mounted read-only. The filenames are fixed by the track spec. Do not assume any path outside it exists.

  2. 2

    Write to /out

    Exactly one output file, named by the spec. Anything else you leave behind gets flagged by the filesystem diff.

  3. 3

    Exit 0

    Any non-zero exit is a failed run. Handle your own errors; the harness will not interpret them for you.

  4. 4

    Build offline

    We build with --network=none. If your Dockerfile fetches anything — apt, cargo, pip, a curl — the build fails and so does your submission.

  5. 5

    Build FROM a published toolchain

    Every FROM names one of the published toolchain images, each pinned by digest. COPY --from and RUN --mount may only use earlier stages of your own Dockerfile, ADD may not fetch URLs, and # syntax= directives and cache, secret or ssh mounts are refused. Builds are capped at 10 minutes and images at 2 GB.

Dockerfile — the shape we expect
# build stage: one of the published toolchain images
FROM rust:1-slim-trixie@sha256:1a939748a7cbab172de91a014e3804a97af1394a8c844faa67fce9401c4e40fb AS build
COPY . /src
WORKDIR /src
RUN ./build.sh          # no network available here

# runtime stage: slim, and also from the published list
FROM debian:13-slim@sha256:62bd423aeee912639ca8ee681c2b762cb3e25137e0a92b20be455898a9f034ad
COPY --from=build /src/run /app/run
# reads /data, writes /out, exits 0
ENTRYPOINT ["/app/run"]

The allowlist, with every digest, is at <a href="/allowlist.json">speeddemon.dev/allowlist.json</a>: debian:13-slim, python:3.13-slim, rust, golang, gcc 15 and 16, Eclipse Temurin 25 (JDK and JRE), GraalVM native-image 25, Node 22 and 24, .NET 10, Swift 6.2 and Haskell 9. Need another? Ask in Discord by October 28. Build time is not scored, only the run: if you want to spend six of your ten minutes on profile-guided optimisation, that is free.

Timing protocol

5

runs per submission

2

discarded — highest and lowest

3

middle runs, meaned, become your T

5%

spread above this is re-run automatically

2%

reference drift that throws out a whole batch

We run the reference implementation before and after every scoring batch. If the reference drifts by more than 2% across a batch, that batch is thrown out and rescored. That calibration is what lets us say these numbers are comparable and mean it — and because your score is a ratio to the reference rather than an absolute time, mild drift partly cancels out anyway. Runs are strictly serialized. Nothing else executes on the box during scoring.

The live leaderboard

During the 72 hours you can push to a submission branch and get an automated run against the full public dataset released at kickoff, on the same Falkenstein machine, rate-limited to one run per team per 15 minutes. Each push gets a single run, capped at three times the reference's time, and a team has at most one run in the queue. Those results go straight onto a public board so you can watch the field move and know whether your last change was worth it.

Your repo can stay private until code freeze. To use the board, install the Speed Demon Board GitHub App on that repo. It gets read-only access to the repos you pick and nothing else, through tokens that last an hour. Every run gets an ID, and the steps in your PERF.md cite those IDs.

Final scoring runs on the holdout dataset after code freeze, and that is the only set that counts. The gap between your board position and your final position is the price of overfitting.

07 the receipts

What you submit

Per track. A repo we can build without asking you a single question.

$ ls -la ./track-a

6 entries · 5 required

  • Dockerfile

    meets the five rules

    Builds offline from a clean checkout.

  • PERF.md

    worth 8% of your total

    The optimisation log. Every step from naive to final, citing the live-board run ID that measured it. The part judges enjoy most.

  • README.md

    prose

    The approach in plain prose, your dependency list if any, and what you would do with another 24 hours.

  • .speed-demon.toml

    machine-readable

    Track letter, team name, language, declared dependency count. So the harness can queue you without a human in the loop.

  • src/

    OSI licence, public at freeze

    Public GitHub repository. One directory per track you entered.

  • demo.mp4 optional

    under 3 min

    Optional and not scored, but the good ones end up in the results write-up and on the archive page.

08 pre, during, after

Timeline

All times UTC. The hack runs across Halloween weekend, which is not an accident.

Pre-event

01

October 2

Registration opens

Join the Discord. Start arguing about languages early.

Pre-event

02

October 26

Team formation closes

1–4 people per team. Solo entries welcome.

Pre-event

03

October 28 · 18:00 UTC

Specs, references and harness published

All four track specifications, one naive Python reference per track, and the complete scoring harness with its exact run command and timing wrapper. Measured reference numbers replace the estimates on this page. Run it locally before kickoff. Nothing about the scoring changes after this date.

Pre-event

04

October 30 · 06:00 UTC

Sample datasets

10% samples with their expected hashes. Twelve hours before the gun.

The hack

05

October 30 · 18:00 UTC

Kickoff

Full datasets released. Live leaderboard opens. Clocks start.

The hack

06

October 31

Midnight, and nothing is haunted but the cache

Standings posted. No costumes, no call, no ceremony — you have a profiler open and we respect that.

The hack

07

November 2 · 18:00 UTC

Code freeze

Repositories locked at the recorded commit. Scoring queue begins.

Post-event

08

November 2–4

Scoring runs

Every submission built and run five times on the holdout dataset. Gate pass/fail for every team is published by November 4, including exactly which gate a failing submission hit. Full per-axis breakdowns wait for the winners on November 13.

Post-event

09

November 3–12

Judging window

Each submission reviewed independently by multiple judges on structured forms. Written feedback to every team, not just the winners.

Post-event

10

November 10

Write-Up side quest closes

Deadline for the $300 write-up prize. Top three, $100 each.

Post-event

11

November 13

Winners announced

Per-track winners, the overall Speed Demon, side quest results, full per-axis breakdowns for every team, and the full scoring dataset published so anyone can reproduce the board. The teardown of every deliberately terrible decision in the reference implementation follows in the results write-up.

09 five ways to win

Prizes

$1,800 total.

  1. $800

    Speed Demon

    Highest geometric mean of your best three track totals, from three or more tracks entered. Fast everywhere, not fast once.

  2. $400

    Runner-up

    Second on the overall board. Same bar, narrower margin.

  3. $200

    Write-Up side quest

    $100 × 2. Publish a post about your optimisation journey — the profiler reading you misread, the rewrite that made it slower, the two-line change that halved it. Tag Hackathon Raptors. Judged on insight, not audience size.

  4. $200

    Third place

    Third on the overall board.

  5. $200

    Highest K on each track

    $50 × 4. The largest efficiency coefficient recorded on each of Tracks A to D. Specialists welcome: you do not need three tracks for this one.

total pool · $1,800

10 ten of them

Rules

  1. 01

    Any language

    If it builds in a Linux container offline and runs on x86-64, it is eligible. Assembly is eligible. Your own compiler is eligible. The coefficient does not care what you wrote it in, and neither do we.

  2. 02

    Dependencies are allowed and priced

    This is not a standard-library purity contest. Import what you want — the D axis charges you for it. What is banned, per track, is the library that solves the entire task: no ready-made search engine in Track B, no analytics dataframe library in Tracks A or C. Those are listed explicitly in each track spec, by name. Vendored code counts toward your source size unless it is a registry package first published before October 1, 2026 by someone outside your team.

  3. 03

    Correct output or no score

    SHA-256 must match. The specs pin down ordering and formatting so this is unambiguous. Verify locally against the sample before you submit.

  4. 04

    No precomputed answers

    The scoring dataset is generated at a seed you have never seen. Shipping cached results, or code that recognises the input and short-circuits, is disqualification from the event.

  5. 05

    New code only

    All project code written during the 72-hour window. Reading up on algorithms, sketching designs, and preparing your environment beforehand are all fine and expected. Code committed before kickoff disqualifies the submission.

  6. 06

    AI tools are expected

    Claude Code, Cursor, Codex, Copilot, local models — bring whatever you use. We do not gatekeep on how the code was produced. We gatekeep on whether it survives five runs on our hardware and whether PERF.md shows a human understood what was happening. Both are hard to fake, which is rather the point of measuring instead of judging.

  7. 07

    Team size 1–4

    Solo entries are welcome. Find teammates on the Raptors Discord before or during.

  8. 08

    Source public at code freeze

    Public GitHub repo under an OSI-approved licence. Until code freeze it can stay private; to use the live board, install the Speed Demon Board GitHub App (https://github.com/apps/speed-demon-board) on that repo. It can only read the repos you pick. Anonymous usernames are fine; the team must be reachable for written follow-up during judging.

  9. 09

    The harness is the referee

    Its numbers are final. If you believe a run was mis-measured you have 48 hours after publication to request a re-run, and we will do it. What we will not do is accept a timing from your machine as evidence.

  10. 10

    The formula does not change after kickoff

    Every weight, cap, anchor and gate on this page is frozen from October 28. If we find a flaw in the coefficient during the event, it gets documented publicly and fixed for the next edition. It does not get patched mid-competition.

11 find your track

Who this is for

If you have ever opened a flame graph at 1am out of curiosity rather than obligation, this is your hackathon.

Audience segments mapped to tracks
who you are ABCD
Systems engineers Cache lines, branch prediction, allocator behaviour. The tracks where memory layout beats cleverness.
Data engineers You have parsed worse CSVs than ours at work, and you have opinions about how the pipeline should have been written.
Search and database folk Posting lists, skip pointers, join strategies. Track B is the one you have been mentally designing for years.
Algorithms people Competitive programming background, and finally a contest where the constant factor counts as much as the complexity class.
Anyone with something to prove Four tracks, one reference implementation, and a public number at the end of it. No pitch, no slides, no interpretation.

12 who reads your code

Judges

The 35% that is not measured is decided by engineers and technical leaders who build and run production systems at scale, from low-latency search and payments to site reliability and cloud infrastructure.

Judges review submissions on structured forms, independently, with each team's live-board run history beside the code. They never see the final measured scores before they submit. That order matters: it stops a final number from colouring the read of the code, and it means the two parts of your score come from two different places.

the split

35%

decided by people, on structured forms, before the final scores are out

also on the panel

The Speed Demon panel is drawn from the Hackathon Raptors judging pool, which across recent events has included staff and principal engineers from:

Apple, Microsoft, Amazon Web Services, Meta, SpaceX, Oracle, Uber, IBM.

13 the short answers

FAQ

01 Do I have to enter all four tracks?
No. Enter one if you want. The per-track prizes, one for the highest K on each track, are open to anyone. Only the overall championship requires three or more, because “Speed Demon” should mean broadly fast rather than narrowly lucky. It counts your best three.
02 My laptop is much faster than an i5-13500. Does that hurt me?
Not at all — you never submit a timing. Build and tune wherever you like. Just be aware that optimisations tuned to your cache sizes may not transfer, and that you get twelve threads on six physical cores at fixed clocks. The harness ships on October 28 so you can run locally under the same limits.
03 Can I use a library that does most of the work?
Sometimes. Each track spec bans the specific class of library that trivialises that task: the search engine, the dataframe. Everything else is permitted, and the D axis charges you 15% weight for the privilege. A zero-dependency submission starts with a real advantage there. Vendored code also counts toward your source size, unless it is a registry package first published before October 1, 2026 by someone outside your team.
04 What if my optimised code is longer than the reference?
It almost certainly will be, and that is priced into the formula rather than punished by it. In the illustrative worked example above, the optimised approach was 95% larger and still reached K = 6.41. Source size is 15%, wall time is 50%. Spend lines where they buy you time.
05 Won't everyone just write it in C or Rust?
Many will. K is a ratio against a naive Python reference, so the language you choose is part of your result, and nothing in the formula is built to cancel that out. Wall time is measured end to end, startup included. Managed runtimes can cut their startup with a build-time AOT cache or GraalVM native-image. Assembly, incidentally, tends to lose badly on source size.
06 Can I train at build time?
Yes. Profile-guided optimisation, a JVM AOT cache (JEP 514) and GraalVM native-image may all train on generator output inside the build. /data is not mounted at build time, and shipping cached outputs is banned under rule 04.
07 What happens if my container gets OOM-killed?
That run fails, and the gates are checked on every run, so you score zero on the track. Memory discipline is part of the event.
08 Can I submit multiple attempts?
Yes, against the full public dataset released at kickoff, once per 15 minutes per team, until code freeze. The final scoring run uses whatever is at your recorded commit at 18:00 UTC on November 2.
09 How is source size measured, exactly?
Code lines as scc counts them, which leaves out blank lines and comments, after your language's canonical formatter where one exists. It covers everything in your repo at the recorded commit, minus registry packages first published before October 1, 2026 by people outside your team, which we spot-check at scoring. There is no identifier normalisation: line counts do not care how long your names are, so write readable code. The exact measurement script ships with the harness on October 28.
10 Why the geometric mean for the overall title?
Because the arithmetic mean lets one enormous result carry a mediocre record. It is the standard choice for aggregating normalised benchmark ratios, for exactly this reason. A team scoring 90/40/40 should not beat a team scoring 60/60/60, and under the geometric mean it does not.
11 Why cap memory at 50× and not wall time?
Because the reference implementation is naive by design, and on memory naive is very naive. Without a cap, the team that noticed the reference allocates 3 GB where 4 MB would do could post a memory ratio in the hundreds and win on a single observation. The cap makes that a strong result rather than a decisive one. Wall time is never pathological in that way, so it has no cap, and a faster program always earns a higher K. Source size and dependencies are capped at 1.0: they can cost you but never pay.
12 Is this just competitive programming?
No. There is no clever-trick puzzle to spot, and the algorithm is usually obvious within an hour. The difficulty is engineering — memory layout, I/O strategy, parallelism, and the discipline to measure before changing things. Competitive programmers do well here, but so do people who have spent years making production systems faster.
13 Where do the datasets come from?
Synthetic, generated from published generators that ship with the harness so you can produce as much extra training data as you want. The scoring set uses the same generators at an unpublished seed. Realistic distributions, no personal data, no licensing questions.
14 Can I use multiple threads?
Yes, up to the twelve you are given. Parallelism is a legitimate and expected strategy. Just remember that the memory cap is shared across all of them and that oversubscribing twelve threads with twenty workers costs you more than it buys.
15 What if two teams tie?
Ties on the final total are broken by raw K, then by wall time, then by earliest submission commit.
16 Can I use io_uring?
No. Docker's default seccomp profile blocks it, and the harness does not change that profile. mmap works.

72 hours. one number.

Every millisecond is a soul

Four tracks. One reference implementation to beat. One machine in Falkenstein that does not care how good your idea sounded.

1.31s

what the board rewards