123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- #!/usr/bin/env python3
- import os
- import sys
- import json
- from math import sqrt
- import numpy as np
- import matplotlib.pyplot as plt
- from contextlib import contextmanager
- directory = os.path.expanduser('../simulation/library')
- sys.path.insert(1, directory)
- from dht_common import SIZE_OF_IP_ADDRESS, SIZE_OF_KEY, AVERAGE_RTT_ESTIMATE, AVERAGE_CLIENT_BANDWIDTH_ESTIMATE, AVERAGE_SERVER_BANDWIDTH_ESTIMATE, ENCRYPTION_SPEED_ESTIMATE, PIR_SPEED_ESTIMATE, SIZE_OF_CHUNK, Z_STAR
- NODE_TYPE_OPTIONS = {
- "DHTPIR_Quorum": {
- 1: {
- "legend": "RCP+QP+DHTPIR, 1 core",
- "marker": '^',
- "linestyle": (0, (3,1,3,1,1,1)),
- "color": "red"
- },
- 2: {
- "legend": "RCP+QP+DHTPIR, 2 cores",
- "marker": '>',
- "linestyle": (0, (3,1,3,1,1,1)),
- "color": "orange"
- },
- 4: {
- "legend": "RCP+QP+DHTPIR, 4 cores",
- "marker": 'v',
- "linestyle": (0, (3,1,3,1,1,1)),
- "color": "pink"
- },
- },
- "QPLastHop_Quorum": {
- 1: {
- "legend": "RCP+QP+LastHop",
- "marker": 'X',
- "linestyle": (0, (1,1,3,1,1,1)),
- "color": "blue"
- }
- },
- "QP_Quorum": {
- 1: {
- "legend": "RCP+QP",
- "marker": "*",
- "linestyle": (0, (3,1,1,1)),
- "color": "green"
- }
- },
- "RCP_Quorum": {
- 1: {
- "legend": "RCP",
- "marker": "h",
- "linestyle": (0, (1,1)),
- "color": "magenta"
- }
- },
- "Base_Node": {
- 1: {
- "legend": "Base",
- "marker": '.',
- "linestyle": (0, (3,1)),
- "color": "purple"
- }
- }
- }
- MAX_CORES = 8
- ##
- # This functionality allows us to temporarily change our working directory
- #
- # @input newdir - the new directory (relative to our current position) we want to be in
- @contextmanager
- def cd(newdir, makenew):
- prevdir = os.getcwd()
- directory = os.path.expanduser(newdir)
- if not os.path.exists(directory) and makenew:
- os.makedirs(directory)
- os.chdir(directory)
- try:
- yield
- finally:
- os.chdir(prevdir)
- def readData(dataDirectory):
- nodeData = {}
- clientData = {}
- realDirectory = os.path.expanduser(dataDirectory)
- for nodeType in os.listdir(realDirectory):
- if not nodeType.startswith('.') and not nodeType.endswith('.tar.gz'):
- nodeData[nodeType] = {}
- clientData[nodeType] = {}
- for numQuorums in os.listdir(os.path.join(realDirectory, nodeType)):
- if not numQuorums.startswith('.'):
- nodeData[nodeType][numQuorums] = {}
- clientData[nodeType][numQuorums] = {}
- for numNodes in os.listdir(os.path.join(realDirectory, nodeType, numQuorums)):
- if not numNodes.startswith('.'):
- nodeData[nodeType][numQuorums][numNodes] = {}
- clientData[nodeType][numQuorums][numNodes] = {}
- for numDocuments in os.listdir(os.path.join(realDirectory, nodeType, numQuorums, numNodes)):
- if not numDocuments.startswith('.'):
- nodeData[nodeType][numQuorums][numNodes][numDocuments] = {}
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_rounds'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_sent'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_recv'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_sent'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_recv'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_total'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_rounds'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_sent'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_recv'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_sent'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_recv'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_total'] = []
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['throughputs'] = {}
- [nodeData[nodeType][numQuorums][numNodes][numDocuments]['throughputs'].update({i: []}) for i in range(1, MAX_CORES + 1)]
- clientData[nodeType][numQuorums][numNodes][numDocuments] = {}
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_rounds'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_sent'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_recv'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_sent'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_recv'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_total'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_rounds'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_sent'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_recv'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_sent'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_recv'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_total'] = []
- clientData[nodeType][numQuorums][numNodes][numDocuments]['latencies'] = {}
- [clientData[nodeType][numQuorums][numNodes][numDocuments]['latencies'].update({i: []}) for i in range(1, MAX_CORES + 1)]
- for seed in os.listdir(os.path.join(realDirectory, nodeType, numQuorums, numNodes, numDocuments)):
- if not seed.startswith('.'):
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed] = {}
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['throughput_valid'] = False
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_range_accesses'] = {}
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_direct_accesses'] = {}
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['phf_generations'] = {}
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['pir_retrievals'] = {}
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['database_ot_accesses'] = {}
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed] = {}
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['latency_valid'] = False
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_range_accesses'] = {}
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_direct_accesses'] = {}
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['phf_generations'] = {}
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['pir_retrievals'] = {}
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['database_ot_accesses'] = {}
- try:
- with open(os.path.expanduser(os.path.join(realDirectory, nodeType, numQuorums, numNodes, numDocuments, seed, 'all_node_calculations.out')), 'r') as nodeBonusFile:
- ft_range_access = False
- ft_direct_access = False
- phf_generation = False
- pir_retrieval = False
- database_ot_access = False
- for line in nodeBonusFile:
- if line.rstrip() == "FT Range Accesses":
- ft_range_access = True
- elif line.rstrip() == "FT Direct Accesses":
- ft_range_access = False
- ft_direct_access = True
- elif line.rstrip() == "PHF Generations":
- ft_direct_access = False
- phf_generation = True
- elif line.rstrip() == "PIR Retrievals":
- phf_generation = False
- pir_retrieval = True
- elif line.rstrip() == "Database OT Accesses":
- ft_direct_access = False
- database_ot_access = True
- else:
- k = float(line.rstrip().split(',')[0])
- v = float(line.rstrip().split(',')[1])
- if ft_range_access:
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_range_accesses'][k] = v
- elif ft_direct_access:
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_direct_accesses'][k] = v
- elif phf_generation:
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['phf_generations'][k] = v
- elif pir_retrieval:
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['pir_retrievals'][k] = v
- elif database_ot_access:
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['database_ot_accesses'][k] = v
- nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['throughput_valid'] = True
- except FileNotFoundError as e:
- pass
- try:
- with open(os.path.expanduser(os.path.join(realDirectory, nodeType, numQuorums, numNodes, numDocuments, seed, 'avg_node.out')), 'r') as nodeFile:
- values = [float(x.split(',')[0]) for x in nodeFile.read().split('\n')[1:] if len(x) > 0]
-
- ##
- # For all values here, the data was stored as the advertised value averaged across all nodes
- # (of which there are numQuorums * numNodes, since numNodes == number of nodes per quorum)
- # What we actually want to measure, though, is the advertised value averaged across all queries,
- # because we want to know, when I make a query, how many bytes/rounds/whatever does it incur?
- # Hence, the weird multiplications/divisions going on here.
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_rounds'].append(values[0] * int(numQuorums) * int(numNodes) / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_sent'].append(values[1] * int(numQuorums) * int(numNodes) / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_recv'].append(values[2] * int(numQuorums) * int(numNodes) / int(numDocuments))
- total_bytes_sent = values[3] * int(numQuorums) * int(numNodes)
- total_bytes_recv = values[4] * int(numQuorums) * int(numNodes)
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_sent'].append(total_bytes_sent / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_recv'].append(total_bytes_recv / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_total'].append((total_bytes_sent + total_bytes_recv) / int(numDocuments))
- send_transmission_time = total_bytes_sent / AVERAGE_SERVER_BANDWIDTH_ESTIMATE
- recv_transmission_time = total_bytes_recv / AVERAGE_SERVER_BANDWIDTH_ESTIMATE
- ##
- # Although RTT matters to calculate real latency,
- # it doesn't inhibit the amount of data that any given node can push out at a time,
- # which is all we actually care about here.
- baseline_latency = send_transmission_time + recv_transmission_time
- extra_latency = 0
- ##
- # the group operations for OT are basically null,
- # but the system has to encrypt the whole database every time
- # (QP finger tables, only present in QP, QP+LastHop, and DHTPIR)
- # (note that, the encryption has to happen when the ranges are requested,
- # not on the direct access, so we don't use the ft_direct_accesses value)
- size_of_one_ft_element = (SIZE_OF_IP_ADDRESS * int(numNodes) + SIZE_OF_KEY)
- for k, v in nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_range_accesses'].items():
- extra_latency += (size_of_one_ft_element * k * v) / ENCRYPTION_SPEED_ESTIMATE
- ##
- # Now we're doing OT on the database, with the same caveat as before
- # (OT on the database only present in QP+LastHop)
- for k, v in nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['database_ot_accesses'].items():
- ##
- # Here, we divide by numNodes because for any OT access, only one node in the quorum actually performs it.
- # To get the amount of time any one node in a quorum spends calculating for a PIR retrieval, we have to factor that out
- extra_latency += (SIZE_OF_CHUNK * k * v) / ENCRYPTION_SPEED_ESTIMATE
- ##
- # For PIR, the server crypto time is relevant (and calculated here),
- # but the client crypto time is negligible (and ignored here)
- # (PIR stuff only present in DHTPIR)
- avg_db_size = int(numDocuments) / int(numQuorums)
- for k, v in nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['pir_retrievals'].items():
- estimates = [abs(avg_db_size - k*i) for i in range(9)]
- blocking_factor = estimates.index(min(estimates))
- extra_latency += (blocking_factor * SIZE_OF_CHUNK * k * v) / PIR_SPEED_ESTIMATE
- ##
- # For both the below values, we divide by numDocuments because
- # what we've calculated so far is in units: (s / numDocuments*lookups), not (s / lookups)
- # And we divide by numNodes because
- # what we've calculated counts each node together,
- # while it makes more sense to calculate the amount of time it takes a node to service any query
- # divided by the chance that that node participates in any given action
- # (remember that, for all OT stuff, in the simulation, only one node has to respond)
- # (also note that, for PIR, the way it was recorded counted numNodes copies of each individual PIR query,
- # because it just pulled data from nodes individually, and all participate in each PIR response)
- baseline_latency /= int(numDocuments) * int(numNodes)
- extra_latency /= int(numDocuments) * int(numNodes)
- # And then, those values are in s / lookup, not lookups / s, so invert them
- num_lookups_bound_by_network = baseline_latency ** -1
- num_lookups_bound_by_cpu = {}
- if extra_latency == 0:
- [num_lookups_bound_by_cpu.update({i: num_lookups_bound_by_network}) for i in range(1, MAX_CORES + 1)]
- else:
- [num_lookups_bound_by_cpu.update({i: (extra_latency / i) ** -1}) for i in range(1, MAX_CORES + 1)]
- # if seed == "b" and (nodeType == "DHTPIR_Quorum" or nodeType == "QPLastHop_Quorum"):
- # print(nodeType + "/" + numQuorums + "/" + numNodes + "/" + numDocuments + "/" + seed)
- # print ("Network: " + str(num_lookups_bound_by_network))
- # print ("CPU: " + str(num_lookups_bound_by_cpu))
- if nodeData[nodeType][numQuorums][numNodes][numDocuments][seed]['throughput_valid'] or nodeType == "Base_Node" or nodeType == "RCP_Quorum":
- [nodeData[nodeType][numQuorums][numNodes][numDocuments]['throughputs'][i].append(min(num_lookups_bound_by_network, num_lookups_bound_by_cpu[i]) * int(numQuorums)) for i in range(1, MAX_CORES + 1)]
- except FileNotFoundError as e:
- pass
- try:
- with open(os.path.expanduser(os.path.join(realDirectory, nodeType, numQuorums, numNodes, numDocuments, seed, 'avg_node_pub.out')), 'r') as nodeFile:
- values = [float(x.split(',')[0]) for x in nodeFile.read().split('\n')[1:] if len(x) > 0]
-
- ##
- # For all values here, the data was stored as the advertised value averaged across all nodes
- # (of which there are numQuorums * numNodes, since numNodes == number of nodes per quorum)
- # What we actually want to measure, though, is the advertised value averaged across all queries,
- # because we want to know, when I make a query, how many bytes/rounds/whatever does it incur?
- # Hence, the weird multiplications/divisions going on here.
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_rounds'].append(values[0] * int(numQuorums) * int(numNodes) / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_sent'].append(values[1] * int(numQuorums) * int(numNodes) / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_recv'].append(values[2] * int(numQuorums) * int(numNodes) / int(numDocuments))
- total_bytes_sent = values[3] * int(numQuorums) * int(numNodes)
- total_bytes_recv = values[4] * int(numQuorums) * int(numNodes)
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_sent'].append(total_bytes_sent / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_recv'].append(total_bytes_recv / int(numDocuments))
- nodeData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_total'].append((total_bytes_sent + total_bytes_recv) / int(numDocuments))
- except FileNotFoundError as e:
- pass
- try:
- with open(os.path.expanduser(os.path.join(realDirectory, nodeType, numQuorums, numNodes, numDocuments, seed, 'client_latency.out')), 'r') as clientBonusFile:
- ft_range_access = False
- ft_direct_access = False
- pir_retrieval = False
- for line in clientBonusFile:
- if line.rstrip() == "FT Range Accesses":
- ft_range_access = True
- elif line.rstrip() == "FT Direct Accesses":
- ft_range_access = False
- ft_direct_access = True
- elif line.rstrip() == "PIR Retrievals":
- ft_direct_access = False
- pir_retrieval = True
- elif line.rstrip() == "Database OT Accesses":
- ft_direct_access = False
- database_ot_access = True
- else:
- k = float(line.rstrip().split(',')[0])
- v = float(line.rstrip().split(',')[1])
- if ft_range_access:
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_range_accesses'][k] = v
- elif ft_direct_access:
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_direct_accesses'][k] = v
- elif pir_retrieval:
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['pir_retrievals'][k] = v
- elif database_ot_access:
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['database_ot_accesses'][k] = v
- clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['latency_valid'] = True
- except FileNotFoundError as e:
- pass
- try:
- with open(os.path.expanduser(os.path.join(realDirectory, nodeType, numQuorums, numNodes, numDocuments, seed, 'client.out')), 'r') as clientFile:
- values = [float(x) for x in clientFile.read().split('\n')[0].split(',')]
-
- total_rounds_participated = values[0]
- total_bytes_sent = values[3]
- total_bytes_recv = values[4]
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_rounds'].append(total_rounds_participated / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_sent'].append(values[1] / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_messages_recv'].append(values[2] / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_sent'].append(total_bytes_sent / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_recv'].append(total_bytes_recv / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_bytes_total'].append((total_bytes_sent + total_bytes_recv) / int(numDocuments))
- rtt_portion = AVERAGE_RTT_ESTIMATE * total_rounds_participated
- send_transmission_time = total_bytes_sent / AVERAGE_CLIENT_BANDWIDTH_ESTIMATE
- recv_transmission_time = total_bytes_recv / AVERAGE_CLIENT_BANDWIDTH_ESTIMATE
- baseline_latency = rtt_portion + send_transmission_time + recv_transmission_time
- extra_latency = 0
- ##
- # the group operations for OT are basically null,
- # but the system has to encrypt the whole database every time
- # (QP finger tables, only present in QP, QP+LastHop, and DHTPIR)
- # (note that, the encryption has to happen when the ranges are requested,
- # not on the direct access, so we don't use the ft_direct_accesses value)
- size_of_one_ft_element = (SIZE_OF_IP_ADDRESS * int(numNodes) + SIZE_OF_KEY)
- for k, v in clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['ft_range_accesses'].items():
- extra_latency += (size_of_one_ft_element * k * v) / ENCRYPTION_SPEED_ESTIMATE
- ##
- # Now we're doing OT on the database, with the same caveat as before
- # (OT on the database only present in QP+LastHop)
- for k, v in clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['database_ot_accesses'].items():
- extra_latency += (SIZE_OF_CHUNK * k * v) / ENCRYPTION_SPEED_ESTIMATE
- ##
- # For PIR, the server crypto time is relevant (and calculated here),
- # but the client crypto time is negligible (and ignored here)
- # (PIR stuff only present in DHTPIR)
- avg_db_size = int(numDocuments) / int(numQuorums)
- for k, v in clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['pir_retrievals'].items():
- estimates = [abs(avg_db_size - k*i) for i in range(9)]
- blocking_factor = estimates.index(min(estimates))
- extra_latency += (blocking_factor * SIZE_OF_CHUNK * k * v) / PIR_SPEED_ESTIMATE
-
- ##
- # For both the below values, we divide by numDocuments because
- # what we've calculated so far is in units: (s / numDocuments*lookups), not (s / lookups)
- baseline_latency /= int(numDocuments)
- extra_latency /= int(numDocuments)
- cpu_latencies = {}
- [cpu_latencies.update({i: extra_latency / i}) for i in range(1, MAX_CORES + 1)]
- if clientData[nodeType][numQuorums][numNodes][numDocuments][seed]['latency_valid'] or nodeType == "Base_Node" or nodeType == "RCP_Quorum":
- [clientData[nodeType][numQuorums][numNodes][numDocuments]['latencies'][i].append(baseline_latency + cpu_latencies[i]) for i in range(1, MAX_CORES + 1)]
- except FileNotFoundError as e:
- pass
- try:
- with open(os.path.expanduser(os.path.join(realDirectory, nodeType, numQuorums, numNodes, numDocuments, seed, 'client_pub.out')), 'r') as clientFile:
- values = [float(x) for x in clientFile.read().split('\n')[0].split(',')]
-
- total_rounds_participated = values[0]
- total_bytes_sent = values[3]
- total_bytes_recv = values[4]
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_rounds'].append(total_rounds_participated / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_sent'].append(values[1] / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_messages_recv'].append(values[2] / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_sent'].append(total_bytes_sent / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_recv'].append(total_bytes_recv / int(numDocuments))
- clientData[nodeType][numQuorums][numNodes][numDocuments]['num_pub_bytes_total'].append((total_bytes_sent + total_bytes_recv) / int(numDocuments))
- except FileNotFoundError as e:
- pass
- return nodeData, clientData
- def plotBasicComparison(data, numQuorums, numNodes, whichGraph, comparisonGuidelines, **kwargs):
- title = kwargs['title'] if 'title' in kwargs else ''
- xLabel = kwargs['xLabel'] if 'xLabel' in kwargs else ''
- yLabel = kwargs['yLabel'] if 'yLabel' in kwargs else ''
- fileNameStr = kwargs['fileNameStr'] if 'fileNameStr' in kwargs else f"basic-{whichGraph}-{numQuorums}-{numNodes}"
- legendLoc = kwargs['legendLoc'] if 'legendLoc' in kwargs else "best"
- legendBBoxAnchor = kwargs['legendBBoxAnchor'] if 'legendBBoxAnchor' in kwargs else (0, 1)
- nodeTypeSelection = kwargs['nodeTypeSelection'] if 'nodeTypeSelection' in kwargs else ['DHTPIR_Quorum', 'QPLastHop_Quorum', 'QP_Quorum', 'RCP_Quorum', 'Base_Node']
- loglog = kwargs['loglog'] if 'loglog' in kwargs else True
- yLim = kwargs['yLim'] if 'yLim' in kwargs else False
- aspect = kwargs['aspect'] if 'aspect' in kwargs else None
- fig = plt.figure()
- ax = fig.gca()
- for selection in nodeTypeSelection:
- xs = []
- xTickLabels = []
- ys = []
- yErrs = []
- if isinstance(selection, list):
- nodeName = selection[0]
- cores = selection[1]
- else:
- nodeName = selection
- cores = 1
- legend = NODE_TYPE_OPTIONS[nodeName][cores]['legend']
- marker = NODE_TYPE_OPTIONS[nodeName][cores]['marker']
- linestyle = NODE_TYPE_OPTIONS[nodeName][cores]['linestyle']
- color = NODE_TYPE_OPTIONS[nodeName][cores]['color']
-
- realNumQuorums = str(numQuorums)
- realNumNodes = str(numNodes)
- if nodeName == "Base_Node":
- realNumQuorums = str(numQuorums * numNodes)
- realNumNodes = "1"
-
- for x in comparisonGuidelines:
- if whichGraph == 'latencies' or whichGraph == 'throughputs':
- curr_data = data[nodeName][realNumQuorums][realNumNodes][f"{x*numQuorums}"][whichGraph][cores]
- else:
- curr_data = data[nodeName][realNumQuorums][realNumNodes][f"{x*numQuorums}"][whichGraph]
- mean = np.mean(curr_data)
- std = np.std(curr_data)
- sqrt_len = sqrt(len(curr_data))
- xs.append(x)
- xTickLabels.append(f"{x}" if x < 1000 else f"{x/1000}k")
- ys.append(mean)
- yErrs.append(Z_STAR * std / sqrt_len)
- line, _, _ = ax.errorbar(xs, ys, yerr=yErrs, capsize=7.0, label=legend, marker=marker, linestyle=linestyle, color=color)
- ax.set_title(title, fontsize='x-large')
- ax.set_xlabel(xLabel, fontsize='large')
- ax.set_ylabel(yLabel, fontsize='large')
- ax.set_xscale("log")
- ax.set_xticks(comparisonGuidelines)
- ax.set_xticklabels(xTickLabels)
- if loglog:
- ax.set_yscale("log")
- else:
- bottom, top = ax.get_ylim()
- bottom = (0 if bottom > 0 else bottom)
- ax.set_ylim(bottom=bottom)
- if yLim:
- ax.set_ylim(bottom=yLim[0], top=yLim[1])
- if aspect:
- ax.set_aspect(aspect, adjustable='box')
- legend = ax.legend(loc=legendLoc, bbox_to_anchor=legendBBoxAnchor, fontsize='large')
- fig.savefig(f"{fileNameStr}.pdf", bbox_inches='tight')
- plt.close(fig)
- def plotAdvancedComparison(data, whichGraph, comparisonGuidelines, **kwargs):
- title = kwargs['title'] if 'title' in kwargs else ''
- xLabel = kwargs['xLabel'] if 'xLabel' in kwargs else ''
- yLabel = kwargs['yLabel'] if 'yLabel' in kwargs else ''
- fileNameStr = kwargs['fileNameStr'] if 'fileNameStr' in kwargs else f"advanced-{whichGraph}"
- legendLoc = kwargs['legendLoc'] if 'legendLoc' in kwargs else "best"
- legendBBoxAnchor = kwargs['legendBBoxAnchor'] if 'legendBBoxAnchor' in kwargs else (0, 1)
- nodeTypeSelection = kwargs['nodeTypeSelection'] if 'nodeTypeSelection' in kwargs else ['DHTPIR_Quorum', 'QPLastHop_Quorum', 'QP_Quorum', 'RCP_Quorum', 'Base_Node']
- loglog = kwargs['loglog'] if 'loglog' in kwargs else True
- yLim = kwargs['yLim'] if 'yLim' in kwargs else False
- fig = plt.figure()
- ax = fig.gca()
- xTicks = []
- xTickLabels = []
- for selection in nodeTypeSelection:
- xs = []
- ys = []
- yErrs = []
- if isinstance(selection, list):
- nodeName = selection[0]
- cores = selection[1]
- else:
- nodeName = selection
- cores = 1
- legend = NODE_TYPE_OPTIONS[nodeName][cores]['legend']
- marker = NODE_TYPE_OPTIONS[nodeName][cores]['marker']
- linestyle = NODE_TYPE_OPTIONS[nodeName][cores]['linestyle']
- color = NODE_TYPE_OPTIONS[nodeName][cores]['color']
- for comparison in comparisonGuidelines:
- numQuorums = comparison[0]
- numNodes = comparison[1]
- numDocuments = comparison[2]
- realNumQuorums = str(numQuorums)
- realNumNodes = str(numNodes)
- realNumDocuments = str(numDocuments)
- if nodeName == "Base_Node":
- realNumQuorums = str(int(numQuorums) * int(numNodes))
- realNumNodes = "1"
- if len(xTicks) < len(comparisonGuidelines):
- xTicks.append(numQuorums)
- xTickLabels.append((f"{numQuorums}" if numQuorums < 1000 else f"{numQuorums/1000}k") + f",{str(numNodes)}")
- if whichGraph == 'latencies' or whichGraph == 'throughputs':
- curr_data = data[nodeName][realNumQuorums][realNumNodes][realNumDocuments][whichGraph][cores]
- else:
- curr_data = data[nodeName][realNumQuorums][realNumNodes][realNumDocuments][whichGraph]
- mean = np.mean(curr_data)
- std = np.std(curr_data)
- sqrt_len = sqrt(len(curr_data))
- xs.append(numQuorums)
- ys.append(mean)
- yErrs.append(Z_STAR * std / sqrt_len)
- line, _, _ = ax.errorbar(xs, ys, yerr=yErrs, capsize=7.0, label=legend, marker=marker, linestyle=linestyle, color=color)
- ax.set_title(title, fontsize='large')
- ax.set_xlabel(xLabel, fontsize='medium')
- ax.set_ylabel(yLabel, fontsize='medium')
- ax.set_xscale("log")
- ax.set_xticks([], minor=True)
- ax.set_xticks(xTicks)
- ax.set_xticklabels(xTickLabels)
- bottom, top = ax.get_ylim()
- if loglog:
- ax.set_yscale("log")
- else:
- bottom = (0 if bottom > 0 else bottom)
- ax.set_ylim(bottom=bottom)
- if top > 100000:
- yTickLabels = ['{:}K'.format(int(int(x)/1000)) for x in ax.get_yticks().tolist()]
- ax.set_yticklabels(yTickLabels)
- if yLim:
- ax.set_ylim(bottom=yLim[0], top=yLim[1])
- legend = ax.legend(loc=legendLoc, bbox_to_anchor=legendBBoxAnchor, fontsize='medium')
- with cd('../plots', True):
- fig.savefig(f"{fileNameStr}.pdf", bbox_extra_artists=(legend,), bbox_inches='tight')
- plt.close(fig)
- def main(dataDirectory, plotOptionsFile):
- nodeData, clientData = readData(dataDirectory)
- plotOptions = []
- with open(plotOptionsFile, 'r') as options:
- plotOptions = json.load(options)
- for option in plotOptions:
- if option['type'].lower() == 'basic' or option['type'].lower() == 'b':
- try:
- data = nodeData if (option['data'].lower() == "node" or option['data'].lower() == "n") else clientData
- numQuorums = option['numQuorums']
- numNodes = option['numNodes']
- whichGraph = option['whichGraph']
- comparisonGuidelines = option["comparisonGuidelines"]
- except KeyError as e:
- continue
-
- kwargs = {}
- if "title" in option:
- kwargs["title"] = option["title"]
- if "xLabel" in option:
- kwargs["xLabel"] = option["xLabel"]
- if "yLabel" in option:
- kwargs["yLabel"] = option["yLabel"]
- if "fileNameStr" in option:
- kwargs["fileNameStr"] = option["fileNameStr"]
- if "legendLoc" in option:
- kwargs["legendLoc"] = option["legendLoc"]
- if "legendBBoxAnchor" in option:
- anchor = (option["legendBBoxAnchor"][0], option["legendBBoxAnchor"][1])
- kwargs["legendBBoxAnchor"] = anchor
- if "nodeTypeSelection" in option:
- kwargs["nodeTypeSelection"] = option["nodeTypeSelection"]
- if "loglog" in option:
- kwargs["loglog"] = option["loglog"]
- if "yLim" in option:
- kwargs["yLim"] = option["yLim"]
- if "aspect" in option:
- kwargs["aspect"] = option["aspect"]
- plotBasicComparison(data, numQuorums, numNodes, whichGraph, comparisonGuidelines, **kwargs)
- else:
- try:
- data = nodeData if (option['data'].lower() == "node" or option['data'].lower() == "n") else clientData
- whichGraph = option['whichGraph']
- comparisonGuidelines = option["comparisonGuidelines"]
- except KeyError as e:
- continue
- kwargs = {}
- if "title" in option:
- kwargs["title"] = option["title"]
- if "xLabel" in option:
- kwargs["xLabel"] = option["xLabel"]
- if "yLabel" in option:
- kwargs["yLabel"] = option["yLabel"]
- if "fileNameStr" in option:
- kwargs["fileNameStr"] = option["fileNameStr"]
- if "legendLoc" in option:
- kwargs["legendLoc"] = option["legendLoc"]
- if "legendBBoxAnchor" in option:
- anchor = (option["legendBBoxAnchor"][0], option["legendBBoxAnchor"][1])
- kwargs["legendBBoxAnchor"] = anchor
- if "nodeTypeSelection" in option:
- kwargs["nodeTypeSelection"] = option["nodeTypeSelection"]
- if "loglog" in option:
- kwargs["loglog"] = option["loglog"]
- if "yLim" in option:
- kwargs["yLim"] = option["yLim"]
- plotAdvancedComparison(data, whichGraph, comparisonGuidelines, **kwargs)
- if __name__ == "__main__":
- main("../outputs", "plots.json")
|