diff --git a/README.md b/README.md index bd83da9..a51664c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/exp.py b/exp.py index b76178d..8edc6d9 100644 --- a/exp.py +++ b/exp.py @@ -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) diff --git a/plots.py b/plots.py index cd56570..6c005a0 100644 --- a/plots.py +++ b/plots.py @@ -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 [ ] [...] + # 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 [ ] [...] + # 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