Home

Awesome

This repository contains the results and analysis after benchmarking the rustls library in different configurations. It also includes benchmarks of OpenSSL, which are interesting because rustls is aiming to be an OpenSSL replacement. The benchmarks are based on previous work by @ctz, who originally benchmarked rustls against OpenSSL in 2019 and published the results online.

1. Scenarios

The benchmarks focus on the following aspects of performance:

  1. Bulk data transfer throughput in MB/s;
  2. Handshake throughput (full, session id, tickets) in handshakes per second;
  3. Memory usage per connection.

For bulk transfers, we benchmark the following cipher suites: AES128-GCM_SHA256, AES256-GCM_SHA384 and CHACHA20_POLY1305. We test them all using TLS 1.2, and additionally test AES256-GCM_SHA384 using TLS 1.3 as a sanity check (performance should be a tiny bit better than TLS 1.2).

For handshakes, we benchmark ECDHE + RSA using a 2048-bit key. We use both TLS 1.2 and TLS 1.3.

As an example, see below a comparison between OpenSSL and the aws-lc rustls configuration, showing the speedup / slowdown factor in the rightmost column:

<details> <summary>Toggle comparison</summary>
ScenarioOpenSSL (3.2.0)Rustls (0.22.0, aws-lc)Factor
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_sent6503.586712.291.03x
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_received7234.116017.820.83x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_sent6162.576193.181.00x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_received6609.275669.770.86x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_sent2998.061750.110.58x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_received3107.171731.270.56x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_sent6041.826256.941.04x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_received6406.445945.020.93x
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_client2581.164991.731.93x
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_server2120.951485.950.70x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_client21514.449893.992.32x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_server23183.841846.341.80x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_client21491.347419.842.21x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_server20190.136518.871.81x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_client2273.574683.462.06x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_server1874.11362.790.73x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_client3415.3511272.983.30x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_server3060.177434.412.43x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_client5182.5111244.792.17x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_server4718.397347.911.56x
</details>

2. Methodology

System configuration

We ran the benchmarks on a bare-metal server with the following characteristics:

OpenSSL

The most recent version of OpenSSL at the time of this research was 3.2.0. We benchmarked it using ctz's openssl-bench repository, which expects a built OpenSSL tree in ../openssl/ and the rustls repository in ../rustls. We ran the benchmarks using BENCH_MULTIPLIER=8 setarch -R make measure (setarch is used here and elsewhere to disable ASLR and thereby reduce noise).

rustls

The most recent version of rustls at the time of this research was 0.22.0. We benchmarked it using bench.rs, included in the repository. We ran it using BENCH_MULTIPLIER=8 setarch -R make -f admin/bench-measure.mk measure (the makefile assumes that the bench.rs binary has already been built, see below for details).

When building rustls, we tested a matrix of configurations involving 3 variables:

The cryptography backend was specified through cargo features:

The C/C++ toolchain was specified through Debian's update-alternatives utility. We manually checked the build artifacts of ring and aws-lc-rs to ensure they were indeed built with the right toolchain (using objdump -s --section .comment target/release/deps/artifact.rlib).

The allocator was specified by manually modifying bench.rs to use Jemallocator (requires cargo add --dev jemallocator -p rustls):

#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;

Result collection and reporting

For each OpenSSL and rustls configuration, we ran the benchmarks 30 times and stored their stdout output in data. We experimentally settled on the median as the value used for reporting due to its stability (taking the maximum or the average was more prone to noise).

Check out our Python notebook in notebook.ipynb, which has code to generate comparison tables in markdown format (and could be modified to generate CSV or something else that suits your needs). Hint: use poetry install --no-root to generate a virtual environment and Visual Studio Code to play with the notebook.

The memory benchmarks are much more deterministic, so we only ran them once.

3. Conclusions

Highlighted results from our benchmarks:

Additional results that might be interesting:

Additional thoughts:

4. Appendix: result comparisons

jemalloc's effect on rustls + ring bulk transfers

ScenarioRustls (0.22.0, ring_clang, malloc)Rustls (0.22.0, ring_clang, jemalloc)Factor
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_sent2281.584150.531.82x
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_received4161.154104.50.99x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_sent2144.813700.621.73x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_received3710.153660.960.99x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_sent1291.561739.361.35x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_received1739.01732.481.00x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_sent2141.693738.281.75x

jemalloc's effect on rustls + aws-lc bulk transfers

ScenarioRustls (0.22.0, aws-lc_gcc, malloc)Rustls (0.22.0, aws-lc_gcc, jemalloc)Factor
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_sent2847.446712.292.36x
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_received6135.096017.820.98x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_sent2788.356193.182.22x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_received5776.345669.770.98x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_sent1287.941750.111.36x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_received1738.971731.271.00x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_sent2791.746256.942.24x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_received6093.485945.020.98x

