Monday, August 31, 2009

Look, everybody! It's a SimPy three tier eBiz simulation! But now with extra statistics! Wowee!

Added some more statistics to the output such as waiting queue length and component utilization by using SimPy Monitors. I couldn't easily use the monitors for the page response times due to the split up nature calls to multiple nodes but for .waitQ and node utilizations they worked out perfectly.



#!/usr/bin/env python

from SimPy.Simulation import *
from random import Random, expovariate, uniform

class Metrics:

metrics = dict()

def Add(self, metricName, frameNumber, value):
if self.metrics.has_key(metricName):
if self.metrics[metricName].has_key(frameNumber):
self.metrics[metricName][frameNumber].append(value)
else:
self.metrics[metricName][frameNumber] = list()
self.metrics[metricName][frameNumber].append(value)
else:
self.metrics[metricName] = dict()
self.metrics[metricName][frameNumber] = list()
self.metrics[metricName][frameNumber].append(value)

def Keys(self):
return self.metrics.keys()

def Mean(self, metricName):
valueArray = list()
if self.metrics.has_key(metricName):
for frame in self.metrics[metricName].keys():
for values in range(len(self.metrics[metricName][frame])):
valueArray.append(self.metrics[metricName][frame][values])

sum = 0.0
for i in range(len(valueArray)):
sum += valueArray[i]

if len(self.metrics[metricName][frame]) != 0:
return sum/len(self.metrics[metricName])
else:
return 0 # Need to learn python throwing exceptions
else:
return 0

class G:

numWS = 1
numAS = 1
numDS = 1

Rnd = random.Random(12345)

PageNames = ["Entry", "Home", "Search", "View", "Login", "Create", "Bid", "Exit" ]

Entry = 0
Home = 1
Search = 2
View = 3
Login = 4
Create = 5
Bid = 6
Exit = 7

WS = 0
AS = 1
DS = 2

CPU = 0
DISK = 1

WS_CPU = 0
WS_DISK = 1
AS_CPU = 2
AS_DISK = 3
DS_CPU = 4
DS_DISK = 5

metrics = Metrics()

# e h s v l c b e
HitCount = [0, 0, 0, 0, 0, 0, 0, 0]

Resources = [[ Resource(1), Resource(1) ], # WS CPU and DISK
[ Resource(1), Resource(1) ], # AS CPU and DISK
[ Resource(1), Resource(1) ]] # DS CPU and DISK

QMon = [[ Monitor(1), Monitor(1)], # WS CPU and DISK
[ Monitor(1), Monitor(1)], # AS CPU and DISK
[ Monitor(1), Monitor(1)]] # DS CPU and DISK

SMon = [[ Monitor(1), Monitor(1)], # WS CPU and DISK
[ Monitor(1), Monitor(1)], # AS CPU and DISK
[ Monitor(1), Monitor(1)]] # DS CPU and DISK

# Enter Home Search View Login Create Bid Exit
ServiceDemand = [ [0.000, 0.008, 0.009, 0.011, 0.060, 0.012, 0.015, 0.000], # WS_CPU
[0.000, 0.030, 0.010, 0.010, 0.010, 0.010, 0.010, 0.000], # WS_DISK
[0.000, 0.000, 0.030, 0.035, 0.025, 0.045, 0.040, 0.000], # AS_CPU
[0.000, 0.000, 0.008, 0.080, 0.009, 0.011, 0.012, 0.000], # AS_DISK
[0.000, 0.000, 0.010, 0.009, 0.015, 0.070, 0.045, 0.000], # DS_CPU
[0.000, 0.000, 0.035, 0.018, 0.050, 0.080, 0.090, 0.000] ] # DS_DISK

# Type B shopper
# 0 1 2 3 4 5 6 7
TransitionMatrix = [ [0.00, 1.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00], # 0
[0.00, 0.00, 0.70, 0.00, 0.10, 0.00, 0.00, 0.20], # 1
[0.00, 0.00, 0.45, 0.15, 0.10, 0.00, 0.00, 0.30], # 2
[0.00, 0.00, 0.00, 0.00, 0.40, 0.00, 0.00, 0.60], # 3
[0.00, 0.00, 0.00, 0.00, 0.00, 0.30, 0.55, 0.15], # 4
[0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00], # 5
[0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00], # 6
[0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00] ] # 7

class RandomPath:

def RowSum(self, Vector):
rowSum = 0.0
for i in range(len(Vector)):
rowSum += Vector[i]
return rowSum

def NextPage(self, T, i):
rowSum = self.RowSum(T[i])
randomValue = G.Rnd.uniform(0, rowSum)

sumT = 0.0

for j in range(len(T[i])):
sumT += T[i][j]
if randomValue < sumT:
break
return j

class ExecuteFrame(Process):

def __init__(self, frameNumber, resource, QMon, SMon, serviceDemand, nodeName, pageName):
Process.__init__(self)
self.frame = frameNumber
self.resource = resource
self.serviceDemand = serviceDemand
self.nodeName = nodeName
self.pageName = pageName
self.QMon = QMon
self.SMon = SMon

