From d3250c1cb9babb2ca9b81957a83026f64ce0291c Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 13:49:06 +0200 Subject: [PATCH 01/15] shorter warmup var --- scripts/perfstat.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/perfstat.sh b/scripts/perfstat.sh index 57cee3e3..1549fc9b 100755 --- a/scripts/perfstat.sh +++ b/scripts/perfstat.sh @@ -26,7 +26,8 @@ for pidfile in /tmp/loona-perfstat/*.PID; do done #PERF_EVENTS="cpu-clock,context-switches,cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,$(paste -sd ',' syscalls)" -PERF_EVENTS="cpu-clock,cycles,branch-misses,cache-misses,page-faults,$(paste -sd ',' syscalls)" +#PERF_EVENTS="cpu-clock,cycles,branch-misses,cache-misses,page-faults,$(paste -sd ',' syscalls)" +PERF_EVENTS="cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,$(paste -sd ',' syscalls)" LOONA_DIR=~/bearcove/loona @@ -95,7 +96,7 @@ ENDPOINT="${ENDPOINT:-/repeat-4k-blocks/128}" RPS="${RPS:-2}" CONNS="${CONNS:-40}" STREAMS="${STREAMS:-8}" -WARM_UP_TIME="${WARM_UP_TIME:-5}" +WARMUP="${WARMUP:-5}" DURATION="${DURATION:-20}" TIMES="${TIMES:-1}" @@ -111,7 +112,7 @@ else exit 1 fi -echo -e "\033[1;34m📊 Benchmark parameters: RPS=$RPS, CONNS=$CONNS, STREAMS=$STREAMS, WARM_UP_TIME=$WARM_UP_TIME, DURATION=$DURATION, TIMES=$TIMES\033[0m" +echo -e "\033[1;34m📊 Benchmark parameters: RPS=$RPS, CONNS=$CONNS, STREAMS=$STREAMS, WARMUP=$WARMUP, DURATION=$DURATION, TIMES=$TIMES\033[0m" for server in "${!servers[@]}"; do read -r PID ADDR <<< "${servers[$server]}" @@ -133,10 +134,10 @@ for server in "${!servers[@]}"; do echo "===================================================" ssh brat "${remote_command[@]}" & SSH_PID=$! - sleep "$WARM_UP_TIME" - ACTUAL_DURATION=$((DURATION - WARM_UP_TIME - 1)) - echo "Starting perf for $ACTUAL_DURATION seconds..." - perf stat -e "$PERF_EVENTS" -p "$PID" -- sleep "$ACTUAL_DURATION" + sleep "$WARMUP" + MEASURE_DURATION=$((DURATION - WARMUP - 1)) + echo "Starting perf for $MEASURE_DURATION seconds..." + perf stat -e "$PERF_EVENTS" -p "$PID" -- sleep "$MEASURE_DURATION" echo "Starting perf... done!" wait $SSH_PID done From 9353b34326f96ccb3a4fdaf73ba8a7ea7832c505 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 14:04:45 +0200 Subject: [PATCH 02/15] Better benchmarks --- scripts/perfstat.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/perfstat.sh b/scripts/perfstat.sh index 1549fc9b..3f984d2a 100755 --- a/scripts/perfstat.sh +++ b/scripts/perfstat.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env -S bash -euo pipefail +#!/usr/bin/env -S bash -euox pipefail . /root/.cargo/env @@ -25,6 +25,9 @@ for pidfile in /tmp/loona-perfstat/*.PID; do fi done +# Kill older remote processes +ssh brat "pkill -9 h2load" || true + #PERF_EVENTS="cpu-clock,context-switches,cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,$(paste -sd ',' syscalls)" #PERF_EVENTS="cpu-clock,cycles,branch-misses,cache-misses,page-faults,$(paste -sd ',' syscalls)" PERF_EVENTS="cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,$(paste -sd ',' syscalls)" From 41f39a607598a2a50e1f460258ec8ad0b9e02144 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 14:10:45 +0200 Subject: [PATCH 03/15] port perfstat to python --- scripts/perfstat.py | 149 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100755 scripts/perfstat.py diff --git a/scripts/perfstat.py b/scripts/perfstat.py new file mode 100755 index 00000000..a51dd0c3 --- /dev/null +++ b/scripts/perfstat.py @@ -0,0 +1,149 @@ +#!/usr/bin/env -S uv run --with 'termcolor' + +import os +import signal +import subprocess +import sys +import time +from pathlib import Path +from termcolor import colored + +# Change to the script's directory +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Create a new process group +os.setpgrp() + +# Set trap to kill the process group on script exit +def kill_group(): + os.killpg(0, signal.SIGTERM) + +signal.signal(signal.SIGTERM, lambda signum, frame: kill_group()) + +# Create directory if it doesn't exist +Path("/tmp/loona-perfstat").mkdir(parents=True, exist_ok=True) + +# Kill older processes +for pidfile in Path("/tmp/loona-perfstat").glob("*.PID"): + if pidfile.is_file(): + with open(pidfile) as f: + pid = f.read().strip() + if pid != str(os.getpid()): + try: + os.kill(int(pid), signal.SIGTERM) + except ProcessLookupError: + pass + pidfile.unlink() + +# Kill older remote processes +subprocess.run(["ssh", "brat", "pkill -9 h2load"], check=False) + +PERF_EVENTS = f"cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,{','.join(open('syscalls').read().splitlines())}" + +LOONA_DIR = os.path.expanduser("~/bearcove/loona") + +# Build the servers +subprocess.run(["cargo", "build", "--release", "--manifest-path", f"{LOONA_DIR}/Cargo.toml", "-F", "tracing/release_max_level_info"], check=True) + +# Set protocol, default to h2c +PROTO = os.environ.get("PROTO", "h2c") +os.environ["PROTO"] = PROTO + +OUR_PUBLIC_IP = subprocess.check_output(["curl", "-4", "ifconfig.me"]).decode().strip() +HTTP_OR_HTTPS = "https" if PROTO == "tls" else "http" + +# Launch hyper server +hyper_env = os.environ.copy() +hyper_env.update({"ADDR": "0.0.0.0", "PORT": "8001"}) +hyper_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-hyper"], env=hyper_env) +HYPER_PID = hyper_process.pid +with open("/tmp/loona-perfstat/hyper.PID", "w") as f: + f.write(str(HYPER_PID)) +print(f"hyper PID: {HYPER_PID}") + +# Launch loona server +loona_env = os.environ.copy() +loona_env.update({"ADDR": "0.0.0.0", "PORT": "8002"}) +loona_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-loona"], env=loona_env) +LOONA_PID = loona_process.pid +with open("/tmp/loona-perfstat/loona.PID", "w") as f: + f.write(str(LOONA_PID)) +print(f"loona PID: {LOONA_PID}") + +HYPER_ADDR = f"{HTTP_OR_HTTPS}://{OUR_PUBLIC_IP}:8001" +LOONA_ADDR = f"{HTTP_OR_HTTPS}://{OUR_PUBLIC_IP}:8002" + +# Declare h2load args based on PROTO +H2LOAD_ARGS = [] +if PROTO == "h1": + print("Error: h1 is not supported") + sys.exit(1) +elif PROTO == "h2c": + pass +elif PROTO == "tls": + ALPN_LIST = os.environ.get("ALPN_LIST", "h2,http/1.1") + H2LOAD_ARGS = [f"--alpn-list={ALPN_LIST}"] +else: + print(f"Error: Unknown PROTO '{PROTO}'") + sys.exit(1) + +servers = { + "hyper": (HYPER_PID, HYPER_ADDR), + "loona": (LOONA_PID, LOONA_ADDR) +} + +SERVER = os.environ.get("SERVER") +if SERVER: + if SERVER in servers: + servers = {SERVER: servers[SERVER]} + else: + print(f"Error: SERVER '{SERVER}' not found in the list of servers.") + sys.exit(1) + +H2LOAD = "/nix/var/nix/profiles/default/bin/h2load" + +ENDPOINT = os.environ.get("ENDPOINT", "/repeat-4k-blocks/128") +RPS = os.environ.get("RPS", "2") +CONNS = os.environ.get("CONNS", "40") +STREAMS = os.environ.get("STREAMS", "8") +WARMUP = int(os.environ.get("WARMUP", "5")) +DURATION = int(os.environ.get("DURATION", "20")) +TIMES = int(os.environ.get("TIMES", "1")) + +# Set MODE to 'stat' if not specified +MODE = os.environ.get("MODE", "stat") + +if MODE == "record": + PERF_CMD = f"perf record -F 99 -e {PERF_EVENTS} -p" +elif MODE == "stat": + PERF_CMD = f"perf stat -e {PERF_EVENTS} -p" +else: + print(f"Error: Unknown MODE '{MODE}'") + sys.exit(1) + +print(colored(f"📊 Benchmark parameters: RPS={RPS}, CONNS={CONNS}, STREAMS={STREAMS}, WARMUP={WARMUP}, DURATION={DURATION}, TIMES={TIMES}", "blue")) + +for server, (PID, ADDR) in servers.items(): + print(colored(f"Loona Git SHA: {subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=os.path.expanduser('~/bearcove/loona')).decode().strip()}", "cyan")) + print(colored(f"🚀 Benchmarking {open(f'/proc/{PID}/cmdline').read().replace(chr(0), ' ').strip()}", "yellow")) + remote_command = [H2LOAD] + H2LOAD_ARGS + ["--rps", RPS, "-c", CONNS, "-m", STREAMS, "--duration", str(DURATION), f"{ADDR}{ENDPOINT}"] + + if MODE == "record": + samply_process = subprocess.Popen(["samply", "record", "-p", str(PID)]) + with open("/tmp/loona-perfstat/samply.PID", "w") as f: + f.write(str(samply_process.pid)) + subprocess.run(["ssh", "brat"] + remote_command, check=True) + samply_process.send_signal(signal.SIGINT) + samply_process.wait() + else: + for i in range(1, TIMES + 1): + print("===================================================") + print(colored(f"🏃 Run {i} of {TIMES} for {server} server 🏃", "magenta")) + print("===================================================") + ssh_process = subprocess.Popen(["ssh", "brat"] + remote_command) + time.sleep(WARMUP) + MEASURE_DURATION = DURATION - WARMUP - 1 + print(f"Starting perf for {MEASURE_DURATION} seconds...") + subprocess.run(["perf", "stat", "-e", PERF_EVENTS, "-p", str(PID), "--", "sleep", str(MEASURE_DURATION)], check=True) + print("Starting perf... done!") + ssh_process.wait() From bcd7e2b91b5cbe95926e10559697562d5de62c46 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 14:22:38 +0200 Subject: [PATCH 04/15] messing with perf rig --- scripts/perfstat.py | 95 ++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/scripts/perfstat.py b/scripts/perfstat.py index a51dd0c3..01eadbf5 100755 --- a/scripts/perfstat.py +++ b/scripts/perfstat.py @@ -1,24 +1,24 @@ -#!/usr/bin/env -S uv run --with 'termcolor' - -import os +#!/usr/bin/env -S uv run --with 'termcolor~=2.4.0' --with 'pandas~=2.2.2' --with 'openpyxl~=3.1.5' + import os import signal import subprocess import sys import time from pathlib import Path from termcolor import colored +import pandas as pd # Change to the script's directory os.chdir(os.path.dirname(os.path.abspath(__file__))) -# Create a new process group -os.setpgrp() - # Set trap to kill the process group on script exit def kill_group(): os.killpg(0, signal.SIGTERM) -signal.signal(signal.SIGTERM, lambda signum, frame: kill_group()) +def abort_and_cleanup(signum, frame): + print(colored("\nAborting and cleaning up...", "red")) + kill_group() + sys.exit(1) # Create directory if it doesn't exist Path("/tmp/loona-perfstat").mkdir(parents=True, exist_ok=True) @@ -55,7 +55,7 @@ def kill_group(): # Launch hyper server hyper_env = os.environ.copy() hyper_env.update({"ADDR": "0.0.0.0", "PORT": "8001"}) -hyper_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-hyper"], env=hyper_env) +hyper_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-hyper"], env=hyper_env, preexec_fn=os.setpgrp) HYPER_PID = hyper_process.pid with open("/tmp/loona-perfstat/hyper.PID", "w") as f: f.write(str(HYPER_PID)) @@ -64,7 +64,7 @@ def kill_group(): # Launch loona server loona_env = os.environ.copy() loona_env.update({"ADDR": "0.0.0.0", "PORT": "8002"}) -loona_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-loona"], env=loona_env) +loona_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-loona"], env=loona_env, preexec_fn=os.setpgrp) LOONA_PID = loona_process.pid with open("/tmp/loona-perfstat/loona.PID", "w") as f: f.write(str(LOONA_PID)) @@ -113,37 +113,50 @@ def kill_group(): # Set MODE to 'stat' if not specified MODE = os.environ.get("MODE", "stat") -if MODE == "record": - PERF_CMD = f"perf record -F 99 -e {PERF_EVENTS} -p" -elif MODE == "stat": - PERF_CMD = f"perf stat -e {PERF_EVENTS} -p" -else: - print(f"Error: Unknown MODE '{MODE}'") - sys.exit(1) - print(colored(f"📊 Benchmark parameters: RPS={RPS}, CONNS={CONNS}, STREAMS={STREAMS}, WARMUP={WARMUP}, DURATION={DURATION}, TIMES={TIMES}", "blue")) -for server, (PID, ADDR) in servers.items(): - print(colored(f"Loona Git SHA: {subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=os.path.expanduser('~/bearcove/loona')).decode().strip()}", "cyan")) - print(colored(f"🚀 Benchmarking {open(f'/proc/{PID}/cmdline').read().replace(chr(0), ' ').strip()}", "yellow")) - remote_command = [H2LOAD] + H2LOAD_ARGS + ["--rps", RPS, "-c", CONNS, "-m", STREAMS, "--duration", str(DURATION), f"{ADDR}{ENDPOINT}"] - - if MODE == "record": - samply_process = subprocess.Popen(["samply", "record", "-p", str(PID)]) - with open("/tmp/loona-perfstat/samply.PID", "w") as f: - f.write(str(samply_process.pid)) - subprocess.run(["ssh", "brat"] + remote_command, check=True) - samply_process.send_signal(signal.SIGINT) - samply_process.wait() - else: - for i in range(1, TIMES + 1): - print("===================================================") - print(colored(f"🏃 Run {i} of {TIMES} for {server} server 🏃", "magenta")) - print("===================================================") - ssh_process = subprocess.Popen(["ssh", "brat"] + remote_command) - time.sleep(WARMUP) - MEASURE_DURATION = DURATION - WARMUP - 1 - print(f"Starting perf for {MEASURE_DURATION} seconds...") - subprocess.run(["perf", "stat", "-e", PERF_EVENTS, "-p", str(PID), "--", "sleep", str(MEASURE_DURATION)], check=True) - print("Starting perf... done!") - ssh_process.wait() +try: + for server, (PID, ADDR) in servers.items(): + print(colored(f"Loona Git SHA: {subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=os.path.expanduser('~/bearcove/loona')).decode().strip()}", "cyan")) + print(colored(f"🚀 Benchmarking {open(f'/proc/{PID}/cmdline').read().replace(chr(0), ' ').strip()}", "yellow")) + h2load_cmd = [H2LOAD] + H2LOAD_ARGS + ["--rps", RPS, "-c", CONNS, "-m", STREAMS, "--duration", str(DURATION), f"{ADDR}{ENDPOINT}"] + + if MODE == "record": + samply_process = subprocess.Popen(["samply", "record", "-p", str(PID)]) + with open("/tmp/loona-perfstat/samply.PID", "w") as f: + f.write(str(samply_process.pid)) + subprocess.run(["ssh", "brat"] + h2load_cmd, check=True) + samply_process.send_signal(signal.SIGINT) + samply_process.wait() + else: + for i in range(1, TIMES + 1): + print(colored(f"🏃 Run {i} of {TIMES} for {server} server 🏃 (will take {DURATION} seconds)", "magenta")) + ssh_process = subprocess.Popen(["ssh", "brat"] + h2load_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setpgrp) + time.sleep(WARMUP) + MEASURE_DURATION = DURATION - WARMUP - 1 + perf_cmd = ["perf", "stat", "-e", PERF_EVENTS, "-p", str(PID), "--", "sleep", str(MEASURE_DURATION)] + perf_process = subprocess.Popen(perf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setpgrp) + _, perf_stderr = perf_process.communicate() + perf_output = perf_stderr.decode('utf-8') + perf_lines = [line.strip().split() for line in perf_output.split('\n') if line.strip() and not line.startswith('#')] + perf_csv = ','.join([line[-1] for line in perf_lines if len(line) > 1]) + + # Create a DataFrame from the CSV string + df = pd.DataFrame([perf_csv.split(',')], columns=[line[-1] for line in perf_lines if len(line) > 1]) + + # Save the DataFrame as an Excel file + excel_filename = f"/tmp/loona-perfstat/{server}_run_{i}.xlsx" + df.to_excel(excel_filename, index=False) + print(colored(f"Saved performance data to {excel_filename}", "green")) + + ssh_process.wait() + + if ssh_process.returncode != 0: + output, error = ssh_process.communicate() + print("h2load command failed. Output:") + print(output.decode()) + print("Error:") + print(error.decode()) +except KeyboardInterrupt: + print(colored("\nKeyboard interrupt detected. Cleaning up...", "red")) + abort_and_cleanup(None, None) From 39292874ec3a9eb386e1e953c8108a0f30e265c6 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 15:22:53 +0200 Subject: [PATCH 05/15] some bench rig cleanups --- scripts/{perfstat.py => bench.py} | 72 +++++++++++---- scripts/perfstat.sh | 148 ------------------------------ scripts/syscalls | 4 - 3 files changed, 56 insertions(+), 168 deletions(-) rename scripts/{perfstat.py => bench.py} (70%) delete mode 100755 scripts/perfstat.sh delete mode 100644 scripts/syscalls diff --git a/scripts/perfstat.py b/scripts/bench.py similarity index 70% rename from scripts/perfstat.py rename to scripts/bench.py index 01eadbf5..a42f9910 100755 --- a/scripts/perfstat.py +++ b/scripts/bench.py @@ -1,5 +1,6 @@ -#!/usr/bin/env -S uv run --with 'termcolor~=2.4.0' --with 'pandas~=2.2.2' --with 'openpyxl~=3.1.5' - import os +#!/usr/bin/env -S PYTHONUNBUFFERED=1 FORCE_COLOR=1 uv run --with 'termcolor~=2.4.0' --with 'pandas~=2.2.2' --with 'openpyxl~=3.1.5' + +import os import signal import subprocess import sys @@ -38,8 +39,19 @@ def abort_and_cleanup(signum, frame): # Kill older remote processes subprocess.run(["ssh", "brat", "pkill -9 h2load"], check=False) -PERF_EVENTS = f"cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,{','.join(open('syscalls').read().splitlines())}" - +PERF_EVENTS = [ + "cycles", + "instructions", + "branches", + "branch-misses", + "cache-references", + "cache-misses", + "page-faults", + "syscalls:sys_enter_write", + "syscalls:sys_enter_writev", + "syscalls:sys_enter_epoll_wait", + "syscalls:sys_enter_io_uring_enter" +] LOONA_DIR = os.path.expanduser("~/bearcove/loona") # Build the servers @@ -49,7 +61,25 @@ def abort_and_cleanup(signum, frame): PROTO = os.environ.get("PROTO", "h2c") os.environ["PROTO"] = PROTO -OUR_PUBLIC_IP = subprocess.check_output(["curl", "-4", "ifconfig.me"]).decode().strip() +# Get the default route interface +default_route = subprocess.check_output(["ip", "route", "show", "0.0.0.0/0"]).decode().strip() +default_interface = default_route.split()[4] + +# Get the IP address of the default interface +ip_addr_output = subprocess.check_output(["ip", "addr", "show", "dev", default_interface]).decode().strip() + +OUR_PUBLIC_IP = None +for line in ip_addr_output.split('\n'): + if 'inet ' in line: + OUR_PUBLIC_IP = line.split()[1].split('/')[0] + break + +if not OUR_PUBLIC_IP: + print(colored("Error: Could not determine our public IP address", "red")) + sys.exit(1) + +print(f"📡 Our public IP address is {OUR_PUBLIC_IP}") + HTTP_OR_HTTPS = "https" if PROTO == "tls" else "http" # Launch hyper server @@ -59,7 +89,6 @@ def abort_and_cleanup(signum, frame): HYPER_PID = hyper_process.pid with open("/tmp/loona-perfstat/hyper.PID", "w") as f: f.write(str(HYPER_PID)) -print(f"hyper PID: {HYPER_PID}") # Launch loona server loona_env = os.environ.copy() @@ -68,7 +97,6 @@ def abort_and_cleanup(signum, frame): LOONA_PID = loona_process.pid with open("/tmp/loona-perfstat/loona.PID", "w") as f: f.write(str(LOONA_PID)) -print(f"loona PID: {LOONA_PID}") HYPER_ADDR = f"{HTTP_OR_HTTPS}://{OUR_PUBLIC_IP}:8001" LOONA_ADDR = f"{HTTP_OR_HTTPS}://{OUR_PUBLIC_IP}:8002" @@ -113,11 +141,12 @@ def abort_and_cleanup(signum, frame): # Set MODE to 'stat' if not specified MODE = os.environ.get("MODE", "stat") +LOONA_GIT_SHA = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=os.path.expanduser('~/bearcove/loona')).decode().strip() + print(colored(f"📊 Benchmark parameters: RPS={RPS}, CONNS={CONNS}, STREAMS={STREAMS}, WARMUP={WARMUP}, DURATION={DURATION}, TIMES={TIMES}", "blue")) try: for server, (PID, ADDR) in servers.items(): - print(colored(f"Loona Git SHA: {subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=os.path.expanduser('~/bearcove/loona')).decode().strip()}", "cyan")) print(colored(f"🚀 Benchmarking {open(f'/proc/{PID}/cmdline').read().replace(chr(0), ' ').strip()}", "yellow")) h2load_cmd = [H2LOAD] + H2LOAD_ARGS + ["--rps", RPS, "-c", CONNS, "-m", STREAMS, "--duration", str(DURATION), f"{ADDR}{ENDPOINT}"] @@ -134,19 +163,28 @@ def abort_and_cleanup(signum, frame): ssh_process = subprocess.Popen(["ssh", "brat"] + h2load_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setpgrp) time.sleep(WARMUP) MEASURE_DURATION = DURATION - WARMUP - 1 - perf_cmd = ["perf", "stat", "-e", PERF_EVENTS, "-p", str(PID), "--", "sleep", str(MEASURE_DURATION)] + perf_cmd = ["perf", "stat", "-x", ",", "-e", ",".join(PERF_EVENTS), "-p", str(PID), "--", "sleep", str(MEASURE_DURATION)] perf_process = subprocess.Popen(perf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setpgrp) - _, perf_stderr = perf_process.communicate() - perf_output = perf_stderr.decode('utf-8') - perf_lines = [line.strip().split() for line in perf_output.split('\n') if line.strip() and not line.startswith('#')] - perf_csv = ','.join([line[-1] for line in perf_lines if len(line) > 1]) + perf_stdout, perf_stderr = perf_process.communicate() - # Create a DataFrame from the CSV string - df = pd.DataFrame([perf_csv.split(',')], columns=[line[-1] for line in perf_lines if len(line) > 1]) + perf_output = perf_stdout.decode('utf-8') + perf_stderr.decode('utf-8') + perf_lines = [line.strip().split(',') for line in perf_output.split('\n') if line.strip() and not line.startswith('#')] + + # Create a dictionary to store the data + data = {} + for line in perf_lines: + if len(line) >= 3: + value, _, label = line[:3] + data[label] = value + + # Create a DataFrame from the dictionary + df = pd.DataFrame(data, index=[0]).T + df.columns = ['Value'] + df.index.name = 'Event' # Save the DataFrame as an Excel file excel_filename = f"/tmp/loona-perfstat/{server}_run_{i}.xlsx" - df.to_excel(excel_filename, index=False) + df.to_excel(excel_filename) print(colored(f"Saved performance data to {excel_filename}", "green")) ssh_process.wait() @@ -160,3 +198,5 @@ def abort_and_cleanup(signum, frame): except KeyboardInterrupt: print(colored("\nKeyboard interrupt detected. Cleaning up...", "red")) abort_and_cleanup(None, None) + +print(colored("✨ All done, good bye! 👋", "cyan")) diff --git a/scripts/perfstat.sh b/scripts/perfstat.sh deleted file mode 100755 index 3f984d2a..00000000 --- a/scripts/perfstat.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env -S bash -euox pipefail - -. /root/.cargo/env - -# Change to the script's directory -cd "$(dirname "$0")" - -# Create a new process group -set -m - -# Set trap to kill the process group on script exit -trap 'kill -TERM -$$' EXIT - -# Create directory if it doesn't exist -mkdir -p /tmp/loona-perfstat - -# Kill older processes -for pidfile in /tmp/loona-perfstat/*.PID; do - if [ -f "$pidfile" ]; then - pid=$(cat "$pidfile") - if [ "$pid" != "$$" ]; then - kill "$pid" 2>/dev/null || true - fi - rm -f "$pidfile" - fi -done - -# Kill older remote processes -ssh brat "pkill -9 h2load" || true - -#PERF_EVENTS="cpu-clock,context-switches,cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,$(paste -sd ',' syscalls)" -#PERF_EVENTS="cpu-clock,cycles,branch-misses,cache-misses,page-faults,$(paste -sd ',' syscalls)" -PERF_EVENTS="cycles,instructions,branches,branch-misses,cache-references,cache-misses,page-faults,$(paste -sd ',' syscalls)" - -LOONA_DIR=~/bearcove/loona - -# Build the servers -cargo build --release --manifest-path="$LOONA_DIR/Cargo.toml" -F tracing/release_max_level_info - -# Set protocol, default to h2c -PROTO=${PROTO:-h2c} -export PROTO - -OUR_PUBLIC_IP=$(curl -4 ifconfig.me) -if [[ "$PROTO" == "tls" ]]; then - HTTP_OR_HTTPS="https" -else - HTTP_OR_HTTPS="http" -fi - -# Launch hyper server -ADDR=0.0.0.0 PORT=8001 "$LOONA_DIR/target/release/httpwg-hyper" & -HYPER_PID=$! -echo $HYPER_PID > /tmp/loona-perfstat/hyper.PID -echo "hyper PID: $HYPER_PID" - -# Launch loona server -ADDR=0.0.0.0 PORT=8002 "$LOONA_DIR/target/release/httpwg-loona" & -LOONA_PID=$! -echo $LOONA_PID > /tmp/loona-perfstat/loona.PID -echo "loona PID: $LOONA_PID" - -HYPER_ADDR="${HTTP_OR_HTTPS}://${OUR_PUBLIC_IP}:8001" -LOONA_ADDR="${HTTP_OR_HTTPS}://${OUR_PUBLIC_IP}:8002" - -# Declare h2load args based on PROTO -declare -a H2LOAD_ARGS -if [[ "$PROTO" == "h1" ]]; then - echo "Error: h1 is not supported" - exit 1 -elif [[ "$PROTO" == "h2c" ]]; then - H2LOAD_ARGS=() -elif [[ "$PROTO" == "tls" ]]; then - ALPN_LIST=${ALPN_LIST:-"h2,http/1.1"} - H2LOAD_ARGS=(--alpn-list="$ALPN_LIST") -else - echo "Error: Unknown PROTO '$PROTO'" - exit 1 -fi - -declare -A servers=( - [hyper]="$HYPER_PID $HYPER_ADDR" - [loona]="$LOONA_PID $LOONA_ADDR" -) - -if [[ -n "${SERVER:-}" ]]; then - # If SERVER is set, only benchmark that one - if [[ -v "servers[$SERVER]" ]]; then - servers=([${SERVER}]="${servers[$SERVER]}") - else - echo "Error: SERVER '$SERVER' not found in the list of servers." - exit 1 - fi -fi - -H2LOAD="/nix/var/nix/profiles/default/bin/h2load" - -ENDPOINT="${ENDPOINT:-/repeat-4k-blocks/128}" -RPS="${RPS:-2}" -CONNS="${CONNS:-40}" -STREAMS="${STREAMS:-8}" -WARMUP="${WARMUP:-5}" -DURATION="${DURATION:-20}" -TIMES="${TIMES:-1}" - -# Set MODE to 'stat' if not specified -MODE=${MODE:-stat} - -if [[ "$MODE" == "record" ]]; then - PERF_CMD="perf record -F 99 -e $PERF_EVENTS -p" -elif [[ "$MODE" == "stat" ]]; then - PERF_CMD="perf stat -e $PERF_EVENTS -p" -else - echo "Error: Unknown MODE '$MODE'" - exit 1 -fi - -echo -e "\033[1;34m📊 Benchmark parameters: RPS=$RPS, CONNS=$CONNS, STREAMS=$STREAMS, WARMUP=$WARMUP, DURATION=$DURATION, TIMES=$TIMES\033[0m" - -for server in "${!servers[@]}"; do - read -r PID ADDR <<< "${servers[$server]}" - echo -e "\033[1;36mLoona Git SHA: $(cd ~/bearcove/loona && git rev-parse --short HEAD)\033[0m" - echo -e "\033[1;33m🚀 Benchmarking \033[1;32m$(cat /proc/$PID/cmdline | tr '\0' ' ')\033[0m" - remote_command=("$H2LOAD" "${H2LOAD_ARGS[@]}" --rps "$RPS" -c "$CONNS" -m "$STREAMS" --duration "$DURATION" "${ADDR}${ENDPOINT}") - - if [[ "$MODE" == "record" ]]; then - samply record -p "$PID" & - SAMPLY_PID=$! - echo $SAMPLY_PID > /tmp/loona-perfstat/samply.PID - ssh brat "${remote_command[@]}" - kill -INT $SAMPLY_PID - wait $SAMPLY_PID - else - for ((i=1; i<=$TIMES; i++)); do - echo "===================================================" - echo -e "\033[1;35m🏃 Run $i of $TIMES for $server server 🏃\033[0m" - echo "===================================================" - ssh brat "${remote_command[@]}" & - SSH_PID=$! - sleep "$WARMUP" - MEASURE_DURATION=$((DURATION - WARMUP - 1)) - echo "Starting perf for $MEASURE_DURATION seconds..." - perf stat -e "$PERF_EVENTS" -p "$PID" -- sleep "$MEASURE_DURATION" - echo "Starting perf... done!" - wait $SSH_PID - done - fi -done diff --git a/scripts/syscalls b/scripts/syscalls deleted file mode 100644 index 81e6fc06..00000000 --- a/scripts/syscalls +++ /dev/null @@ -1,4 +0,0 @@ -syscalls:sys_enter_write -syscalls:sys_enter_writev -syscalls:sys_enter_epoll_wait -syscalls:sys_enter_io_uring_enter From a239a3a658d0797cf0db0c13e33eae42bf167524 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 15:44:26 +0200 Subject: [PATCH 06/15] Set up python more properly --- .gitignore | 1 + .zed/settings.json | 7 ++ pyproject.toml | 27 +++++++ scripts/bench.py | 32 +++++++- scripts/uv.lock | 187 +++++++++++++++++++++++++++++++++++++++++++++ uv.lock | 149 ++++++++++++++++++++++++++++++++++++ 6 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 pyproject.toml create mode 100644 scripts/uv.lock create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore index b6993c08..8f76ea40 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ junit.xml .DS_Store /vendor profile.json +*.egg-info diff --git a/.zed/settings.json b/.zed/settings.json index 477c7499..0a9fb00e 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -14,6 +14,13 @@ "features": [] } } + }, + "pyright": { + "settings": { + "python": { + "pythonPath": "scripts/.venv/bin/python" + } + } } } } diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..8e6a81c8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[project] +name = "loona" +version = "0.1.0" +description = "Scripts" +requires-python = ">=3.11" +dependencies = [ + "termcolor~=2.4.0", + "pandas~=2.2.2", + "openpyxl~=3.1.5", +] + +[tool.setuptools] +packages = ["scripts"] + +[tool.pyright] +include = ["scripts"] +exclude = [ + "**/__pycache__", + "**/node_modules", + ".venv", +] +venvPath = "." +venv = ".venv" + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/scripts/bench.py b/scripts/bench.py index a42f9910..44700df3 100755 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -1,5 +1,6 @@ -#!/usr/bin/env -S PYTHONUNBUFFERED=1 FORCE_COLOR=1 uv run --with 'termcolor~=2.4.0' --with 'pandas~=2.2.2' --with 'openpyxl~=3.1.5' +#!/usr/bin/env -S PYTHONUNBUFFERED=1 FORCE_COLOR=1 uv run +import ctypes import os import signal import subprocess @@ -12,9 +13,20 @@ # Change to the script's directory os.chdir(os.path.dirname(os.path.abspath(__file__))) +# Define constants for prctl +PR_SET_PDEATHSIG = 1 + +# Load the libc shared library +libc = ctypes.CDLL("libc.so.6") + +def set_pdeathsig(): + """Set the parent death signal to SIGKILL.""" + # Call prctl with PR_SET_PDEATHSIG and SIGKILL + libc.prctl(PR_SET_PDEATHSIG, signal.SIGKILL) + # Set trap to kill the process group on script exit def kill_group(): - os.killpg(0, signal.SIGTERM) + os.killpg(0, signal.SIGKILL) def abort_and_cleanup(signum, frame): print(colored("\nAborting and cleaning up...", "red")) @@ -200,3 +212,19 @@ def abort_and_cleanup(signum, frame): abort_and_cleanup(None, None) print(colored("✨ All done, good bye! 👋", "cyan")) + +print(colored("Processes still running in the group:", "yellow")) +try: + pgid = os.getpgid(0) + ps_output = subprocess.check_output(["ps", "-o", "pid,cmd", "--no-headers", "-g", str(pgid)]).decode().strip() + if ps_output: + for line in ps_output.split('\n'): + pid, cmd = line.strip().split(None, 1) + print(f"PID: {pid}, Command: {cmd}") + else: + print("No processes found in the group.") +except subprocess.CalledProcessError: + print("Error: Unable to retrieve process information.") + +print(colored("Cleaning up...", "red")) +kill_group() diff --git a/scripts/uv.lock b/scripts/uv.lock new file mode 100644 index 00000000..cf786605 --- /dev/null +++ b/scripts/uv.lock @@ -0,0 +1,187 @@ +version = 1 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version < '3.11'", + "python_full_version == '3.11.*'", + "python_full_version >= '3.12'", +] + +[[package]] +name = "bencimy-projec" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "openpyxl" }, + { name = "pandas" }, + { name = "termcolor" }, +] + +[package.metadata] +requires-dist = [ + { name = "openpyxl", specifier = "~=3.1.5" }, + { name = "pandas", specifier = "~=2.2.2" }, + { name = "termcolor", specifier = "~=2.4.0" }, +] + +[[package]] +name = "et-xmlfile" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/5d/0413a31d184a20c763ad741cc7852a659bf15094c24840c5bdd1754765cd/et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c", size = 3218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/c2/3dd434b0108730014f1b96fd286040dc3bcb70066346f7e01ec2ac95865f/et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada", size = 4688 }, +] + +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245 }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540 }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623 }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774 }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081 }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451 }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572 }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722 }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170 }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558 }, + { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137 }, + { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552 }, + { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957 }, + { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573 }, + { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330 }, + { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895 }, + { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253 }, + { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074 }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640 }, + { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230 }, + { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803 }, + { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835 }, + { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499 }, + { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497 }, + { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158 }, + { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173 }, + { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174 }, + { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701 }, + { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313 }, + { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179 }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942 }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512 }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976 }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494 }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596 }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099 }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823 }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424 }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809 }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314 }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288 }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793 }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885 }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784 }, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 }, +] + +[[package]] +name = "pandas" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/2d/39600d073ea70b9cafdc51fab91d69c72b49dd92810f24cb5ac6631f387f/pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce", size = 12551798 }, + { url = "https://files.pythonhosted.org/packages/fd/4b/0cd38e68ab690b9df8ef90cba625bf3f93b82d1c719703b8e1b333b2c72d/pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238", size = 11287392 }, + { url = "https://files.pythonhosted.org/packages/01/c6/d3d2612aea9b9f28e79a30b864835dad8f542dcf474eee09afeee5d15d75/pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08", size = 15634823 }, + { url = "https://files.pythonhosted.org/packages/89/1b/12521efcbc6058e2673583bb096c2b5046a9df39bd73eca392c1efed24e5/pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0", size = 13032214 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/303dba73f1c3a9ef067d23e5afbb6175aa25e8121be79be354dcc740921a/pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51", size = 16278302 }, + { url = "https://files.pythonhosted.org/packages/ba/df/8ff7c5ed1cc4da8c6ab674dc8e4860a4310c3880df1283e01bac27a4333d/pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99", size = 13892866 }, + { url = "https://files.pythonhosted.org/packages/69/a6/81d5dc9a612cf0c1810c2ebc4f2afddb900382276522b18d128213faeae3/pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772", size = 11621592 }, + { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 }, + { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 }, + { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 }, + { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 }, + { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 }, + { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 }, + { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 }, + { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 }, + { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 }, + { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 }, + { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 }, + { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 }, + { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 }, + { url = "https://files.pythonhosted.org/packages/1b/cc/eb6ce83667131667c6561e009823e72aa5c76698e75552724bdfc8d1ef0b/pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2", size = 12566406 }, + { url = "https://files.pythonhosted.org/packages/96/08/9ad65176f854fd5eb806a27da6e8b6c12d5ddae7ef3bd80d8b3009099333/pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd", size = 11304008 }, + { url = "https://files.pythonhosted.org/packages/aa/30/5987c82fea318ac7d6bcd083c5b5259d4000e99dd29ae7a9357c65a1b17a/pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863", size = 15662279 }, + { url = "https://files.pythonhosted.org/packages/bb/30/f6f1f1ac36250f50c421b1b6af08c35e5a8b5a84385ef928625336b93e6f/pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921", size = 13069490 }, + { url = "https://files.pythonhosted.org/packages/b5/27/76c1509f505d1f4cb65839352d099c90a13019371e90347166811aa6a075/pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a", size = 16299412 }, + { url = "https://files.pythonhosted.org/packages/5d/11/a5a2f52936fba3afc42de35b19cae941284d973649cb6949bc41cc2e5901/pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57", size = 13920884 }, + { url = "https://files.pythonhosted.org/packages/bf/2c/a0cee9c392a4c9227b835af27f9260582b994f9a2b5ec23993b596e5deb7/pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4", size = 11637580 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "termcolor" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/56/d7d66a84f96d804155f6ff2873d065368b25a07222a6fd51c4f24ef6d764/termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a", size = 12664 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5f/8c716e47b3a50cbd7c146f45881e11d9414def768b7cd9c5e6650ec2a80a/termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63", size = 7719 }, +] + +[[package]] +name = "tzdata" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 }, +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..05b16bcc --- /dev/null +++ b/uv.lock @@ -0,0 +1,149 @@ +version = 1 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version < '3.11'", + "python_full_version == '3.11.*'", + "python_full_version >= '3.12'", +] + +[[package]] +name = "et-xmlfile" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/5d/0413a31d184a20c763ad741cc7852a659bf15094c24840c5bdd1754765cd/et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c", size = 3218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/c2/3dd434b0108730014f1b96fd286040dc3bcb70066346f7e01ec2ac95865f/et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada", size = 4688 }, +] + +[[package]] +name = "loona" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "openpyxl" }, + { name = "pandas" }, + { name = "termcolor" }, +] + +[package.metadata] +requires-dist = [ + { name = "openpyxl", specifier = "~=3.1.5" }, + { name = "pandas", specifier = "~=2.2.2" }, + { name = "termcolor", specifier = "~=2.4.0" }, +] + +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137 }, + { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552 }, + { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957 }, + { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573 }, + { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330 }, + { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895 }, + { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253 }, + { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074 }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640 }, + { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230 }, + { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803 }, + { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835 }, + { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499 }, + { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497 }, + { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158 }, + { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173 }, + { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174 }, + { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701 }, + { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313 }, + { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179 }, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "et-xmlfile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 }, +] + +[[package]] +name = "pandas" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 }, + { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 }, + { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 }, + { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 }, + { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 }, + { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 }, + { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 }, + { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 }, + { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 }, + { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 }, + { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 }, + { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 }, + { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "termcolor" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/56/d7d66a84f96d804155f6ff2873d065368b25a07222a6fd51c4f24ef6d764/termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a", size = 12664 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5f/8c716e47b3a50cbd7c146f45881e11d9414def768b7cd9c5e6650ec2a80a/termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63", size = 7719 }, +] + +[[package]] +name = "tzdata" +version = "2024.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 }, +] From d5ae5d14b40d6287aaafe9025d876c5bde800427 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 15:53:26 +0200 Subject: [PATCH 07/15] Mhh close --- pyproject.toml | 2 +- scripts/bench.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8e6a81c8..76fc8e15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "loona" version = "0.1.0" -description = "Scripts" +description = "Benchmarking rig for the loona Rust HTTP implementation" requires-python = ">=3.11" dependencies = [ "termcolor~=2.4.0", diff --git a/scripts/bench.py b/scripts/bench.py index 44700df3..a16419e0 100755 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -190,7 +190,7 @@ def abort_and_cleanup(signum, frame): data[label] = value # Create a DataFrame from the dictionary - df = pd.DataFrame(data, index=[0]).T + df = pd.DataFrame([data]).T df.columns = ['Value'] df.index.name = 'Event' From c311abc333057d3c724a1108a6269a7aadb3fc8e Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 15:54:30 +0200 Subject: [PATCH 08/15] Children cleanup --- scripts/bench.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index a16419e0..b3b57be5 100755 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -33,6 +33,10 @@ def abort_and_cleanup(signum, frame): kill_group() sys.exit(1) +# Register signal handlers +signal.signal(signal.SIGINT, abort_and_cleanup) +signal.signal(signal.SIGTERM, abort_and_cleanup) + # Create directory if it doesn't exist Path("/tmp/loona-perfstat").mkdir(parents=True, exist_ok=True) @@ -97,7 +101,7 @@ def abort_and_cleanup(signum, frame): # Launch hyper server hyper_env = os.environ.copy() hyper_env.update({"ADDR": "0.0.0.0", "PORT": "8001"}) -hyper_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-hyper"], env=hyper_env, preexec_fn=os.setpgrp) +hyper_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-hyper"], env=hyper_env, preexec_fn=set_pdeathsig) HYPER_PID = hyper_process.pid with open("/tmp/loona-perfstat/hyper.PID", "w") as f: f.write(str(HYPER_PID)) @@ -105,7 +109,7 @@ def abort_and_cleanup(signum, frame): # Launch loona server loona_env = os.environ.copy() loona_env.update({"ADDR": "0.0.0.0", "PORT": "8002"}) -loona_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-loona"], env=loona_env, preexec_fn=os.setpgrp) +loona_process = subprocess.Popen([f"{LOONA_DIR}/target/release/httpwg-loona"], env=loona_env, preexec_fn=set_pdeathsig) LOONA_PID = loona_process.pid with open("/tmp/loona-perfstat/loona.PID", "w") as f: f.write(str(LOONA_PID)) @@ -163,7 +167,7 @@ def abort_and_cleanup(signum, frame): h2load_cmd = [H2LOAD] + H2LOAD_ARGS + ["--rps", RPS, "-c", CONNS, "-m", STREAMS, "--duration", str(DURATION), f"{ADDR}{ENDPOINT}"] if MODE == "record": - samply_process = subprocess.Popen(["samply", "record", "-p", str(PID)]) + samply_process = subprocess.Popen(["samply", "record", "-p", str(PID)], preexec_fn=set_pdeathsig) with open("/tmp/loona-perfstat/samply.PID", "w") as f: f.write(str(samply_process.pid)) subprocess.run(["ssh", "brat"] + h2load_cmd, check=True) @@ -172,11 +176,11 @@ def abort_and_cleanup(signum, frame): else: for i in range(1, TIMES + 1): print(colored(f"🏃 Run {i} of {TIMES} for {server} server 🏃 (will take {DURATION} seconds)", "magenta")) - ssh_process = subprocess.Popen(["ssh", "brat"] + h2load_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setpgrp) + ssh_process = subprocess.Popen(["ssh", "brat"] + h2load_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=set_pdeathsig) time.sleep(WARMUP) MEASURE_DURATION = DURATION - WARMUP - 1 perf_cmd = ["perf", "stat", "-x", ",", "-e", ",".join(PERF_EVENTS), "-p", str(PID), "--", "sleep", str(MEASURE_DURATION)] - perf_process = subprocess.Popen(perf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setpgrp) + perf_process = subprocess.Popen(perf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=set_pdeathsig) perf_stdout, perf_stderr = perf_process.communicate() perf_output = perf_stdout.decode('utf-8') + perf_stderr.decode('utf-8') From 958a1ba302715d08482a8b3a0fac9f61f8fa19dc Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Sun, 1 Sep 2024 16:45:31 +0200 Subject: [PATCH 09/15] more measuring --- scripts/bench.py | 205 +++++++++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 86 deletions(-) diff --git a/scripts/bench.py b/scripts/bench.py index b3b57be5..b147821d 100755 --- a/scripts/bench.py +++ b/scripts/bench.py @@ -9,10 +9,26 @@ from pathlib import Path from termcolor import colored import pandas as pd +import tempfile # Change to the script's directory os.chdir(os.path.dirname(os.path.abspath(__file__))) +PERF_EVENTS = [ + "cycles", + "instructions", + "branches", + "branch-misses", + "cache-references", + "cache-misses", + "page-faults", + "syscalls:sys_enter_write", + "syscalls:sys_enter_writev", + "syscalls:sys_enter_epoll_wait", + "syscalls:sys_enter_io_uring_enter" +] +PERF_EVENTS_STRING = ",".join(PERF_EVENTS) + # Define constants for prctl PR_SET_PDEATHSIG = 1 @@ -55,19 +71,6 @@ def abort_and_cleanup(signum, frame): # Kill older remote processes subprocess.run(["ssh", "brat", "pkill -9 h2load"], check=False) -PERF_EVENTS = [ - "cycles", - "instructions", - "branches", - "branch-misses", - "cache-references", - "cache-misses", - "page-faults", - "syscalls:sys_enter_write", - "syscalls:sys_enter_writev", - "syscalls:sys_enter_epoll_wait", - "syscalls:sys_enter_io_uring_enter" -] LOONA_DIR = os.path.expanduser("~/bearcove/loona") # Build the servers @@ -96,6 +99,7 @@ def abort_and_cleanup(signum, frame): print(f"📡 Our public IP address is {OUR_PUBLIC_IP}") +# Declare h2load args based on PROTO HTTP_OR_HTTPS = "https" if PROTO == "tls" else "http" # Launch hyper server @@ -117,20 +121,6 @@ def abort_and_cleanup(signum, frame): HYPER_ADDR = f"{HTTP_OR_HTTPS}://{OUR_PUBLIC_IP}:8001" LOONA_ADDR = f"{HTTP_OR_HTTPS}://{OUR_PUBLIC_IP}:8002" -# Declare h2load args based on PROTO -H2LOAD_ARGS = [] -if PROTO == "h1": - print("Error: h1 is not supported") - sys.exit(1) -elif PROTO == "h2c": - pass -elif PROTO == "tls": - ALPN_LIST = os.environ.get("ALPN_LIST", "h2,http/1.1") - H2LOAD_ARGS = [f"--alpn-list={ALPN_LIST}"] -else: - print(f"Error: Unknown PROTO '{PROTO}'") - sys.exit(1) - servers = { "hyper": (HYPER_PID, HYPER_ADDR), "loona": (LOONA_PID, LOONA_ADDR) @@ -146,71 +136,114 @@ def abort_and_cleanup(signum, frame): H2LOAD = "/nix/var/nix/profiles/default/bin/h2load" -ENDPOINT = os.environ.get("ENDPOINT", "/repeat-4k-blocks/128") -RPS = os.environ.get("RPS", "2") -CONNS = os.environ.get("CONNS", "40") -STREAMS = os.environ.get("STREAMS", "8") -WARMUP = int(os.environ.get("WARMUP", "5")) -DURATION = int(os.environ.get("DURATION", "20")) -TIMES = int(os.environ.get("TIMES", "1")) - -# Set MODE to 'stat' if not specified -MODE = os.environ.get("MODE", "stat") +endpoint = os.environ.get("ENDPOINT", "/repeat-4k-blocks/128") +rps = os.environ.get("RPS", "2") +clients = os.environ.get("CLIENTS", "20") +streams = os.environ.get("STREAMS", "2") +warmup = int(os.environ.get("WARMUP", "5")) +duration = int(os.environ.get("DURATION", "20")) +times = int(os.environ.get("TIMES", "3")) + +# Mode can be 'perfstat' or 'samply' +mode = os.environ.get("MODE", "perfstat") + +loona_git_sha = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=os.path.expanduser('~/bearcove/loona')).decode().strip() + +print(colored(f"📊 Benchmark parameters: RPS={rps}, CONNS={clients}, STREAMS={streams}, WARMUP={warmup}, DURATION={duration}, TIMES={times}", "blue")) + +def gen_h2load_cmd(addr: str): + return [ + H2LOAD, + "--rps", rps, + "--clients", clients, + "--max-concurrent-streams", streams, + "--duration", str(duration), + f"{addr}{endpoint}", + ] + +def do_samply(): + if len(servers) > 1: + print(colored("Error: More than one server specified.", "red")) + print("Please use SERVER=[loona,hyper] to narrow down to a single server.") + sys.exit(1) -LOONA_GIT_SHA = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=os.path.expanduser('~/bearcove/loona')).decode().strip() + for server, (PID, ADDR) in servers.items(): + print(colored("Warning: Warmup period is not taken into account in samply mode yet.", "yellow")) -print(colored(f"📊 Benchmark parameters: RPS={RPS}, CONNS={CONNS}, STREAMS={STREAMS}, WARMUP={WARMUP}, DURATION={DURATION}, TIMES={TIMES}", "blue")) + samply_process = subprocess.Popen(["samply", "record", "-p", str(PID)], preexec_fn=set_pdeathsig) + with open("/tmp/loona-perfstat/samply.PID", "w") as f: + f.write(str(samply_process.pid)) + h2load_cmd = gen_h2load_cmd(ADDR) + subprocess.run(["ssh", "brat"] + h2load_cmd, check=True) + samply_process.send_signal(signal.SIGINT) + samply_process.wait() -try: +def do_perfstat(): for server, (PID, ADDR) in servers.items(): print(colored(f"🚀 Benchmarking {open(f'/proc/{PID}/cmdline').read().replace(chr(0), ' ').strip()}", "yellow")) - h2load_cmd = [H2LOAD] + H2LOAD_ARGS + ["--rps", RPS, "-c", CONNS, "-m", STREAMS, "--duration", str(DURATION), f"{ADDR}{ENDPOINT}"] - if MODE == "record": - samply_process = subprocess.Popen(["samply", "record", "-p", str(PID)], preexec_fn=set_pdeathsig) - with open("/tmp/loona-perfstat/samply.PID", "w") as f: - f.write(str(samply_process.pid)) - subprocess.run(["ssh", "brat"] + h2load_cmd, check=True) - samply_process.send_signal(signal.SIGINT) - samply_process.wait() - else: - for i in range(1, TIMES + 1): - print(colored(f"🏃 Run {i} of {TIMES} for {server} server 🏃 (will take {DURATION} seconds)", "magenta")) - ssh_process = subprocess.Popen(["ssh", "brat"] + h2load_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=set_pdeathsig) - time.sleep(WARMUP) - MEASURE_DURATION = DURATION - WARMUP - 1 - perf_cmd = ["perf", "stat", "-x", ",", "-e", ",".join(PERF_EVENTS), "-p", str(PID), "--", "sleep", str(MEASURE_DURATION)] - perf_process = subprocess.Popen(perf_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=set_pdeathsig) - perf_stdout, perf_stderr = perf_process.communicate() - - perf_output = perf_stdout.decode('utf-8') + perf_stderr.decode('utf-8') - perf_lines = [line.strip().split(',') for line in perf_output.split('\n') if line.strip() and not line.startswith('#')] - - # Create a dictionary to store the data - data = {} - for line in perf_lines: - if len(line) >= 3: - value, _, label = line[:3] - data[label] = value - - # Create a DataFrame from the dictionary - df = pd.DataFrame([data]).T - df.columns = ['Value'] - df.index.name = 'Event' - - # Save the DataFrame as an Excel file - excel_filename = f"/tmp/loona-perfstat/{server}_run_{i}.xlsx" - df.to_excel(excel_filename) - print(colored(f"Saved performance data to {excel_filename}", "green")) - - ssh_process.wait() - - if ssh_process.returncode != 0: - output, error = ssh_process.communicate() - print("h2load command failed. Output:") - print(output.decode()) - print("Error:") - print(error.decode()) + print(colored(f"🏃 Measuring {server} 🏃 (will take {duration} seconds)", "magenta")) + output_path = tempfile.NamedTemporaryFile(delete=False, suffix='.csv').name + + perf_cmd = [ + "perf", "stat", + "--event", PERF_EVENTS_STRING, + "--field-separator", ",", + "--output", output_path, + "--pid", str(PID), + "--delay", str(warmup*1000), + "--", + "ssh", "brat", + ] + gen_h2load_cmd(ADDR) + if PROTO == "tls": + perf_cmd += ["--alpn-list", "h2"] + + perf_process = subprocess.Popen(perf_cmd, preexec_fn=set_pdeathsig) + + # perf stat output format: + # For CSV output (-x','): + # ,,,,