clang's effect on rustls + ring handshakes

ScenarioRustls (0.22.0, ring_gcc, jemalloc)Rustls (0.22.0, ring_clang, jemalloc)Factor
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_client3671.064208.91.15x
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_server1427.561494.011.05x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_client47940.5947471.950.99x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_server42929.4541969.720.98x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_client45656.7145306.780.99x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_server39121.8938510.330.98x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_client4060.864216.281.04x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_server1373.621433.711.04x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_client12008.7512616.71.05x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_server7067.548944.51.27x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_client12031.7512615.561.05x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_server7036.618883.551.26x

clang's effect on OpenSSL bulk transfers

ScenarioOpenSSL (3.2.0_gcc)OpenSSL (3.2.0_clang)Factor
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_sent6503.586376.770.98x
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_received7234.117055.490.98x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_sent6162.576140.831.00x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_received6609.276671.441.01x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_sent2998.061676.110.56x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_received3107.171708.90.55x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_sent6041.826064.641.00x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_received6406.446405.741.00x

ring vs aws-lc

ScenarioRustls (0.22.0, ring_clang, jemalloc)Rustls (0.22.0, aws-lc_clang, jemalloc)Factor
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_sent4150.536573.11.58x
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_received4104.56036.71.47x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_sent3700.626171.021.67x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_received3660.965680.811.55x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_sent1739.361740.761.00x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_received1732.481731.351.00x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_sent3738.286225.51.67x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_received3657.535987.831.64x
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_client4208.94959.081.18x
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_server1494.011481.990.99x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_client47471.9550580.451.07x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_server41969.7242275.271.01x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_client45306.7848077.081.06x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_server38510.3336683.260.95x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_client4216.284680.921.11x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_server1433.711363.290.95x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_client12616.711522.160.91x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_server8944.57529.760.84x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_client12615.5611519.540.91x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_server8883.557482.640.84x

OpenSSL vs rustls + aws-lc

ScenarioOpenSSL (3.2.0)Rustls (0.22.0, aws-lc_gcc, jemalloc)Factor
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_sent6503.586712.291.03x
bulk_1.2_ECDHE_RSA_AES128-GCM_SHA256_received7234.116017.820.83x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_sent6162.576193.181.00x
bulk_1.2_ECDHE_RSA_AES256-GCM_SHA384_received6609.275669.770.86x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_sent2998.061750.110.58x
bulk_1.2_ECDHE_RSA_CHACHA20_POLY1305_received3107.171731.270.56x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_sent6041.826256.941.04x
bulk_1.3_ECDHE_RSA_AES256-GCM_SHA384_received6406.445945.020.93x
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_client2581.164991.731.93x
handshake-full_1.2_ECDHE_RSA_AES256-GCM_SHA384_server2120.951485.950.70x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_client21514.449893.992.32x
handshake-session-id_1.2_ECDHE_RSA_AES256-GCM_SHA384_server23183.841846.341.80x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_client21491.347419.842.21x
handshake-ticket_1.2_ECDHE_RSA_AES256-GCM_SHA384_server20190.136518.871.81x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_client2273.574683.462.06x
handshake-full_1.3_ECDHE_RSA_AES256-GCM_SHA384_server1874.11362.790.73x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_client3415.3511272.983.30x
handshake-session-id_1.3_ECDHE_RSA_AES256-GCM_SHA384_server3060.177434.412.43x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_client5182.5111244.792.17x
handshake-ticket_1.3_ECDHE_RSA_AES256-GCM_SHA384_server4718.397347.911.56x

OpenSSL vs rustls + aws-lc memory usage

Peak memory usage of a process which creates N sessions (N/2 client sessions associated with N/2 server sessions) and then takes these sessions through a full handshake in lockstep.

Cipher suiteNOpenSSL 3.2.0 (KiB)Rustls 0.22.0, aws-lc_gcc, jemalloc (KiB)vs.
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (TLS 1.2)1001586011540-27%
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (TLS 1.2)10008330021044-75%
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (TLS 1.2)500038247657124-85%
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (TLS 1.2)10000756380104932-86%
TLS_AES_256_GCM_SHA384 (TLS 1.3)1001554411536-0.26
TLS_AES_256_GCM_SHA384 (TLS 1.3)10007671221792-72%
TLS_AES_256_GCM_SHA384 (TLS 1.3)500034864070728-80%
TLS_AES_256_GCM_SHA384 (TLS 1.3)10000688436131816-81%

Note that, when the number of handshakes is small, the difference between Rustls and OpenSSL narrows. This is due to jemalloc reserving more memory than strictly necessary.