def execute(self):
StartUpTime = now()

yield request, self, self.resource
yield hold, self, self.serviceDemand

self.QMon.observe(len(self.resource.waitQ))
self.SMon.observe(self.serviceDemand)

yield release, self, self.resource

R = now() - StartUpTime

G.metrics.Add(self.pageName, self.frame, R)

class CallPage(Process):

def __init__(self, frameNumber, node, pageName):
Process.__init__(self)
self.frame = frameNumber
self.StartUpTime = 0.0
self.currentPage = node
self.pageName = pageName

def execute(self):

if self.currentPage != G.Exit:

print >> sys.stderr, "Working on Frame # ", self.frame, " @ ", now() , " for page ", self.pageName

self.StartUpTime = now()

if G.ServiceDemand[G.WS_CPU][self.currentPage] > 0.0:
wsCPU = ExecuteFrame(self.frame, \
G.Resources[G.WS][G.CPU], \
G.QMon[G.WS][G.CPU], \
G.SMon[G.WS][G.CPU], \
G.ServiceDemand[G.WS_CPU][self.currentPage]/G.numWS, \
"wsCPU", self.pageName)
activate(wsCPU, wsCPU.execute())

if G.ServiceDemand[G.WS_DISK][self.currentPage] > 0.0:
wsDISK = ExecuteFrame(self.frame, \
G.Resources[G.WS][G.DISK], \
G.QMon[G.WS][G.DISK], \
G.SMon[G.WS][G.DISK], \
G.ServiceDemand[G.WS_DISK][self.currentPage]/G.numWS, \
"wsDISK", self.pageName)
activate(wsDISK, wsDISK.execute())

if G.ServiceDemand[G.AS_CPU][self.currentPage] > 0.0:
asCPU = ExecuteFrame(self.frame, \
G.Resources[G.AS][G.CPU], \
G.QMon[G.AS][G.CPU], \
G.SMon[G.AS][G.CPU], \
G.ServiceDemand[G.AS_CPU][self.currentPage]/G.numAS, \
"asCPU", \
self.pageName)
activate(asCPU, asCPU.execute())

if G.ServiceDemand[G.AS_DISK][self.currentPage] > 0.0:
asDISK = ExecuteFrame(self.frame, \
G.Resources[G.AS][G.DISK], \
G.QMon[G.AS][G.DISK], \
G.SMon[G.AS][G.DISK], \
G.ServiceDemand[G.AS_DISK][self.currentPage]/G.numAS, \
"asDISK", \
self.pageName)
activate(asDISK, asDISK.execute())

if G.ServiceDemand[G.DS_CPU][self.currentPage] > 0.0:
dsCPU = ExecuteFrame(self.frame, \
G.Resources[G.DS][G.CPU], \
G.QMon[G.DS][G.CPU], \
G.SMon[G.DS][G.CPU], \
G.ServiceDemand[G.DS_CPU][self.currentPage]/G.numDS, \
"dsCPU", \
self.pageName)
activate(dsCPU, dsCPU.execute())

if G.ServiceDemand[G.DS_DISK][self.currentPage] > 0.0:
dsDISK = ExecuteFrame(self.frame, \
G.Resources[G.DS][G.DISK], \
G.QMon[G.DS][G.DISK], \
G.SMon[G.DS][G.DISK], \
G.ServiceDemand[G.DS_DISK][self.currentPage]/G.numDS, \
"dsDISK", \
self.pageName)
activate(dsDISK, dsDISK.execute())

G.HitCount[self.currentPage] += 1

yield hold, self, 0.00001

class Generator(Process):
def __init__(self, rate, maxT):
Process.__init__(self)
self.name = "Generator"
self.rate = rate
self.maxT = maxT
self.g = Random(11335577)
self.i = 0
self.currentPage = G.Home

def execute(self):
while (now() < self.maxT):
self.i+=1
p = CallPage(self.i,self.currentPage,G.PageNames[self.currentPage])
activate(p,p.execute())
yield hold,self,self.g.expovariate(self.rate)
randomPath = RandomPath()

if self.currentPage == G.Exit:
self.currentPage = G.Home
else:
self.currentPage = randomPath.NextPage(G.TransitionMatrix, self.currentPage)

def main():

Lambda = 4.026*float(sys.argv[1])
maxSimTime = float(sys.argv[2])

initialize()
g = Generator(Lambda, maxSimTime)
activate(g,g.execute())

simulate(until=maxSimTime)

print >> sys.stderr, "Simulated Seconds : ", maxSimTime

print >> sys.stderr, "Page Hits :"
for i in range(len(G.PageNames)):
print >> sys.stderr, "\t", G.PageNames[i], " = ", G.HitCount[i]

