Skip to content
5 min read

The Race Car vs. The Go-Kart: Why My Fastest Code Was Slower

I built a project to prove gRPC was faster than HTTP. The results shocked me. Here's a deep dive into how network protocols perform in the real world and why the 'fastest' tool isn't always the winner.

  • #dotnet
  • #csharp
  • #architecture
  • #performance
  • #messaging
  • #docker

My high-performance gRPC service was supposed to be a Formula 1 race car. In my first test against a simple HTTP service, it lost... to a go-kart.

If you've ever been humbled by a piece of code that defied your expectations, you know the feeling. I set out to build a hands-on demo comparing three popular .NET communication methods: a classic HTTP API, a modern gRPC service, and an asynchronous RabbitMQ message queue. The goal was to see their performance differences firsthand. The results weren't what I expected, and they taught me a crucial lesson about performance.

Setting Up the Race: Couriers, Race Cars, and the Postal Service

To make sense of these technologies, I thought of them as different delivery services:

  • HTTP API: A standard, reliable courier. You give them a package, they drive it over, get a signature, and come right back. Simple and effective.
  • gRPC with Protobuf: A high-tech, private delivery service with a fleet of F1 cars. It uses a super-efficient shorthand language (Protobuf) on a private, multi-lane highway (HTTP/2). It's built for raw speed.
  • RabbitMQ: The postal service. You drop a letter in the mailbox and walk away, trusting it will get there. It's not about speed for one letter, but about handling a massive volume of mail without breaking a sweat.

I containerized everything with Docker and built a simple console app to send messages and time the results. I was ready to see the F1 car leave the others in the dust.

// The whole "benchmark": a Stopwatch around each call, timings collected per
// protocol. Crude, but enough to expose a 114 ms outlier — and nothing more.
var timings = new List<long>();

for (var i = 0; i < iterations; i++)
{
    var sw = Stopwatch.StartNew();
    await client.SendAsync(payload, cancellationToken);
    sw.Stop();

    timings.Add(sw.ElapsedMilliseconds);
}

Console.WriteLine(
    
quot;{name,-12} {timings.Count,-8} {timings.Min(),-11} {timings.Max(),-11} {timings.Average():F2}"
);
Diagram showing how I build HTTP, gRPC, and RabbitMQ as different delivery services.
One payload, three delivery mechanisms: HTTP, gRPC, and RabbitMQ.

The Surprising Results: Why the Go-Kart Won the First Lap

I ran the tests. RabbitMQ was instantly fast, as expected for a 'fire-and-forget' system. But then came the shock: on my local machine, the simple HTTP service consistently beat my powerful gRPC service on average time.

Here's a look at the final summary from my tests:

TypeCountShortestLongestAverage
PROTOBUF13111410.62
RABBITMQ150533.53
HTTP151192.20

Look at that Longest time for Protobuf: 114ms. That's the key. This is the 'cold start' problem. The F1 car has a huge setup cost: warming up the engine, getting the tires right, and establishing the complex HTTP/2 connection. The go-kart just starts and goes. On a short, local race with zero network latency, the go-kart's simplicity gives it the edge.

Benchmarking Done Right

In retrospect, using a tool like BenchmarkDotNet would have provided even more granular insights, isolating the connection overhead from the actual serialization/deserialization time. For any serious performance testing in .NET, I highly recommend it over manual Stopwatch measurements.

The Real Lesson: Context is Everything

This project was a powerful reminder that in software engineering, the 'best' tool is always relative to the problem you're solving. While gRPC is undoubtedly faster over a real network where its advanced features can shine, the overhead can make it slower in specific, low-latency scenarios.

It's a fantastic insight to have, especially for technical interviews. It shows you don't just know what a technology is; you understand when and why to use it.

If you want to run the race yourself, you can find the complete source code and a guide on how to run it on my GitHub.