Benchmarking gRPC in Rust & Go

Rustler
4 min readJul 16, 2020

--

Some background

If you’re a developer looking to build a reliable, memory safe, high performance application today, Rust & Go are surely your options.

If you’re looking to get even more performance out of your internal applications, you might also want to look at using gRPC instead of a normal REST api. All these are solutions to reducing your computational overhead.

I try to compare various libraries to understand their performance and hope you find it helpful.

Libraries looked at

  • tower-grpc (Rust)
    A performant rust library, though it is replaced with tonic
  • grpc-go (Golang)
    The official go module for grpc.
  • grpc-rust (Rust)
    Still in development, another grpc in rust. Looked promising.
  • tonic (Rust)
    An improved update from tower-grpc supporting the new await syntax
  • grpc-node (NodeJs)
    Included for a benchmarking reference point.
  • grpc-rs (Rust — C bindings)
    A rust library that uses grpc

Benchmark Tool

For the benchmark I’ll use the equivalent of Hey for gRPC, ghz.

The test will focus on how much overhead there is if the same load of 10,000 concurrent requests is sent to servers using each of the libraries above. I will use the same helloworld greeter for each. TLS will not be enabled for any of the tests so we can get an idea of the raw performance with each.

grpc-go

Summary:
Count: 10000
Total: 229.40 ms
Slowest: 6.26 ms
Fastest: 0.11 ms
Average: 1.04 ms
Requests/sec: 43591.54
Response time histogram:
0.114 [1] |
0.728 [3355] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
1.343 [4580] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
1.957 [1519] |∎∎∎∎∎∎∎∎∎∎∎∎∎
2.572 [294] |∎∎∎
3.187 [36] |
3.801 [11] |
4.416 [74] |∎
5.030 [52] |
5.645 [45] |
6.259 [33] |
Latency distribution:
10 % in 0.46 ms
25 % in 0.64 ms
50 % in 0.88 ms
75 % in 1.24 ms
90 % in 1.68 ms
95 % in 2.00 ms
99 % in 4.77 ms
Status code distribution:
[OK] 10000 responses

tonic

Summary:
Count: 10000
Total: 581.81 ms
Slowest: 6.39 ms
Fastest: 0.17 ms
Average: 2.84 ms
Requests/sec: 17187.66
Response time histogram:
0.174 [1] |
0.796 [5] |
1.418 [6] |
2.040 [167] |∎
2.662 [3427] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.284 [5074] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.906 [1048] |∎∎∎∎∎∎∎∎
4.528 [217] |∎∎
5.150 [29] |
5.773 [21] |
6.395 [5] |
Latency distribution:
10 % in 2.35 ms
25 % in 2.55 ms
50 % in 2.79 ms
75 % in 3.07 ms
90 % in 3.39 ms
95 % in 3.66 ms
99 % in 4.22 ms
Status code distribution:
[OK] 10000 responses

grpc-node

Summary:
Count: 10000
Total: 589.25 ms
Slowest: 12.56 ms
Fastest: 1.21 ms
Average: 2.88 ms
Requests/sec: 16970.81
Response time histogram:
1.206 [1] |
2.341 [2592] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.476 [5516] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
4.612 [1588] |∎∎∎∎∎∎∎∎∎∎∎∎
5.747 [180] |∎
6.882 [42] |
8.017 [32] |
9.152 [5] |
10.288 [20] |
11.423 [6] |
12.558 [18] |
Latency distribution:
10 % in 2.14 ms
25 % in 2.33 ms
50 % in 2.62 ms
75 % in 3.24 ms
90 % in 3.85 ms
95 % in 4.26 ms
99 % in 6.41 ms
Status code distribution:
[OK] 10000 responses

tower-grpc

Summary:
Count: 10000
Total: 571.88 ms
Slowest: 10.59 ms
Fastest: 0.25 ms
Average: 2.76 ms
Requests/sec: 17486.12
Response time histogram:
0.246 [1] |
1.280 [139] |∎
2.314 [2927] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.348 [5198] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
4.382 [1390] |∎∎∎∎∎∎∎∎∎∎∎
5.416 [114] |∎
6.450 [61] |
7.484 [80] |∎
8.518 [47] |
9.552 [31] |
10.586 [12] |
Latency distribution:
10 % in 1.80 ms
25 % in 2.20 ms
50 % in 2.65 ms
75 % in 3.13 ms
90 % in 3.64 ms
95 % in 4.05 ms
99 % in 7.18 ms
Status code distribution:
[OK] 10000 responses

grpc-rust

Summary:
Count: 10000
Total: 479.30 ms
Slowest: 8.15 ms
Fastest: 0.90 ms
Average: 2.34 ms
Requests/sec: 20863.64
Response time histogram:
0.901 [1] |
1.626 [193] |∎
2.351 [5587] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.076 [3562] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.801 [506] |∎∎∎∎
4.526 [119] |∎
5.251 [16] |
5.976 [5] |
6.701 [2] |
7.426 [2] |
8.151 [7] |
Latency distribution:
10 % in 1.87 ms
25 % in 2.03 ms
50 % in 2.25 ms
75 % in 2.57 ms
90 % in 2.90 ms
95 % in 3.23 ms
99 % in 4.11 ms
Status code distribution:
[OK] 10000 responses

Grpc-rs

Summary:
Count: 10000
Total: 289.82 ms
Slowest: 4.22 ms
Fastest: 0.22 ms
Average: 1.36 ms
Requests/sec: 34504.74
Response time histogram:
0.222 [1] |
0.621 [195] |∎∎
1.021 [1829] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
1.420 [4059] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
1.820 [2625] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
2.219 [972] |∎∎∎∎∎∎∎∎∎∎
2.618 [239] |∎∎
3.018 [45] |
3.417 [18] |
3.817 [6] |
4.216 [11] |
Latency distribution:
10 % in 0.87 ms
25 % in 1.07 ms
50 % in 1.30 ms
75 % in 1.62 ms
90 % in 1.90 ms
95 % in 2.07 ms
99 % in 2.56 ms
Status code distribution:
[OK] 10000 responses

Summary

Total Reponse Time in Milliseconds. Lower is better.
Throughput (Requests/second). Higher is better.

Conclusion

The results indicate rust is on par with node in terms of performance, which was a bit of a shocking result.

The go library was extremely performant, both in concurrency & minimal overhead.

If anyone has some thoughts to why the go library seems to be so performant relatively give me a shout out!

UPDATE: Thanks to Trisfald for suggesting grpc-rs as a (presently) more performant incarnation of gRPC in rust.

--

--