add bulk_run

This commit is contained in:
Youngjoon Lee 2024-05-24 21:31:06 +09:00
parent 812061ab60
commit af94346279
No known key found for this signature in database
GPG Key ID: 09B750B5BD6F08A2
3 changed files with 128 additions and 4 deletions

124
mixnet/v2/sim/bulk_run.py Normal file
View File

@ -0,0 +1,124 @@
import argparse
import pandas as pd
import seaborn
from matplotlib import pyplot as plt
from config import P2PConfig, Config
from analysis import Analysis
from simulation import Simulation
COL_P2P_TYPE = "P2P Type"
COL_NUM_NODES = "Num Nodes"
COL_TRAFFIC_TYPE = "Traffic Type"
COL_STAT = "Stat"
COL_BANDWIDTH = "Bandwidth"
TRAFFIC_TYPE_INGRESS = "Ingress"
TRAFFIC_TYPE_EGRESS = "Egress"
STAT_MEAN = "mean"
STAT_MAX = "max"
def bulk_run():
parser = argparse.ArgumentParser(description="Run simulation",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--config", type=str, required=True, help="Configuration file path")
args = parser.parse_args()
config = Config.load(args.config)
data = {
COL_P2P_TYPE: [],
COL_NUM_NODES: [],
COL_TRAFFIC_TYPE: [],
COL_STAT: [],
COL_BANDWIDTH: [],
}
message_size_df = None
for p2p_type in [P2PConfig.TYPE_ONE_TO_ALL, P2PConfig.TYPE_GOSSIP]:
config.p2p.type = p2p_type
for num_nodes in [10, 100, 1000]:
config.mixnet.num_nodes = num_nodes
sim = Simulation(config)
sim.run()
if message_size_df is None:
message_size_df = Analysis(sim, config).message_size_distribution()
nonzero_ingresses, nonzero_egresses = [], []
for ingress_bandwidths, egress_bandwidths in zip(sim.p2p.measurement.ingress_bandwidth_per_time,
sim.p2p.measurement.egress_bandwidth_per_time):
for node in sim.p2p.nodes:
ingress = ingress_bandwidths[node] / 1024.0
egress = egress_bandwidths[node] / 1024.0
if ingress > 0:
nonzero_ingresses.append(ingress)
if egress > 0:
nonzero_egresses.append(egress)
ingresses = pd.Series(nonzero_ingresses)
add_data(data, p2p_type, num_nodes, TRAFFIC_TYPE_INGRESS, STAT_MEAN, ingresses.mean())
add_data(data, p2p_type, num_nodes, TRAFFIC_TYPE_INGRESS, STAT_MAX, ingresses.max())
egresses = pd.Series(nonzero_egresses)
add_data(data, p2p_type, num_nodes, TRAFFIC_TYPE_EGRESS, STAT_MEAN, egresses.mean())
add_data(data, p2p_type, num_nodes, TRAFFIC_TYPE_EGRESS, STAT_MAX, egresses.max())
df = pd.DataFrame(data)
draw_bandwidth_plot(df, TRAFFIC_TYPE_INGRESS, config, message_size_df)
draw_bandwidth_plot(df, TRAFFIC_TYPE_EGRESS, config, message_size_df)
def add_data(data: dict, p2p_type: str, num_nodes: int, bandwidth_type: str, stat: str, bandwidth: float):
data[COL_P2P_TYPE].append(p2p_type)
data[COL_NUM_NODES].append(num_nodes)
data[COL_TRAFFIC_TYPE].append(bandwidth_type)
data[COL_STAT].append(stat)
data[COL_BANDWIDTH].append(bandwidth)
def draw_bandwidth_plot(df: pd.DataFrame, traffic_type: str, config: Config, message_size_df: pd.DataFrame):
ingress_df = df[df[COL_TRAFFIC_TYPE] == traffic_type]
plt.figure(figsize=(12, 6))
mean_df = ingress_df[ingress_df[COL_STAT] == STAT_MEAN]
seaborn.barplot(data=mean_df, x=COL_NUM_NODES, y=COL_BANDWIDTH, hue=COL_P2P_TYPE, ax=plt.gca(), capsize=0.1)
max_df = ingress_df[ingress_df[COL_STAT] == STAT_MAX]
barplot = seaborn.barplot(data=max_df, x=COL_NUM_NODES, y=COL_BANDWIDTH, hue=COL_P2P_TYPE, ax=plt.gca(),
capsize=0.1, alpha=0.5)
# Adding labels to each bar
for p in barplot.patches:
height = p.get_height()
if height > 0: # Only label bars with positive height
barplot.annotate(format(height, ".2f"),
(p.get_x() + p.get_width() / 2., height),
ha="center", va="center",
xytext=(0, 9),
textcoords="offset points")
plt.title(f"{traffic_type} Bandwidth")
plt.xlabel(COL_NUM_NODES)
plt.ylabel(f"{COL_BANDWIDTH} (KB/s)")
# Custom legend to show Mean and Max
handles, labels = barplot.get_legend_handles_labels()
for i in range(len(labels) // 2):
labels[i] = labels[i] + f" ({STAT_MEAN})"
for i in range(len(labels) // 2, len(labels)):
labels[i] = labels[i] + f" ({STAT_MAX})"
plt.legend(handles=handles, labels=labels, loc="upper left")
desc = (
f"message: {message_size_df["message_size"].mean():.0f} bytes\n"
f"{config.description()}"
)
plt.text(1.02, 0.5, desc, transform=plt.gca().transAxes, verticalalignment="center", fontsize=12)
plt.subplots_adjust(right=0.8) # Adjust layout to make room for the text
plt.show()
if __name__ == "__main__":
bulk_run()

View File

@ -96,18 +96,18 @@ class MixnetConfig:
@dataclass
class P2PConfig:
# Broadcasting type: naive | gossip
# Broadcasting type: 1-to-all | gossip
type: str
# A connection density, only if the type is gossip
connection_density: int
# A maximum network latency between nodes directly connected with each other
max_network_latency: float
TYPE_NAIVE = "naive"
TYPE_ONE_TO_ALL = "1-to-all"
TYPE_GOSSIP = "gossip"
def validate(self):
assert self.type in [self.TYPE_NAIVE, self.TYPE_GOSSIP]
assert self.type in [self.TYPE_ONE_TO_ALL, self.TYPE_GOSSIP]
if self.type == self.TYPE_GOSSIP:
assert self.connection_density > 0
assert self.max_network_latency >= 0

View File

@ -22,7 +22,7 @@ class Simulation:
@classmethod
def init_p2p(cls, env: simpy.Environment, config: Config):
match config.p2p.type:
case P2PConfig.TYPE_NAIVE:
case P2PConfig.TYPE_ONE_TO_ALL:
return NaiveBroadcastP2P(env, config)
case P2PConfig.TYPE_GOSSIP:
return GossipP2P(env, config)