Summary plots

This commit is contained in:
Pascal Engélibert 2026-06-05 16:12:49 +02:00
commit 09764b6107
3 changed files with 550 additions and 70 deletions

View file

@ -135,15 +135,11 @@ sudo chmod u+s powercap
### Grid5000
```bash
ssh lyon.g5k
oarsub -q default -l host=2,walltime=2 -p "wattmeter=YES" -I
oarsub -q default -l host=2,walltime=2 -p "nova" -I
ssh rennes.g5k
oarsub -q default -l host=2,walltime=2 -p "paradoxe AND wattmeter=YES" -I
# Check the name of the other node in https://intranet.grid5000.fr/oar/Lyon/drawgantt-svg/
# Let's call them p1 and p2
ping p2
# Note p2 addr to exp.py
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
pip3 install fabric
pip3 install fabric pyOpenSSL
python exp.py make g5k -c
python exp.py send g5k

92
exp.py
View file

@ -73,13 +73,13 @@ CONFIGS = {
"pi3": {
"experiments": [
#"impl-cipher-ver",
"impl-cert-ver",
#"impl-cert-ver",
#"impl-kex-ver",
#"zrtt",
#"realistic"
"realistic"
],
"sides": [
#"client",
"client",
"server",
],
"tls": [
@ -87,9 +87,9 @@ CONFIGS = {
True,
],
"records": [
{ "filename": "wp2", "repeat": 10000, "time": 300 },
#{ "filename": "yt2-ads", "repeat": 10000, "time": 600 },
#{ "filename": "yt2-ublock", "repeat": 10000, "time": 600 },
{ "filename": "wp2", "repeat": 10000, "time": 300, "reproduce": 2 },
{ "filename": "yt2-ads", "repeat": 10000, "time": 300, "reproduce": 2 },
{ "filename": "yt2-ublock", "repeat": 10000, "time": 300, "reproduce": 2 },
#{ "filename": "wp2", "repeat": 10000, "time": 300, "reproduce": 100 },
],
"repo_dir": "/home/tuxmain/reps/tlsbench",
@ -108,29 +108,32 @@ CONFIGS = {
"idle": "idle - - - - - - - - - - - 600.000081539154 0.0 896 4792 0.5399999999999991 0 -",
"notify_listen": ("0.0.0.0", 8090),
"notify_addr": "192.168.3.1:8090",
"concurrency": "4",
"concurrency": "1",
},
# i7-4790 -> core2
"core2": {
"experiments": [
"impl-cipher-ver",
"impl-cert-ver",
"impl-kex-ver",
#"impl-cipher-ver",
#"impl-cert-ver",
#"impl-kex-ver",
#"zrtt",
#"realistic",
"realistic",
#"debug",
],
"sides": [
"client",
#"server",
"server",
],
"tls": [
#False,
True,
False,
#True,
],
"records": [
{ "filename": "wp2", "repeat": 10000, "time": 300 },
#{ "filename": "yt2-ads", "repeat": 10000, "time": 600 },
#{ "filename": "yt2-ublock", "repeat": 10000, "time": 600 },
{ "filename": "wp2", "repeat": 10000, "time": 300, "reproduce": 3 },
{ "filename": "yt2-ads", "repeat": 10000, "time": 300, "reproduce": 3 },
{ "filename": "yt2-ublock", "repeat": 10000, "time": 300, "reproduce": 3 },
#{ "filename": "test", "repeat": 1, "time": 10 },
#{ "filename": "wp2", "repeat": 10000, "time": 300, "reproduce": 100 },
],
"repo_dir": "/home/tuxmain/reps/tlsbench",
"exp_dir": "/dev/shm/exp",
@ -148,6 +151,7 @@ CONFIGS = {
"idle": "idle - - - - - - - - - 1777360401.7482355 1777361601.7482355 1200.0000903606415 0 589 4764 3.478999999999999 0 -",
"notify_listen": ("0.0.0.0", 8090),
"notify_addr": "192.168.3.1:8090",
"concurrency": "1",
},
"core2-local": {
"experiments": [
@ -182,11 +186,11 @@ CONFIGS = {
# i7-4790 -> i5-7300HQ
"i5": {
"experiments": [
"impl-cipher-ver",
"impl-cert-ver",
#"impl-cipher-ver",
#"impl-cert-ver",
#"impl-kex-ver",
#"zrtt",
#"realistic",
"realistic",
],
"sides": [
"client",
@ -197,9 +201,9 @@ CONFIGS = {
True,
],
"records": [
{ "filename": "wp2", "repeat": 10000, "time": 300 },
#{ "filename": "yt2-ads", "repeat": 10000, "time": 600 },
#{ "filename": "yt2-ublock", "repeat": 10000, "time": 600 },
{ "filename": "wp2", "repeat": 10000, "time": 300, "reproduce": 2 },
{ "filename": "yt2-ads", "repeat": 10000, "time": 300, "reproduce": 2 },
{ "filename": "yt2-ublock", "repeat": 10000, "time": 300, "reproduce": 2 },
#{ "filename": "wp2", "repeat": 10000, "time": 300, "reproduce": 100 },
],
"repo_dir": "/home/tuxmain/reps/tlsbench",
@ -220,7 +224,8 @@ CONFIGS = {
"notify_addr": "192.168.3.1:8090",
"ld_preload": {
"openssl": "/home/tuxmain/reps/tlsbench/libssl.so:/home/tuxmain/reps/tlsbench/libcrypto.so",
}
},
"concurrency": "1",
},
"i5-local": {
"experiments": [
@ -265,23 +270,23 @@ CONFIGS = {
"server",
],
"tls": [
False,
#False,
True,
],
"records": [
# Can't repeat more than 8000 times here
# TODO check if netreplay client frees ports correctly, or try to reuse ports
{ "filename": "wp2", "repeat": 8000, "time": 180 },
{ "filename": "yt2-ads", "repeat": 8000, "time": 180 },
{ "filename": "yt2-ublock", "repeat": 8000, "time": 180 },
#{ "filename": "wp2", "repeat": 10000, "time": 300, "reproduce": 100 },
#{ "filename": "wp2", "repeat": 8000, "time": 180 },
#{ "filename": "yt2-ads", "repeat": 8000, "time": 180 },
#{ "filename": "yt2-ublock", "repeat": 8000, "time": 180 },
{ "filename": "wp2", "repeat": 10000, "time": 120, "reproduce": 10 },
],
"repo_dir": "/home/pengelib/tlsbench",
"exp_dir": "/dev/shm/exp",
"log_backup_dir": "/home/pengelib",
"local_addr": "TODO",
"remote_addr": "172.16.52.6",
"remote_ssh": "pengelib@gros-69",
"local_addr": "172.16.101.x",
"remote_addr": "172.16.101.x",
"remote_ssh": "pengelib@paradoxe-x",
"remote_psw": None,
"remote_repo_dir": "/home/pengelib/tlsbench",
"wattmeter": False,
@ -289,9 +294,9 @@ CONFIGS = {
"rapl": False,
"sa": False,
"listen_port": 8080,
"idle": "idle - - - - - - - - - 1775721200 1775721800 600 0 222346 1105446 10.105035525271305 0 -",
"idle": "idle - - - - - - - - - 1779276600 1779277200 600 0 222346 1105446 2.935994312264558 0 -",
"notify_listen": ("0.0.0.0", 8090),
"notify_addr": "TODO:8090",
"notify_addr": "172.16.101.x:8090",
"ld_preload": {
"openssl": "/home/pengelib/openssl-openssl-3.6.1/libssl.so.3:/home/pengelib/openssl-openssl-3.6.1/libcrypto.so.3",
}
@ -351,13 +356,13 @@ CERT_SIGN_ALGS = [
"rsa2048", "rsa3072", "rsa4096", # widely used
]
IMPLS = [
#"aws-lc", # Amazon's crypto widely used in Rust stuff
"aws-lc", # Amazon's crypto widely used in Rust stuff
#"boring", # Google's fork of OpenSSL used in Chrome and Android
#"graviola", # New crypto in Rust
#"openssl", # widely used
#"openssl-static",
#"ring", # used in most Rust stuff
"symcrypt", # Microsoft's crypto
#"symcrypt", # Microsoft's crypto
]
# Symmetric ciphers
# They also allow to choose the TLS version.
@ -449,12 +454,11 @@ EXPERIMENTS = {
"debug": {
"impls": IMPLS,
"ciphers": [
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
#"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"AES_256_GCM_SHA384",
],
"kexes": ["X25519"],
"kexes": ["X25519MLKEM768"],
"cert": ["prime256v1"],
"earlydata": ["0"],
"earlydata": ["1"],
},
"realistic": {
"impls": IMPLS,
@ -463,14 +467,14 @@ EXPERIMENTS = {
"AES_256_GCM_SHA384",
#"CHACHA20_POLY1305_SHA256",
#"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256",
#"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384",
#"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
],
"kexes": [
"X25519",
#"SECP256R1",
#"SECP384R1",
#"X25519MLKEM768",
"X25519MLKEM768",
#"SECP256R1MLKEM768",
#"MLKEM768",
],
@ -482,7 +486,7 @@ EXPERIMENTS = {
#"rsa4096",
"realistic",
],
"earlydata": ["1"],
"earlydata": ["0", "1"],
},
}
@ -845,7 +849,7 @@ def run_exp(config, only_record=None, idle=False, shutdown=False, debug=False):
for kex in exp["kexes"]:
for cipher in exp["ciphers"]:
for earlydata in exp["earlydata"]:
if not alg_filter(kex, alg, cipher, impl):
if not alg_filter(kex, alg, cipher, impl) or (earlydata == "1" and "WITH" in cipher):
continue
for tls in config["tls"]:
tls_int = int(tls)

518
plots.py
View file

@ -66,6 +66,18 @@ CRITERION_TITLE = {
"kex": "key exchange",
"ed": "0-RTT",
}
PRETTY_TABLE = {
"pi3": "$\\pi$3",
"wp2": "WP",
"yt2-ads": "YT",
"yt2-ublock": "YT+µ",
}
PRETTY_TABLE_GNUPLOT = {
"pi3": "$\\\\pi$3",
"wp2": "WP",
"yt2-ads": "YT",
"yt2-ublock": "YT+µ",
}
def impl_title(impl):
# Gnuplot does not escape underscores when generating tex
@ -79,6 +91,13 @@ PLOTS_DIR = "/dev/shm/plots"
#WS_PER_BYTE = 0.06 * 1000 * 3600 / 1024**3
WS_PER_BYTE = 0.001875 * 1000 * 3600 / 1024**3
# Round and ensure exactly n decimal digits
def roundz(x, n):
s = str(round(x, n))
if "." not in s:
s += "."
return s + "0" * (n - len(s) + s.index(".") + 1)
def gnuplot_histogram(**kwargs):
if "machine" in kwargs and kwargs["machine"] != None:
kwargs["machine"] = ", " + kwargs["machine"]
@ -638,7 +657,7 @@ def tabulate(lines, separator=" ", endline="", ruler=True):
ruler_state = False
for line in lines:
for col in range(len(line)):
if col > 0:
if col > 0 and line[col] != None:
table += separator
table += line[col]
rem = widths[col] - len(line[col])
@ -775,6 +794,7 @@ def analyze_logs(logs):
})
return lines
# Group items by equality classes on the tuple of "unique" values, and filter by constraints
def aggregate(items, unique, constraints=[]):
results = {}
for item in items:
@ -792,6 +812,7 @@ def aggregate(items, unique, constraints=[]):
continue
unique_key = tuple(item[u] for u in unique)
print(unique_key)
if unique_key not in results:
results[unique_key] = {u: item[u] for u in unique}
results[unique_key]["items"] = [item]
@ -800,6 +821,104 @@ def aggregate(items, unique, constraints=[]):
return results
# Average to_average values of equality classes on the tuple of "unique" values
def average_logs(items, unique, to_average):
results = []
aggregated = aggregate(items, unique)
for key in aggregated:
avg = aggregated[key]["items"][0]
for item in aggregated[key]["items"][1:]:
for k in to_average:
avg[k] += item[k]
for k in to_average:
avg[k] /= len(aggregated[key]["items"])
results.append(avg)
return results
# Average to_average values of equality classes on the tuple of "unique" values
def average_items(items, to_average, target=None):
avg = items[0]
for item in items[1:]:
for k in to_average:
avg[k] += item[k]
for k in to_average:
avg[k] /= len(items)
if len(items) < 3:
print("Only", len(items), "for", target, avg)
return avg
def analyze_average_logs(logs, target=None):
plain_result_key = lambda log: (log["exp"], log["side"], log["record"])
result_key = lambda log: (log["exp"], log["side"], log["record"], log["impl"], log["alg"], log["kex"], log["cipher"], log["ed"])
plain_results = {}
results = {}
idle_val = None
for log in logs:
if log["exp"] == "idle":
idle_val = {
"cpu": float(log["cpu"]) / float(log["time"]),
"energy": float(log["Wh"]) / float(log["time"]) * 3600,
"in": float(log["bytes_in"]) / float(log["time"]),
"out": float(log["bytes_out"]) / float(log["time"]),
}
if log["tls"] == "0":
n = float(log.get("n", "1000"))
if plain_result_key(log) not in plain_results:
plain_results[plain_result_key(log)] = []
plain_results[plain_result_key(log)].append({
"cpu": (float(log["cpu"]) - idle_val["cpu"] * float(log["time"])) / n,
"total_energy": float(log["Wh"]) * 3600 / n,
"energy": (float(log["Wh"]) * 3600 - idle_val["energy"] * float(log["time"])) / n,
"in": (float(log["bytes_in"]) - idle_val["in"] * float(log["time"])) / n,
"out": (float(log["bytes_out"]) - idle_val["out"] * float(log["time"])) / n,
})
if log["exp"] != "idle" and log["tls"] == "1":
n = float(log.get("n", "1000"))
if result_key(log) not in results:
results[result_key(log)] = []
results[result_key(log)].append({
"exp": log["exp"],
"side": log["side"],
"record": log["record"],
"impl": log["impl"],
"alg": log["alg"],
"kex": log["kex"],
"cipher": log["cipher"],
"ed": log["ed"],
"idle": idle_val["energy"],
"cpu": (float(log["cpu"]) - idle_val["cpu"] * float(log["time"])) / n,
"total_energy": float(log["Wh"]) * 3600 / n,
"energy": (float(log["Wh"]) * 3600 - idle_val["energy"] * float(log["time"])) / n,
"in": (float(log["bytes_in"]) - idle_val["in"] * float(log["time"])) / n,
"out": (float(log["bytes_out"]) - idle_val["out"] * float(log["time"])) / n,
})
for key in plain_results:
plain_results[key] = average_items(plain_results[key], ["cpu", "total_energy", "energy", "in", "out"], target=target)
for key in results:
results[key] = average_items(results[key], ["cpu", "total_energy", "energy", "in", "out"], target=target)
lines = []
for k in results:
r = results[k]
p = plain_results[k[:3]]
lines.append({
"exp": r["exp"],
"side": r["side"],
"record": r["record"],
"impl": r["impl"],
"alg": r["alg"],
"kex": r["kex"],
"cipher": r["cipher"],
"ed": r["ed"],
"idle": r["idle"],
"tls": r,
"plain": p,
})
return lines
def fzfill(x, n):
if type(x) != str:
x = str(round(x, n))
@ -898,6 +1017,335 @@ def make_tx_summary(client_logs, server_logs):
])
print(tabulate(latex, separator=" & ", endline="\\\\ \\cline{2-8}", ruler=False))
def make_tx_plot(logs_by_target, server_target):
txs = []
lines = [["exp", "record", "alg", "kex", "cipher", "ed", "client_target", "server_target", "client_impl", "server_impl", "plain (J/S)", "TLS (J/S)", "TLS only (J/S)", "TLS io (J/S)", "TLS rel"]]
server_logs = logs_by_target[server_target]
server_results = analyze_logs(server_logs)
for client_target in logs_by_target:
client_logs = logs_by_target[client_target]
client_results = analyze_logs(client_logs)
for client in client_results:
#print("client", client)
if client["side"] != "client":
continue
for server in server_results:
#print("server", server)
if server["side"] != "server":
continue
#print(client, server)
if client["exp"] == server["exp"] \
and client["record"] == server["record"] \
and client["alg"] == server["alg"] \
and client["kex"] == server["kex"] \
and client["cipher"] == server["cipher"] \
and client["ed"] == server["ed"]:
plain_io_avg = (client["plain"]["in"] + client["plain"]["out"] + server["plain"]["in"] + server["plain"]["out"]) / 2
tls_io_avg = (client["tls"]["in"] + client["tls"]["out"] + server["tls"]["in"] + server["tls"]["out"]) / 2
tls_only_io_avg = tls_io_avg - plain_io_avg
plain_energy = client["plain"]["energy"] + server["plain"]["energy"] + plain_io_avg * WS_PER_BYTE
tls_energy = client["tls"]["energy"] + server["tls"]["energy"] + tls_io_avg * WS_PER_BYTE
tls_only_energy = tls_energy - plain_energy
tls_rel_energy = tls_only_energy / plain_energy
tls_only_io_energy = tls_io_avg * WS_PER_BYTE - plain_io_avg * WS_PER_BYTE
txs.append({
"client_target": client_target,
"server_target": server_target,
"exp": client["exp"],
"record": client["record"],
"alg": client["alg"],
"kex": client["kex"],
"cipher": client["cipher"],
"ed": client["ed"],
"version": VER_LABEL[client["cipher"]],
"client_impl": client["impl"],
"server_impl": server["impl"],
"plain_energy": plain_energy,
"tls_energy": tls_energy,
"tls_only_energy": tls_only_energy,
"tls_rel_energy": tls_rel_energy,
"tls_only_io_energy": tls_only_io_energy,
})
lines.append([
client["exp"],
client["record"],
client["alg"],
client["kex"],
client["cipher"],
client["ed"],
client_target,
server_target,
client["impl"],
server["impl"],
str(round(plain_energy, 2)),
str(round(tls_energy, 2)),
str(round(tls_only_energy, 2)),
str(round(tls_only_io_energy, 2)),
str(round(tls_rel_energy*100, 2))+"%",
])
print(tabulate(lines))
aggregated = aggregate(txs, ["exp", "record", "alg"], {})
latex = []
for cluster_k in aggregated:
cluster = aggregated[cluster_k]
items_to_find = {"kex": ["X25519", "X25519MLKEM768"], "cipher": []}
items_to_find = {
"1.2": {"kex": "X25519", "cipher": "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384"},
"ec": {"kex": "X25519", "cipher": "AES_256_GCM_SHA384", "ed": "0"},
"pq": {"kex": "X25519MLKEM768", "ed": "0"},
"ec+ed": {"kex": "X25519", "cipher": "AES_256_GCM_SHA384", "ed": "1"},
"pq+ed": {"kex": "X25519MLKEM768", "ed": "1"},
}
items = {}
for k in items_to_find:
for item in cluster["items"]:
ok = True
for kk in items_to_find[k]:
if item[kk] != items_to_find[k][kk]:
ok = False
break
# named breaks please!
if ok:
items[k] = item
latex.append([
server_target,
client_target,
cluster["record"],
"$"+str(round(items["ec"]["plain_energy"], 2))+"$",
("$" + str(fzfill(items["1.2"]["tls_only_energy"], 2)) + "$ ($" + str(fzfill(items["1.2"]["tls_rel_energy"]*100, 2)) + "\\%$)") if "1.2" in items else "",
"$" + str(fzfill(items["ec"]["tls_only_energy"], 2)) + "$ ($" + str(fzfill(items["ec"]["tls_rel_energy"]*100, 2)) + "\\%$)",
("$" + str(fzfill(items["pq"]["tls_only_energy"], 2)) + "$ ($" + str(fzfill(items["pq"]["tls_rel_energy"]*100, 2)) + "\\%$)") if "pq" in items else "",
("$" + str(fzfill(items["ec+ed"]["tls_only_energy"], 2)) + "$ ($" + str(fzfill(items["ec+ed"]["tls_rel_energy"]*100, 2)) + "\\%$)") if "ec+ed" in items else "",
("$" + str(fzfill(items["pq+ed"]["tls_only_energy"], 2)) + "$ ($" + str(fzfill(items["pq+ed"]["tls_rel_energy"]*100, 2)) + "\\%$)") if "pq+ed" in items else "",
])
print(tabulate(latex, separator=" & ", endline="\\\\ \\cline{3-9}", ruler=False))
def make_summary_plot(logs_by_target):
lines = [["exp", "record", "alg", "kex", "cipher", "ed", "side", "target", "impl", "plain E (J/S)", "TLS E (J/S)", "TLS only E (J/S)", "plain io (o/S)", "TLS io (o/S)", "TLS only io (o/S)", "TLS E rel", "TLS io rel"]]
results = []
for target in logs_by_target:
logs = logs_by_target[target]
target_results = analyze_average_logs(logs, target=target)
for log in target_results:
plain_energy = log["plain"]["energy"]
tls_energy = log["tls"]["energy"]
tls_only_energy = tls_energy - plain_energy
tls_rel_energy = tls_only_energy / plain_energy
plain_io = log["plain"]["in"] + log["plain"]["out"]
tls_io = log["tls"]["in"] + log["tls"]["out"]
tls_only_io = tls_io - plain_io
tls_rel_io = tls_only_io / plain_io
results.append({
"target": target,
"exp": log["exp"],
"record": log["record"],
"alg": log["alg"],
"kex": log["kex"],
"cipher": log["cipher"],
"ed": log["ed"],
"version": VER_LABEL[log["cipher"]],
"impl": log["impl"],
"side": log["side"],
"tls": log["tls"],
"idle_energy": log["idle"],
"plain_energy": plain_energy,
"tls_energy": tls_energy,
"tls_only_energy": tls_only_energy,
"plain_io": plain_io,
"tls_io": tls_io,
"tls_only_io": tls_only_io,
"tls_rel_energy": tls_rel_energy,
"tls_rel_io": tls_rel_io,
})
lines.append([
log["exp"],
log["record"],
log["alg"],
log["kex"],
log["cipher"],
log["ed"],
log["side"],
target,
log["impl"],
str(round(plain_energy, 2)),
str(round(tls_energy, 2)),
str(round(tls_only_energy, 2)),
str(round(plain_io, 2)),
str(round(tls_io, 2)),
str(round(tls_only_io, 2)),
str(round(tls_rel_energy*100, 2))+"%",
str(round(tls_rel_io*100, 2))+"%",
])
print(tabulate(lines))
print()
print("\
\\multicolumn{1}{|c|}{\\textbf{Target}}&\
\\multicolumn{1}{|c|}{\\textbf{Idle}}&\
\\multicolumn{1}{|c|}{\\textbf{Record}}&\
\\multicolumn{1}{|c|}{\\textbf{Side}}&\
\\multicolumn{1}{|c|}{\\textbf{Energy}}&\
\\multicolumn{2}{|c|}{\\textbf{Traffic}}\
\\\\")
latex_lines = {}
latex_keys = []
for log in results:
key = (log["target"], log["record"], log["side"])
if key in latex_lines:
continue
latex_keys.append(key)
latex_lines[key] = [
PRETTY_TABLE.get(log["target"], log["target"]),
"${}$W".format(roundz(log["idle_energy"], 2)),
PRETTY_TABLE[log["record"]],
log["side"],
"${}$J".format(roundz(log["plain_energy"], 2)),
"${}$MB".format(roundz(log["plain_io"]/1024**2, 1)),
#"${}$J".format(roundz(log["plain_io"] * WS_PER_BYTE, 2))
"${}$J".format(int(log["plain_io"] * WS_PER_BYTE))
]
latex_keys.sort()
last = None
multirows = [1, 1, 1]
key_i = 0
for key in latex_keys:
line = latex_lines[key].copy()
for i in range(len(multirows)):
multirows[i] -= 1
if multirows[i] > 0:
line[i] = ""
else:
multirows[i] = 1
for nkey in latex_keys[key_i+1:]:
nline = latex_lines[nkey]
if nline[i] == line[i]:
multirows[i] += 1
else:
break
if multirows[i] > 1:
line[i] = "\\multirow{{{}}}*{{{}}}".format(multirows[i], line[i])
cline = 1
if last != None:
if last[0] == latex_lines[key][0]:
cline = 3
if last[2] == latex_lines[key][2]:
cline = 4
if last[3] == latex_lines[key][3]:
cline = 5
print(f"\cline{{{cline}-7}} " + " & ".join(line) + "\\\\")
last = latex_lines[key]
key_i += 1
f = open("/dev/shm/plots/summary.dat", "w")
f.write("Variant Order Client Server Traffic\n")
for log in results:
variant = None
if "WITH" in log["cipher"]:
variant = "1.2-x25519 0"
elif log["ed"] == "1":
if "MLKEM" in log["kex"]:
variant = "1.3-x25519mlkem768-0RTT 4"
else:
variant = "1.3-x25519-0RTT 3"
else:
if "MLKEM" in log["kex"]:
variant = "1.3-x25519mlkem768 2"
else:
variant = "1.3-x25519 1"
if variant == None:
print("Error: unknown variant", log)
exit(1)
f.write(" ".join([
variant,
str(log["tls_rel_energy"]) if (log["side"] == "client") else "-",
str(log["tls_rel_energy"]) if (log["side"] == "server") else "-",
str(log["tls_rel_io"]),
]) + "\n")
f.close()
cmps = {
"pq-ec": {
"new": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519MLKEM768", "ed": "0"},
"old": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519", "ed": "0"},
},
"ec0rtt-ec": {
"new": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519", "ed": "1"},
"old": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519", "ed": "0"},
},
"pq0rtt-pq": {
"new": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519MLKEM768", "ed": "1"},
"old": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519MLKEM768", "ed": "0"},
},
"pq0rtt-ec": {
"new": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519MLKEM768", "ed": "1"},
"old": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519", "ed": "0"},
},
"13-12": {
"new": {"cipher": "AES_256_GCM_SHA384", "kex": "X25519", "ed": "0"},
"old": {"cipher": "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384", "kex": "X25519", "ed": "0"},
},
}
for cmp_name in cmps:
cmp = cmps[cmp_name]
samples_energy_client = []
samples_energy_server = []
samples_io = []
for log_new in results:
ok = True
for filter in cmp["new"]:
if log_new[filter] != cmp["new"][filter]:
ok = False
break
if not ok:
continue
key_new = (log_new["exp"], log_new["target"], log_new["impl"], log_new["record"], log_new["side"])
for log_old in results:
ok = True
for filter in cmp["old"]:
if log_old[filter] != cmp["old"][filter]:
ok = False
break
if not ok:
continue
key_old = (log_old["exp"], log_old["target"], log_old["impl"], log_old["record"], log_old["side"])
if key_new == key_old:
if log_new["side"] == "server":
samples_energy_server.append((log_new["tls_energy"] / log_old["tls_energy"] - 1.0) * 100.0)
else:
samples_energy_client.append((log_new["tls_energy"] / log_old["tls_energy"] - 1.0) * 100.0)
samples_io.append((log_new["tls_io"] / log_old["tls_io"] - 1.0) * 100.0)
break
samples_energy_client.sort(reverse=True)
samples_energy_server.sort(reverse=True)
f = open(f"/dev/shm/plots/{cmp_name}-client.dat", "w")
for sample in samples_energy_client:
f.write(f"{sample}\n")
f.close()
f = open(f"/dev/shm/plots/{cmp_name}-server.dat", "w")
for sample in samples_energy_server:
f.write(f"{sample}\n")
f.close()
f = open(f"/dev/shm/plots/{cmp_name}-io.dat", "w")
for sample in samples_io:
f.write(f"{sample}\n")
f.close()
f_energy = open("/dev/shm/plots/energy-global.dat", "w")
f_io = open("/dev/shm/plots/io-global.dat", "w")
for log in results:
if log["target"] == "pi3" and "yt" in log["record"]:
continue
f_energy.write("{}-{} {}\n".format(PRETTY_TABLE_GNUPLOT.get(log["target"], log["target"]), log["side"], log["tls_rel_energy"] * 100.0))
f_io.write("{}-{} {}\n".format(PRETTY_TABLE_GNUPLOT.get(log["target"], log["target"]), log["side"], log["tls_rel_io"] * 100.0))
f_energy.close()
f_io.close()
def compute_box_plot(a):
a.sort()
median = a[len(a)//2] if len(a)%2 == 1 else (a[len(a)//2-1]+a[len(a)//2])/2
@ -920,6 +1368,37 @@ def make_stability_plot(logs_by_target):
"io": float(log["bytes_in"])+float(log["bytes_out"]) / float(log["time"]),
}
all_results = {}
for target in logs_by_target:
logs = logs_by_target[target]
for log in logs:
if log["exp"] == "idle":
continue
n = float(log.get("n", "1000"))
key = "{}-{}".format(target, log["side"])
if key not in all_results:
all_results[key] = []
all_results[key].append({
"energy": (float(log[COL["energy"]]) * 3600.0 - idle_val[target]["energy"] * float(log["time"])) / n,
"io": (float(log["bytes_in"])+float(log["bytes_out"]) - idle_val[target]["io"] * float(log["time"])) / n / 1024**2
})
means = {key: {
"energy": sum([i["energy"] for i in all_results[key]])/len(all_results[key]),
"io": sum([i["io"] for i in all_results[key]])/len(all_results[key]),
} for key in all_results}
for key in all_results:
for result in all_results[key]:
f.write("{} {} {} {} {}\n".format(
key,
result["energy"],
result["io"],
(result["energy"] / means[key]["energy"] - 1.0) * 100.0,
(result["io"] / means[key]["io"] - 1.0) * 100.0,
))
"""
for target in logs_by_target:
logs = logs_by_target[target]
for log in logs:
@ -931,7 +1410,7 @@ def make_stability_plot(logs_by_target):
log["side"],
(float(log[COL["energy"]]) * 3600.0 - idle_val[target]["energy"] * float(log["time"])) / n,
(float(log["bytes_in"])+float(log["bytes_out"]) - idle_val[target]["io"] * float(log["time"])) / n / 1024**2
))
))"""
f.close()
def getargv(arg:str, default="", n:int=1, args:list=sys.argv):
@ -1015,24 +1494,25 @@ if __name__ == "__main__":
make_profile_plot_grouped(logs, "impl-cert-ver", "cert", side, record, "impl", no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3")
make_profile_plot_grouped(logs, "impl-kex-ver", "kex", side, record, "impl", no_flamegraph=no_flamegraph, machine=machine, maketitle=maketitle, version="1.3")
elif cmd == "summary":
make_summary(logs)
# Args: stab <file1> <target1> [<file2> <target2>] [...]
# First target is the server
server = sys.argv[3]
logs_by_target = {server: logs}
for i in range(4, len(sys.argv), 2):
logs_i, records_i = parse_logs(sys.argv[i])
target_i = sys.argv[i+1]
logs_by_target[target_i] = logs_i
make_summary_plot(logs_by_target)
elif cmd == "tx":
logfile_name = sys.argv[3]
logfile = open(logfile_name, "r")
lines = logfile.readlines()
logfile.close()
colnames = lines[0].removesuffix("\n").split(" ")
logs2 = []
records = {}
for line in lines[1:]:
cols = line.removesuffix("\n").split(" ")
log = {}
for col in range(len(cols)):
log[colnames[col]] = cols[col]
if log["record"] != "-":
records[log["record"]] = ()
logs2.append(log)
make_tx_summary(logs, logs2)
# Args: stab <file1> <target1> [<file2> <target2>] [...]
# First target is the server
server = sys.argv[3]
logs_by_target = {server: logs}
for i in range(4, len(sys.argv), 2):
logs_i, records_i = parse_logs(sys.argv[i])
target_i = sys.argv[i+1]
logs_by_target[target_i] = logs_i
make_tx_plot(logs_by_target, server)
elif cmd == "correl":
from scipy import stats
import matplotlib.pyplot as plt