- read

C# vs Rust vs Go. A performance benchmarking in Kubernetes

B Shyam Sundar 40

A simple comparison between three excellent technologies

Introduction

In this article, we will take a look at how to create high-performance web APIs in Rust, C# and Go and deploy them to a Kubernetes cluster. We will also look at how to monitor the resource usage of these APIs using performance monitoring tools.

This article expands upon the work made by Anton Putra. Video Link. We will create one app for each language and see how well they perform.

Prerequisites

This article assumes that you have a basic understanding of Rust, C#, Go, Docker, Terraform and Kubernetes.

Creating the Web APIs

For the purpose of this article, we will create two web APIs, one in Rust and one in C#. Each API will have two methods. The first method will be used to store information, while the second will be used to retrieve it.

In Rust, we can create a web API using the Actix framework. This framework provides a simple and efficient way to create web APIs in Rust. To create the storage and retrieval methods, we can use the Serde library to serialize and deserialize data.

In C#, we can create a web API using the ASP.NET Core framework. This framework provides a simple and efficient way to create web APIs in C#. To create the storage and retrieval methods, we can use Entity Framework Core to interact with a database.

Exploring the Code

I have cloned the Repo from Anton and have edited the code to fit the needs of this Article. The edited repository link for this article can be found shyamsundarb-arch/rustvcsvgo (github.com). Please watch Anton’s video for explanation on Helm charts and other deployment objects. The changes I made were:

  1. Rocket to Actix for Rust
  2. Created a C# Web API project with 2 endpoints along with required docker files.
  3. Added Kubernetes files for the C# project

Monitoring Resource Usage with Performance Monitoring Tools

To ensure that our APIs are running efficiently, we will use performance monitoring tools to monitor their resource usage. These tools will provide us with information on the CPU and memory usage of our APIs, as well as any other performance-related metrics that we need to track. This information will help us identify any areas of our APIs that need to be optimised, and make any necessary adjustments to improve their performance. To visualize we will use Grafana.

Initial Observation

Without any load the base running application benchmarks are showcased below.

CPU% — No Load
Memory Usage — No Load

The red line represents Rust, Blue line represents Go and the Green line represents C#.

In idle state rust’s CPU usage is slightly higher than C#’s and C#’s is slightly higher than Go. C#’s memory usage when idle is slightly higher than Go, and Go’s memory usage is slightly higher than Rust. Rust’s memory usage is very minimal.

Load for GET Request

We are going to use an excellent tool called bombardier.

First we will hit all 3 services with 1 million requests from 50 connections and limit the rate to with 100 request per second.

Command for doing so is:

bombardier -n 1000000 -m GET -c 50 -r 100 <URL>
CPU% — Under Load

C# and Go has almost same CPU usage while Rust CPU usage is much lesser than these two.

Memory Usage — Under Load

C# memory usage is certainly higher in this scenario, followed by Go. But Rust’s memory usage did not even budge. That is impressive.

Let us look at latencies.

p99 — Under Load

p99 (99% of requests being served):

C# — 6.10 ms, Go — 4.96 ms, Rust — 4.98 ms

Go has a slight edge over Rust followed by C#.

p90 — Under load

p90 (90% of request being served):

C# — 4.55 ms, Go — 4.51 ms, Rust — 4.54 ms

Go again has a slight edge over Rust. But C# is quite close as well.

Bombardier stats:

Rust
C#
Go

C# seems to be have the highest throughput.

Load for POST Request with limits

Now let us hit all 3 services with 1 million requests from 50 connections and limit the rate to with 100 request per second. But this time we will use a single connection making 1 request every second.

Bombardier command is

bombardier -n 1000000 -m POST -c 1 -r 1 <URL>
CPU% — POST Load

Go lang has the highest CPU usage.

Memory usage — POST load

C# has the highest memory usage.

p99 — POST Load

Latency is where things get interesting. Go’s latency is trying to keep up with C# it isn’t consistent. Rust seems to be shining as it seems have half the latency of C#.

p90 — POST load

Again, Rust has amazingly low latency.

Bombardier stats:

Rust
C#
Go

Conclusion

These benchmarks while serves as a high level simple understanding of performance. This does not account for complex scenarios. If you are interested in web frameworks benchmarks, I would suggest you visit TechEmpower Framework Benchmarks.

From this benchmark, we are able to understand that Rust has consistent performance and is almost always faster than C# and Go. But that is to be expected as Rust runs on the metal. Between C# and Go the performance seems to be nuanced. As C# and Go seems to outperform each other in difference scenarios.