-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CmpE49F and CmpE487 Final project
- Loading branch information
Showing
14 changed files
with
856 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1 @@ | ||
cmpe443 | ||
cmpe487/final-project | ||
cmpe49f/ |
Submodule final-project
added at
d3a72a
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
log.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
## CmpE 49F | ||
### Caching Project | ||
|
||
### Requirements | ||
- python 3 | ||
- simpy | ||
- numpy | ||
- matplotlib | ||
|
||
### Usage | ||
```bash | ||
$ python3 main.py | ||
# You can overwrite time, count, and algorithm via command arguments | ||
$ python3 main.py --time=2000 --count=1 --algorithm=MY | ||
# Algorithm parameter has 4 different options: LRU | LFU | RAND | MY | ||
``` | ||
|
||
You can set general system parameters at `config.py` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import math | ||
|
||
# GENERAL | ||
ALGORITHM = "LRU" # LRU | LFU | RAND | MY | ||
SIMULATION_TIME = 100 | ||
SEED = 10 | ||
NUMBER_OF_SIMULATIONS = 10 | ||
|
||
# CONTENT | ||
NUMBER_OF_CONTENTS = 100 | ||
ZIPF_PARAMETER = 0.8 | ||
LAMBDA_BASE_SIZE = 25e6 | ||
LAMBDA_ENC_SIZE = 5e6 | ||
PROBABILITY_HQ = 0.5 | ||
|
||
# USER | ||
LAMBDA_USERS_PPP = 0.0015 | ||
LAMBDA_PRIMARY_USER = 1 | ||
LAMBDA_SECONDARY_USER = 1.5 | ||
PRIMARY_USER_DISTANCE = 150 | ||
CACHE_CAPACITY = 1e9 | ||
NUMBER_OF_CACHE_TRY = 5 | ||
|
||
# ENV | ||
RADIUS = 300 | ||
DEVICE_RADIUS = math.sqrt((NUMBER_OF_CONTENTS / LAMBDA_USERS_PPP) / math.pi) | ||
|
||
# NETWORK | ||
NUMBER_OF_CHANNELS = 10 | ||
CHANNEL_BANDWIDTH = 2e6 | ||
LAMBDA_PRIMARY_CHANNEL = LAMBDA_PRIMARY_USER / NUMBER_OF_CHANNELS | ||
INITIAL_FREQUENCY = 700e6 | ||
NOISE = 1.6e-19 | ||
|
||
# PERFORMANCE | ||
BASE_LATENCY = 0.25 | ||
ENC_LATENCY = 0.05 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import matplotlib.pyplot as plt | ||
import matplotlib.patches as mpatch | ||
|
||
|
||
def draw_plot(logs, sim_time): | ||
fig, ax = plt.subplots() | ||
data = [] | ||
for i in range(10): | ||
data.append({"x": [], "color": [], "border": []}) | ||
|
||
for log in logs: | ||
if log.ev_type == "SERVED" and log.user == "PU": | ||
data[log.freq]['x'].append((log.start, log.end - log.start)) | ||
data[log.freq]['color'].append('#7db1fc') | ||
data[log.freq]['border'].append('#004c99') | ||
elif log.ev_type == "SERVED" and log.user == "SU" and log.layer == "BASE": | ||
data[log.freq]['x'].append((log.start, log.end - log.start)) | ||
data[log.freq]['color'].append('#a8e2a5') | ||
data[log.freq]['border'].append('#24a624') | ||
|
||
elif log.ev_type == "SERVED" and log.user == "SU" and log.layer == "ENH": | ||
data[log.freq]['x'].append((log.start, log.end - log.start)) | ||
data[log.freq]['color'].append('#f99f9f') | ||
data[log.freq]['border'].append('#bc48aa') | ||
|
||
elif log.ev_type == "DROPPED" and log.user == "SU" and log.layer == "BASE": | ||
data[log.freq]['x'].append((log.start, log.end - log.start)) | ||
data[log.freq]['color'].append('#fcd1a4') | ||
data[log.freq]['border'].append('#e1b43f') | ||
|
||
elif log.ev_type == "DROPPED" and log.user == "SU" and log.layer == "ENH": | ||
data[log.freq]['x'].append((log.start, log.end - log.start)) | ||
data[log.freq]['color'].append('#f86161') | ||
data[log.freq]['border'].append('#c50c0c') | ||
|
||
for i, channel in enumerate(data): | ||
ax.broken_barh(channel['x'], (700 + i * 2, 2), facecolors=channel['color'], edgecolors=channel['border']) | ||
|
||
ax.set_ylim(700, 720) | ||
ax.set_xlim(0, sim_time) | ||
ax.set_xlabel('Channel history') | ||
ax.set_yticks([702 + 2 * i for i in range(10)]) | ||
ax.set_yticklabels(["f{}".format(i + 1) for i in range(10)]) | ||
ax.grid(True) | ||
ax.legend([mpatch.Rectangle((0, 0), 1, 1, fc="#7db1fc"), | ||
mpatch.Rectangle((0, 0), 1, 1, fc="#a8e2a5"), | ||
mpatch.Rectangle((0, 0), 1, 1, fc="#f99f9f"), | ||
mpatch.Rectangle((0, 0), 1, 1, fc="#fcd1a4"), | ||
mpatch.Rectangle((0, 0), 1, 1, fc="#f86161")], | ||
['PU', 'SU-BASE', 'SU-ENH', 'SU-BASE Drop', 'SU-ENH Drop']) | ||
plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from models.simulation import Simulation | ||
import config | ||
# from config import SEED, SIMULATION_TIME, NUMBER_OF_SIMULATIONS, ALGORITHM | ||
import random | ||
import argparse | ||
from utils import * | ||
import os | ||
from threading import Thread | ||
import time | ||
from graph import * | ||
|
||
|
||
def print_value(name, value): | ||
print("\t{} {}".format(change_style(name + ":", 'green').rjust(44), change_style(value, 'bold'))) | ||
|
||
|
||
# Parse arguments | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('--time', help='simulation time', dest='time', type=int, default=config.SIMULATION_TIME) | ||
parser.add_argument('--count', help='how many time simulation runs', dest='count', type=int, | ||
default=config.NUMBER_OF_SIMULATIONS) | ||
parser.add_argument('--seed', help='random seed', dest='seed', type=int, default=config.SEED) | ||
parser.add_argument('--algorithm', help='cache replacement algorithm: LRU, LFU, RAND, MY', dest='algorithm', | ||
default=config.ALGORITHM) | ||
args = parser.parse_args() | ||
config.SIMULATION_TIME = args.time | ||
config.NUMBER_OF_SIMULATIONS = args.count | ||
config.SEED = args.seed | ||
config.ALGORITHM = args.algorithm | ||
|
||
# Delete old logs | ||
os.system("rm -f log_*.txt") | ||
|
||
# Start simulations | ||
start_time = time.time() | ||
if config.NUMBER_OF_SIMULATIONS == 1: | ||
sim = Simulation(config.SEED, config.SIMULATION_TIME, 1) | ||
sim.start() | ||
draw_plot(sim.logger.logs, config.SIMULATION_TIME) | ||
else: | ||
threads = [] | ||
|
||
for i in range(config.NUMBER_OF_SIMULATIONS): | ||
sim = Simulation(random.randint(1, 9999), config.SIMULATION_TIME, i + 1) | ||
thread = Thread(target=sim.start) | ||
thread.start() | ||
threads.append(thread) | ||
|
||
for t in threads: | ||
t.join() | ||
|
||
end_time = time.time() | ||
|
||
os.system("clear") | ||
print("\n\n" + change_style("=== PERFORMANCE RESULTS ===\n", "blue").rjust(64)) | ||
print_value("ALGORITHM", config.ALGORITHM) | ||
print_value("SIMULATION TIME", config.SIMULATION_TIME) | ||
print_value("SIMULATION COUNT", config.NUMBER_OF_SIMULATIONS) | ||
print_value("REAL TIME", "{:.5f} seconds".format(end_time - start_time)) | ||
print_value("AVERAGE LATENCY", "{:.5f}".format(Simulation.performances['latency'] / config.NUMBER_OF_SIMULATIONS)) | ||
print_value("LOCAL HIT RATE SQ", "{:.5f}".format(Simulation.performances['p']['sq'] / config.NUMBER_OF_SIMULATIONS)) | ||
print_value("LOCAL HIT RATE HQ BASE", | ||
"{:.5f}".format(Simulation.performances['p']['hq']['base'] / config.NUMBER_OF_SIMULATIONS)) | ||
print_value("LOCAL HIT RATE HQ ENH. | BASE LH", | ||
"{:.5f}".format(Simulation.performances['p']['hq']['enh']['base_local_hit'] / config.NUMBER_OF_SIMULATIONS)) | ||
print_value("LOCAL HIT RATE HQ ENH. | BASE D2D", | ||
"{:.5f}".format(Simulation.performances['p']['hq']['enh']['base_d2d'] / config.NUMBER_OF_SIMULATIONS)) | ||
print("\n\n") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from enum import Enum | ||
from models.content import Content | ||
import random | ||
|
||
|
||
class CacheType(Enum): | ||
""" Represents type of cache """ | ||
BASE = "Base" | ||
ENHANCEMENT = "Enh" | ||
|
||
|
||
class Cache: | ||
""" Represents content cache at device cache storage """ | ||
|
||
def __init__(self, id, type, size): | ||
self.id = id | ||
self.type = type | ||
self.size = size | ||
self.LFU = 0 | ||
self.LRU = 0 | ||
self.weight = 0 | ||
|
||
def __str__(self): | ||
return "Cache" + "_" + str(self.type.value) + '_' + str(self.id) | ||
|
||
@staticmethod | ||
def get_random(contents): | ||
""" Return random cache for initial filling""" | ||
content = Content.get_random(contents) | ||
if random.random() < .5: | ||
return Cache(content.id, CacheType.ENHANCEMENT, content.enhancement) | ||
else: | ||
return Cache(content.id, CacheType.BASE, content.base) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from config import ZIPF_PARAMETER, NUMBER_OF_CONTENTS, LAMBDA_BASE_SIZE, LAMBDA_ENC_SIZE | ||
import numpy as np | ||
import random | ||
|
||
|
||
class Content: | ||
""" Represents content in simulation """ | ||
|
||
def __init__(self, id, base, enhancement): | ||
self.id = id | ||
self.base = base | ||
self.enhancement = enhancement | ||
self.popularity = self.calculate_popularity() | ||
|
||
def calculate_popularity(self): | ||
return (1 / (self.id ** ZIPF_PARAMETER)) / sum( | ||
[(1 / (n ** ZIPF_PARAMETER)) for n in range(1, NUMBER_OF_CONTENTS + 1)]) | ||
|
||
@staticmethod | ||
def get_random(contents): | ||
""" Returns random content for secondary user request """ | ||
return np.random.choice(contents, p=[content.popularity for content in contents]) | ||
|
||
@staticmethod | ||
def generate(): | ||
""" Generates initial contents """ | ||
contents = [] | ||
for i in range(1, NUMBER_OF_CONTENTS + 1): | ||
base = random.expovariate(1 / LAMBDA_BASE_SIZE) | ||
enhancement = random.expovariate(1 / LAMBDA_ENC_SIZE) | ||
contents.append(Content(i, int(base), int(enhancement))) | ||
return contents |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
class Logger: | ||
""" Keeps logs of the simulation """ | ||
|
||
def __init__(self, seed): | ||
self.seed = seed | ||
self.logs = [] | ||
self.id_counter = 1 | ||
|
||
def new(self, ev_type, is_hq, user, layer, start, end, tx_device, rec_device, freq, prev_ev_id=None): | ||
""" Creates new log """ | ||
log = Log(self.id_counter, ev_type, "HQ" if is_hq else "SQ", user, layer, start, end, tx_device, rec_device, | ||
freq, prev_ev_id) | ||
self.logs.append(log) | ||
self.id_counter += 1 | ||
|
||
return log.id | ||
|
||
def save(self): | ||
""" Writes logs to file """ | ||
file = open("log_" + str(self.seed) + ".txt", 'w') | ||
header = "| {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} |".format("EV ID".ljust(5), | ||
"PV ID".ljust(5), | ||
"EV TYPE".ljust(12), | ||
"REQ TYPE".ljust(8), | ||
"USE".ljust(3), | ||
"LAYER".ljust(5), | ||
"START TIME".ljust(12), | ||
"END TIME".ljust(12), | ||
"TX DEVICE".ljust(10), | ||
"REC DEVICE".ljust(10), | ||
"FREQUENCY".ljust(10)) | ||
header += "\n" + ("-" * 126) | ||
file.write(header + "\n") | ||
# for log in sorted(Log.logs, key=lambda k: k.start): | ||
for log in self.logs: | ||
file.write(str(log) + "\n") | ||
header += ("-" * 126) | ||
|
||
|
||
class Log: | ||
""" Represents log record for calculations """ | ||
|
||
def __init__(self, id, ev_type, req_type, user, layer, start, end, tx_device, rec_device, freq, prev_ev_id=None): | ||
self.id = id | ||
self.ev_type = ev_type | ||
self.req_type = req_type | ||
self.user = user | ||
self.layer = layer | ||
self.start = start | ||
self.end = end | ||
self.tx_device = tx_device | ||
self.rec_device = rec_device | ||
self.freq = freq | ||
self.prev_ev_id = prev_ev_id if prev_ev_id else self.id | ||
|
||
def __str__(self): | ||
return "| {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} |".format(str(self.id).ljust(5), | ||
str(self.prev_ev_id).ljust(5), | ||
self.ev_type.ljust(12), | ||
self.req_type.ljust(8), | ||
self.user.ljust(3), | ||
self.layer.ljust(5), | ||
"{:.2f} sec".format(self.start).ljust( | ||
12), | ||
"{:.2f} sec".format(self.end).ljust( | ||
12), | ||
( | ||
"NON" if self.tx_device is None else "dev_{}".format( | ||
self.tx_device)).ljust(10), | ||
( | ||
"NON" if self.rec_device is None else "dev_{}".format( | ||
self.rec_device)).ljust(10), | ||
( | ||
"NON" if self.freq is None else "f_{}".format( | ||
self.freq + 1)).ljust(10)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
from simpy import PreemptiveResource, Interrupt | ||
from config import NUMBER_OF_CHANNELS, CHANNEL_BANDWIDTH, INITIAL_FREQUENCY | ||
import random | ||
|
||
|
||
class Network: | ||
""" Handles channel allocation operations """ | ||
|
||
def __init__(self, env): | ||
self.env = env | ||
self.channels = [PreemptiveResource(env, capacity=1) for _ in range(NUMBER_OF_CHANNELS)] | ||
|
||
def serve(self, user, cache, distance): | ||
""" Serves channel to user for transferring content """ | ||
service_time = user.get_service_time(cache, distance) | ||
request = self.channels[user.channel_id].request(priority=user.get_priority(), preempt=True) | ||
request.user = user | ||
success = False | ||
yield request | ||
try: | ||
user.serving = True | ||
user.print("Start serving f_" + str(user.channel_id + 1)) | ||
yield self.env.timeout(service_time) | ||
success = True | ||
except Interrupt: | ||
user.print("Drop serving f_" + str(user.channel_id + 1), 'red') | ||
finally: | ||
self.channels[user.channel_id].release(request) | ||
user.serving = False | ||
if success: | ||
user.print("End serving f_" + str(user.channel_id + 1)) | ||
|
||
return success | ||
|
||
def get_current_type(self, ch): | ||
""" Returns type of current user that allocates given channel""" | ||
|
||
user = self.get_current_user(ch) | ||
if user: | ||
return user.type | ||
return None | ||
|
||
def get_current_user(self, ch): | ||
""" Returns current user that allocates given channel""" | ||
channel = self.channels[ch] | ||
if channel.users: | ||
return channel.users[0].user | ||
|
||
return None | ||
|
||
def get_idle_channel(self): | ||
""" Returns first idle channel """ | ||
idles = [] | ||
for ch in range(NUMBER_OF_CHANNELS): | ||
if self.channels[ch].count == 0: | ||
idles.append(ch) | ||
|
||
if idles: | ||
return random.choice(idles) | ||
|
||
return None | ||
|
||
@staticmethod | ||
def get_channel_frequency(ch): | ||
""" Calculates frequency of given channel """ | ||
return INITIAL_FREQUENCY + (int(ch) - 1) * CHANNEL_BANDWIDTH |
Oops, something went wrong.