print >> sys.stderr, "Throughput : "
for i in range(len(G.PageNames)):
print >> sys.stderr, "\t", G.PageNames[i], " = ", G.HitCount[i]/maxSimTime

print >> sys.stderr, "Mean Response Times:"

for i in G.metrics.Keys():
print >> sys.stderr, "\t", i, " = ", G.metrics.Mean(i)

print >> sys.stderr, "Component Waiting Queues:"
print >> sys.stderr, "\tWeb Server CPU : ", G.QMon[G.WS][G.CPU].mean()
print >> sys.stderr, "\tWeb Server DISK : ", G.QMon[G.WS][G.DISK].mean()
print >> sys.stderr, "\tApplication Server CPU : ", G.QMon[G.AS][G.CPU].mean()
print >> sys.stderr, "\tApplication Server DISK : ", G.QMon[G.AS][G.DISK].mean()
print >> sys.stderr, "\tDatabase Server CPU : ", G.QMon[G.DS][G.CPU].mean()
print >> sys.stderr, "\tDatabase Server DISK : ", G.QMon[G.DS][G.DISK].mean()

print >> sys.stderr, "Component Utilization:"
print >> sys.stderr, "\tWeb Server CPU : ", ((G.SMon[G.WS][G.CPU].mean()*len(G.QMon[G.WS][G.CPU]))/maxSimTime)*100
print >> sys.stderr, "\tWeb Server DISK : ", ((G.SMon[G.WS][G.DISK].mean()*len(G.QMon[G.WS][G.DISK]))/maxSimTime)*100
print >> sys.stderr, "\tApplication Server CPU : ", ((G.SMon[G.AS][G.CPU].mean()*len(G.QMon[G.AS][G.CPU]))/maxSimTime)*100
print >> sys.stderr, "\tApplication Server DISK : ", ((G.SMon[G.AS][G.DISK].mean()*len(G.QMon[G.AS][G.DISK]))/maxSimTime)*100
print >> sys.stderr, "\tDatabase Server CPU : ", ((G.SMon[G.DS][G.CPU].mean()*len(G.QMon[G.DS][G.CPU]))/maxSimTime)*100
print >> sys.stderr, "\tDatabase Server DISK : ", ((G.SMon[G.DS][G.DISK].mean()*len(G.QMon[G.DS][G.DISK]))/maxSimTime)*100

print >> sys.stderr, "Total Component Hits :"
print >> sys.stderr, "\tWeb Server CPU : ", len(G.QMon[G.WS][G.CPU])
print >> sys.stderr, "\tWeb Server DISK : ", len(G.QMon[G.WS][G.DISK])
print >> sys.stderr, "\tApplication Server CPU : ", len(G.QMon[G.AS][G.CPU])
print >> sys.stderr, "\tApplication Server DISK : ", len(G.QMon[G.AS][G.DISK])
print >> sys.stderr, "\tDatabase Server CPU : ", len(G.QMon[G.DS][G.CPU])
print >> sys.stderr, "\tDatabase Server DISK : ", len(G.QMon[G.DS][G.DISK])

print >> sys.stderr, "Total Component Thoughput :"
print >> sys.stderr, "\tWeb Server CPU : ", len(G.QMon[G.WS][G.CPU])/maxSimTime
print >> sys.stderr, "\tWeb Server DISK : ", len(G.QMon[G.WS][G.DISK])/maxSimTime
print >> sys.stderr, "\tApplication Server CPU : ", len(G.QMon[G.AS][G.CPU])/maxSimTime
print >> sys.stderr, "\tApplication Server DISK : ", len(G.QMon[G.AS][G.DISK])/maxSimTime
print >> sys.stderr, "\tDatabase Server CPU : ", len(G.QMon[G.DS][G.CPU])/maxSimTime
print >> sys.stderr, "\tDatabase Server DISK : ", len(G.QMon[G.DS][G.DISK])/maxSimTime

print >> sys.stderr, "Mean Component Svc Demand :"
print >> sys.stderr, "\tWeb Server CPU : ", G.SMon[G.WS][G.CPU].mean()
print >> sys.stderr, "\tWeb Server DISK : ", G.SMon[G.WS][G.DISK].mean()
print >> sys.stderr, "\tApplication Server CPU : ", G.SMon[G.AS][G.CPU].mean()
print >> sys.stderr, "\tApplication Server DISK : ", G.SMon[G.AS][G.DISK].mean()
print >> sys.stderr, "\tDatabase Server CPU : ", G.SMon[G.DS][G.CPU].mean()
print >> sys.stderr, "\tDatabase Server DISK : ", G.SMon[G.DS][G.DISK].mean()

print G.HitCount[G.Home]/maxSimTime, ",", G.metrics.Mean("Home"), ",", G.metrics.Mean("View"), ",", G.metrics.Mean("Search"), ",", G.metrics.Mean("Login"), ",", G.metrics.Mean("Create"), ",", G.metrics.Mean("Bid")

if __name__ == '__main__':
main()

No comments: