Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 79 additions & 11 deletions http2/benchmark.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,99 @@
import argparse
import shlex
import subprocess
import time
import os
import signal

def run_benchmark():
print(f"Running HTTP/2 without TLS benchmark ...")
BUILD_DIR = "./build"
SERVER_BINARY_NAME = "h2_echo_server"
CLIENT_BINARY_NAME = "h2_bench_client"
SERVER_BINARY_PATH = os.path.join(BUILD_DIR, SERVER_BINARY_NAME)
CLIENT_BINARY_PATH = os.path.join(BUILD_DIR, CLIENT_BINARY_NAME)

server_process = subprocess.Popen(["./build/h2_echo_server"])
time.sleep(1) # wait for server to start

def _client_host_from_remote_target(remote_host):
if "@" in remote_host:
return remote_host.split("@", 1)[1]
return remote_host


def _start_server(remote_host):
if not remote_host:
return subprocess.Popen([SERVER_BINARY_PATH])

remote_binary_path = f"/tmp/{SERVER_BINARY_NAME}"

subprocess.run(
["ssh", remote_host, f"rm -f {shlex.quote(remote_binary_path)}"],
check=True,
)
subprocess.run(
["scp", SERVER_BINARY_PATH, f"{remote_host}:{remote_binary_path}"],
check=True,
)

remote_command = (
f"chmod +x {shlex.quote(remote_binary_path)} && "
f"{shlex.quote(remote_binary_path)}"
)

# Pass the `-t` option multiple times to ensure a pseudo-terminal is allocated, so that we can send a SIGTERM signal to the remote process, not the `ssh` process itself. This allows us to properly terminate the remote server when the benchmark is done.
return subprocess.Popen(["ssh", "-tt", remote_host, remote_command], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)


def run_benchmark(remote_host=None):
print("Running HTTP/2 without TLS benchmark ...")

client_host = "127.0.0.1"
if remote_host:
client_host = _client_host_from_remote_target(remote_host)
print(f"Starting remote server via SSH on {remote_host} ...")
else:
print("Starting local server ...")

server_process = _start_server(remote_host)
time.sleep(2) # wait for server to start

if server_process.poll() is not None:
raise RuntimeError(f"{SERVER_BINARY_NAME} failed to start")

try:
subprocess.run([
"./build/h2_bench_client",
CLIENT_BINARY_PATH,
"--stderrthreshold=0",
"--benchmark_counters_tabular=true",
], capture_output=False, text=True)
f"--host={client_host}",
], capture_output=False, text=True, check=True)
except subprocess.CalledProcessError as e:
print(f"Error running HTTP/2 client:\n{e.output}")
print(f"Error running HTTP/2 client: {e}")
except Exception as e:
print(f"Failed to start/run HTTP/2 client: {e}")
finally:
server_process.terminate()
server_process.wait()
if server_process.poll() is None:
server_process.terminate()
server_process.wait()


if __name__ == "__main__":
if not os.path.isdir("build"):
parser = argparse.ArgumentParser(description="Run HTTP/2 benchmark")
parser.add_argument(
"--remote-host",
help="SSH target for remote h2_echo_server, e.g. user@10.0.0.5",
)
args = parser.parse_args()

if not os.path.isdir(BUILD_DIR):
print("Error: 'build' directory not found. Please compile the project first.")
exit(1)

if not os.path.isfile(SERVER_BINARY_PATH):
print(f"Error: '{SERVER_BINARY_PATH}' not found. Please compile the project first.")
exit(1)

if not os.path.isfile(CLIENT_BINARY_PATH):
print(f"Error: '{CLIENT_BINARY_PATH}' not found. Please compile the project first.")
exit(1)

print("Starting benchmarks...")
run_benchmark()
run_benchmark(remote_host=args.remote_host)
17 changes: 12 additions & 5 deletions http2/results.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
2026-03-11T19:12:36+00:00
2026-03-18T03:53:29+00:00
Running ./build/h2_bench_client
Run on (2 X 2450 MHz CPU s)
CPU Caches:
L1 Data 32 KiB (x1)
L1 Instruction 32 KiB (x1)
L2 Unified 512 KiB (x1)
L3 Unified 32768 KiB (x1)
Load Average: 0.27, 0.65, 0.49
Load Average: 0.48, 0.24, 0.11
---------------------------------------------------------------------------------------------------------------------------------------------
Benchmark Time CPU Iterations items_per_second p10_latency_ms p50_latency_ms p90_latency_ms p99_latency_ms
---------------------------------------------------------------------------------------------------------------------------------------------
BM_HTTP2Client/0 61713 ns 27767 ns 20047 36.0145k/s 0.042291 0.05026 0.06686 0.12446
BM_HTTP2Client/1024 70859 ns 36578 ns 20824 27.3387k/s 0.06296 0.06762 0.07477 0.09962
BM_HTTP2Client/131072 46753895 ns 454124 ns 100 2.20204k/s 42.962 43.9969 87.689 91.4688
BM_HTTP2Client/0 69540 ns 35423 ns 20002 28.23k/s 0.06082 0.06532 0.07368 0.14472
BM_HTTP2Client/1024 73996 ns 37409 ns 19361 26.7319k/s 0.05987 0.06916 0.07918 0.177689
BM_HTTP2Client/2048 79365 ns 40345 ns 18347 24.7861k/s 0.06806 0.07213 0.08655 0.20568
BM_HTTP2Client/4096 97370 ns 48575 ns 15427 20.5865k/s 0.08071 0.08598 0.11125 0.27893
BM_HTTP2Client/8192 106721 ns 52152 ns 10000 19.1748k/s 0.07369 0.0967 0.14 0.29226
BM_HTTP2Client/16384 153717 ns 77756 ns 8547 12.8607k/s 0.12328 0.13408 0.172549 0.34857
BM_HTTP2Client/32768 214652 ns 121252 ns 5857 8.24726k/s 0.19138 0.20315 0.23542 0.404201
BM_HTTP2Client/65536 356799 ns 189867 ns 3626 5.26684k/s 0.28469 0.29941 0.3921 1.44324
BM_HTTP2Client/131072 487052 ns 320039 ns 2082 3.12462k/s 0.45225 0.4656 0.52445 0.77176
Starting benchmarks...
Running HTTP/2 without TLS benchmark ...
Starting local server ...