I ported Basecamp's Campfire to C# as a drop-in replacement for the original Rails version. Then we paired agents with a profiler and what we found 33x'ing the Rails performance (and leading 3 benchmark categories over Rust and C) might surprise you.
Drop-in means you point it at your Rails Campfire's database and storage, reuse your SECRET_KEY_BASE, and everything works exactly the same. Same schema, same cookies, same HTML.
Every speedup required a real-life performance tuning loop to unlock the next 10%.
The benchmark is Basecamp's shared harness, run on one Google Cloud VM: 4 dedicated cores per app, 16 clients, every port measured twice from scratch.
196 million responses were checked against the route contracts before they counted. 0 invalid.
C# vs Rails, req/s:
Messages page: 2,183 → 71,154
Sidebar: 2,407 → 78,359
Search: 2,491 → 76,385
Room page: 2,388 → 37,115
Post a message: 265 → 2,598
Rails runs behind its Thruster proxy as shipped. C#, Rust, Go and C serve HTTP themselves.
The loop: an agent attaches the dotnet-diagnostics MCP to the container under load, finds the single biggest cost it can explain, makes one change, and proves it on the harness. Then a fresh chat starts the next round.
Most of the big wins changed a design, not a hot loop.
Why borrow when you can steal? Designs we took from the other ports:
• C: check for database changes outside the cache lock
• Rust: checkpoint when WAL pages pile up, not every commit (posting 50 → 1,449/s)
• Rust, Go, C: serve HTTP with no proxy hop (~3× on reads)
The profiler found the rest:
• read SQLite's 48-byte WAL-index header instead of querying for changes: +14–24%
• reuse HMAC and AES-GCM contexts per thread instead of .NET's one-shot calls on OpenSSL: +10–14%
• stop writing 2 log lines per request: +15–20%
A lot of the speed came from
@jlchavezGT, who ran the same profiling loop on his own and sent a stack of PRs: cookie and cache-key code without throwaway strings (51 KB → 29 KB allocated per request, about +10%) and an avatar cache. Thank you, Jose.
But "C# is faster" would be the wrong takeaway. C# leads 3 workloads here because of design changes the profiler found, in the same language that started at 2–3× Rails.
C leads the room page and Rust and Go post faster. Reading C's source, its lead is design too.
The fast ports are all within about 3× of each other, and the order flips from route to route. What moved C# up was finding where the time went and changing the design, one round at a time.
Any team can run that loop.
I wrote it all up as an 11-part .NET series:
learninglinqpad.com/blog/202…
Try it on your laptop, then open localhost:8080:
docker run -p 8080:80 -e SECRET_KEY_BASE=$(openssl rand -hex 64) -e DISABLE_SSL=true
ghcr.io/ryanrodemoyer/once-c…
Code, harness and every raw result, including the other ports' numbers:
github.com/ryanrodemoyer/onc…
Thanks to Basecamp for Campfire and the shared harness, and to everyone who built the other ports. C# got where it did because of what they figured out first.