Skip to content

Commit

Permalink
Add CmpE49F and CmpE487 Final project
Browse files Browse the repository at this point in the history
  • Loading branch information
enescakir committed Dec 31, 2018
1 parent 3f8b0f8 commit ab8a1ae
Show file tree
Hide file tree
Showing 14 changed files with 856 additions and 2 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
cmpe443
cmpe487/final-project
cmpe49f/
1 change: 1 addition & 0 deletions cmpe487/final-project
Submodule final-project added at d3a72a
1 change: 1 addition & 0 deletions cmpe49f/cache/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
log.txt
18 changes: 18 additions & 0 deletions cmpe49f/cache/README.md
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`
37 changes: 37 additions & 0 deletions cmpe49f/cache/config.py
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
51 changes: 51 additions & 0 deletions cmpe49f/cache/graph.py
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()
68 changes: 68 additions & 0 deletions cmpe49f/cache/main.py
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")
33 changes: 33 additions & 0 deletions cmpe49f/cache/models/cache.py
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)
32 changes: 32 additions & 0 deletions cmpe49f/cache/models/content.py
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
75 changes: 75 additions & 0 deletions cmpe49f/cache/models/log.py
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))
66 changes: 66 additions & 0 deletions cmpe49f/cache/models/network.py
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
Loading

0 comments on commit ab8a1ae

Please sign in to comment.