add ostep homeworks
This commit is contained in:
719
related_info/ostep/ostep15-disk/disk-precise.py
Executable file
719
related_info/ostep/ostep15-disk/disk-precise.py
Executable file
@@ -0,0 +1,719 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
from Tkinter import *
|
||||
from types import *
|
||||
import math, random, time, sys, os
|
||||
from optparse import OptionParser
|
||||
from decimal import *
|
||||
|
||||
MAXTRACKS = 1000
|
||||
|
||||
# states that a request/disk go through
|
||||
STATE_NULL = 0
|
||||
STATE_SEEK = 1
|
||||
STATE_ROTATE = 2
|
||||
STATE_XFER = 3
|
||||
STATE_DONE = 4
|
||||
|
||||
#
|
||||
# TODO
|
||||
# XXX transfer time
|
||||
# XXX satf
|
||||
# XXX skew
|
||||
# XXX scheduling window
|
||||
# XXX sstf
|
||||
# XXX specify requests vs. random requests in range
|
||||
# XXX add new requests as old ones complete (starvation)
|
||||
# XXX run in non-graphical mode
|
||||
# XXX better graphical display (show key, long lists of requests, more timings on screen)
|
||||
# XXX be able to do "pure" sequential
|
||||
# XXX add more blocks around outer tracks (zoning)
|
||||
# XXX simple flag to make scheduling window a fairness window (-F)
|
||||
# new algs to scan and c-scan the disk?
|
||||
#
|
||||
|
||||
class Disk:
|
||||
def __init__(self, addr, addrDesc, lateAddr, lateAddrDesc,
|
||||
policy, seekSpeed, rotateSpeed, skew, window, compute,
|
||||
graphics, zoning):
|
||||
self.addr = addr
|
||||
self.addrDesc = addrDesc
|
||||
self.lateAddr = lateAddr
|
||||
self.lateAddrDesc = lateAddrDesc
|
||||
self.policy = policy
|
||||
self.seekSpeed = Decimal(seekSpeed)
|
||||
self.rotateSpeed = Decimal(rotateSpeed)
|
||||
self.skew = skew
|
||||
self.window = window
|
||||
self.compute = compute
|
||||
self.graphics = graphics
|
||||
self.zoning = zoning
|
||||
|
||||
# figure out zones first, to figure out the max possible request
|
||||
self.InitBlockLayout()
|
||||
|
||||
# figure out requests
|
||||
random.seed(options.seed)
|
||||
self.requests = self.MakeRequests(self.addr, self.addrDesc)
|
||||
self.lateRequests = self.MakeRequests(self.lateAddr, self.lateAddrDesc)
|
||||
|
||||
# graphical startup
|
||||
self.width = 500
|
||||
if self.graphics:
|
||||
self.root = Tk()
|
||||
tmpLen = len(self.requests)
|
||||
if len(self.lateRequests) > 0:
|
||||
tmpLen += len(self.lateRequests)
|
||||
self.canvas = Canvas(self.root, width=410, height=460 + ((tmpLen / 20) * 20))
|
||||
self.canvas.pack()
|
||||
|
||||
# fairness stuff
|
||||
if self.policy == 'BSATF' and self.window != -1:
|
||||
self.fairWindow = self.window
|
||||
else:
|
||||
self.fairWindow = -1
|
||||
|
||||
print 'REQUESTS', self.requests
|
||||
print ''
|
||||
|
||||
# for late requests
|
||||
self.lateCount = 0
|
||||
if len(self.lateRequests) > 0:
|
||||
print 'LATE REQUESTS', self.lateRequests
|
||||
print ''
|
||||
|
||||
if self.compute == False:
|
||||
print ''
|
||||
print 'For the requests above, compute the seek, rotate, and transfer times.'
|
||||
print 'Use -c or the graphical mode (-G) to see the answers.'
|
||||
print ''
|
||||
|
||||
# BINDINGS
|
||||
if self.graphics:
|
||||
self.root.bind('s', self.Start)
|
||||
self.root.bind('p', self.Pause)
|
||||
self.root.bind('q', self.Exit)
|
||||
|
||||
# TRACK INFO
|
||||
self.tracks = {}
|
||||
self.trackWidth = 40
|
||||
self.tracks[0] = 140
|
||||
self.tracks[1] = self.tracks[0] - self.trackWidth
|
||||
self.tracks[2] = self.tracks[1] - self.trackWidth
|
||||
|
||||
if (self.seekSpeed > 1 and self.trackWidth % self.seekSpeed != 0):
|
||||
print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth)
|
||||
sys.exit(1)
|
||||
if self.seekSpeed < 1:
|
||||
x = (self.trackWidth / self.seekSpeed)
|
||||
y = int(float(self.trackWidth) / float(self.seekSpeed))
|
||||
if float(x) != float(y):
|
||||
print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth)
|
||||
sys.exit(1)
|
||||
|
||||
# DISK SURFACE
|
||||
self.cx = self.width/2.0
|
||||
self.cy = self.width/2.0
|
||||
if self.graphics:
|
||||
self.canvas.create_rectangle(self.cx-175, 30, self.cx - 20, 80, fill='gray', outline='black')
|
||||
self.platterSize = 320
|
||||
ps2 = self.platterSize / 2.0
|
||||
if self.graphics:
|
||||
self.canvas.create_oval(self.cx-ps2, self.cy-ps2, self.cx+ps2, self.cy + ps2, fill='darkgray', outline='black')
|
||||
for i in range(len(self.tracks)):
|
||||
t = self.tracks[i] - (self.trackWidth / 2.0)
|
||||
if self.graphics:
|
||||
self.canvas.create_oval(self.cx - t, self.cy - t, self.cx + t, self.cy + t, fill='', outline='black', width=1.0)
|
||||
|
||||
# SPINDLE
|
||||
self.spindleX = self.cx
|
||||
self.spindleY = self.cy
|
||||
if self.graphics:
|
||||
self.spindleID = self.canvas.create_oval(self.spindleX-3, self.spindleY-3, self.spindleX+3, self.spindleY+3, fill='orange', outline='black')
|
||||
|
||||
# DISK ARM
|
||||
self.armTrack = 0
|
||||
self.armSpeedBase = float(seekSpeed)
|
||||
self.armSpeed = float(seekSpeed)
|
||||
|
||||
distFromSpindle = self.tracks[self.armTrack]
|
||||
self.armWidth = 20
|
||||
self.headWidth = 10
|
||||
|
||||
self.armX = self.spindleX - (distFromSpindle * math.cos(math.radians(0)))
|
||||
self.armX1 = self.armX - self.armWidth
|
||||
self.armX2 = self.armX + self.armWidth
|
||||
self.armY1 = 50.0
|
||||
self.armY2 = self.width / 2.0
|
||||
|
||||
self.headX1 = self.armX - self.headWidth
|
||||
self.headX2 = self.armX + self.headWidth
|
||||
self.headY1 = (self.width/2.0) - self.headWidth
|
||||
self.headY2 = (self.width/2.0) + self.headWidth
|
||||
|
||||
if self.graphics:
|
||||
self.armID = self.canvas.create_rectangle(self.armX1, self.armY1, self.armX2, self.armY2, fill='gray', outline='black')
|
||||
self.headID = self.canvas.create_rectangle(self.headX1, self.headY1, self.headX2, self.headY2, fill='gray', outline='black')
|
||||
|
||||
self.targetSize = 10.0
|
||||
if self.graphics:
|
||||
sz = self.targetSize
|
||||
self.targetID = self.canvas.create_oval(self.armX1-sz, self.armY1-sz, self.armX1+sz, self.armY1+sz, fill='orange', outline='')
|
||||
|
||||
# IO QUEUE
|
||||
self.queueX = 20
|
||||
self.queueY = 450
|
||||
|
||||
self.requestCount = 0
|
||||
self.requestQueue = []
|
||||
self.requestState = []
|
||||
self.queueBoxSize = 20
|
||||
self.queueBoxID = {}
|
||||
self.queueTxtID = {}
|
||||
|
||||
# draw each box
|
||||
for index in range(len(self.requests)):
|
||||
self.AddQueueEntry(int(self.requests[index]), index)
|
||||
if self.graphics:
|
||||
self.canvas.create_text(self.queueX - 5, self.queueY - 20, anchor='w', text='Queue:')
|
||||
|
||||
# scheduling window
|
||||
self.currWindow = self.window
|
||||
|
||||
# draw current limits of queue
|
||||
if self.graphics:
|
||||
self.windowID = -1
|
||||
self.DrawWindow()
|
||||
|
||||
# initial scheduling info
|
||||
self.currentIndex = -1
|
||||
self.currentBlock = -1
|
||||
|
||||
# initial state of disk (vs seeking, rotating, transferring)
|
||||
self.state = STATE_NULL
|
||||
|
||||
# DRAW BLOCKS on the TRACKS
|
||||
for bid in range(len(self.blockInfoList)):
|
||||
(track, angle, name) = self.blockInfoList[bid]
|
||||
if self.graphics:
|
||||
distFromSpindle = self.tracks[track]
|
||||
xc = self.spindleX + (distFromSpindle * math.cos(math.radians(angle)))
|
||||
yc = self.spindleY + (distFromSpindle * math.sin(math.radians(angle)))
|
||||
cid = self.canvas.create_text(xc, yc, text=name, anchor='center')
|
||||
else:
|
||||
cid = -1
|
||||
self.blockInfoList[bid] = (track, angle, name, cid)
|
||||
|
||||
# angle of rotation
|
||||
self.angle = Decimal(0.0)
|
||||
|
||||
# TIME INFO
|
||||
if self.graphics:
|
||||
self.timeID = self.canvas.create_text(10, 10, text='Time: 0.00', anchor='w')
|
||||
self.canvas.create_rectangle(95,0,200,18, fill='orange', outline='orange')
|
||||
self.seekID = self.canvas.create_text(100, 10, text='Seek: 0.00', anchor='w')
|
||||
self.canvas.create_rectangle(195,0,300,18, fill='lightblue', outline='lightblue')
|
||||
self.rotID = self.canvas.create_text(200, 10, text='Rotate: 0.00', anchor='w')
|
||||
self.canvas.create_rectangle(295,0,400,18, fill='green', outline='green')
|
||||
self.xferID = self.canvas.create_text(300, 10, text='Transfer: 0.00', anchor='w')
|
||||
self.canvas.create_text(320, 40, text='"s" to start', anchor='w')
|
||||
self.canvas.create_text(320, 60, text='"p" to pause', anchor='w')
|
||||
self.canvas.create_text(320, 80, text='"q" to quit', anchor='w')
|
||||
self.timer = 0
|
||||
|
||||
# STATS
|
||||
self.seekTotal = 0.0
|
||||
self.rotTotal = 0.0
|
||||
self.xferTotal = 0.0
|
||||
|
||||
# set up animation loop
|
||||
if self.graphics:
|
||||
self.doAnimate = True
|
||||
else:
|
||||
self.doAnimate = False
|
||||
self.isDone = False
|
||||
|
||||
# call this to start simulation
|
||||
def Go(self):
|
||||
if options.graphics:
|
||||
self.root.mainloop()
|
||||
else:
|
||||
self.GetNextIO()
|
||||
while self.isDone == False:
|
||||
self.Animate()
|
||||
|
||||
# crappy error message
|
||||
def PrintAddrDescMessage(self, value):
|
||||
print 'Bad address description (%s)' % value
|
||||
print 'The address description must be a comma-separated list of length three, without spaces.'
|
||||
print 'For example, "10,100,0" would indicate that 10 addresses should be generated, with'
|
||||
print '100 as the maximum value, and 0 as the minumum. A max of -1 means just use the highest'
|
||||
print 'possible value as the max address to generate.'
|
||||
sys.exit(1)
|
||||
|
||||
#
|
||||
# ZONES AND BLOCK LAYOUT
|
||||
#
|
||||
def InitBlockLayout(self):
|
||||
self.blockInfoList = []
|
||||
self.blockToTrackMap = {}
|
||||
self.blockToAngleMap = {}
|
||||
self.tracksBeginEnd = {}
|
||||
self.blockAngleOffset = []
|
||||
|
||||
zones = self.zoning.split(',')
|
||||
assert(len(zones) == 3)
|
||||
for i in range(len(zones)):
|
||||
self.blockAngleOffset.append(int(zones[i]) / 2)
|
||||
|
||||
track = 0 # outer track
|
||||
angleOffset = 2 * self.blockAngleOffset[track]
|
||||
for angle in range(0, 360, angleOffset):
|
||||
block = angle / angleOffset
|
||||
self.blockToTrackMap[block] = track
|
||||
self.blockToAngleMap[block] = angle
|
||||
self.blockInfoList.append((track, angle, block))
|
||||
self.tracksBeginEnd[track] = (0, block)
|
||||
pblock = block + 1
|
||||
|
||||
track = 1 # middle track
|
||||
skew = self.skew
|
||||
angleOffset = 2 * self.blockAngleOffset[track]
|
||||
for angle in range(0, 360, angleOffset):
|
||||
block = (angle / angleOffset) + pblock
|
||||
self.blockToTrackMap[block] = track
|
||||
self.blockToAngleMap[block] = angle + (angleOffset * skew)
|
||||
self.blockInfoList.append((track, angle + (angleOffset * skew), block))
|
||||
self.tracksBeginEnd[track] = (pblock, block)
|
||||
pblock = block + 1
|
||||
|
||||
track = 2 # inner track
|
||||
skew = 2 * self.skew
|
||||
angleOffset = 2 * self.blockAngleOffset[track]
|
||||
for angle in range(0, 360, angleOffset):
|
||||
block = (angle / angleOffset) + pblock
|
||||
self.blockToTrackMap[block] = track
|
||||
self.blockToAngleMap[block] = angle + (angleOffset * skew)
|
||||
self.blockInfoList.append((track, angle + (angleOffset * skew), block))
|
||||
self.tracksBeginEnd[track] = (pblock, block)
|
||||
self.maxBlock = pblock
|
||||
# print 'MAX BLOCK:', self.maxBlock
|
||||
|
||||
# adjust angle to starting position relative
|
||||
for i in self.blockToAngleMap:
|
||||
self.blockToAngleMap[i] = (self.blockToAngleMap[i] + 180) % 360
|
||||
|
||||
# print 'btoa map', self.blockToAngleMap
|
||||
# print 'btot map', self.blockToTrackMap
|
||||
# print 'bao', self.blockAngleOffset
|
||||
|
||||
def MakeRequests(self, addr, addrDesc):
|
||||
(numRequests, maxRequest, minRequest) = (0, 0, 0)
|
||||
if addr == '-1':
|
||||
# first extract values from descriptor
|
||||
desc = addrDesc.split(',')
|
||||
if len(desc) != 3:
|
||||
self.PrintAddrDescMessage(addrDesc)
|
||||
(numRequests, maxRequest, minRequest) = (int(desc[0]), int(desc[1]), int(desc[2]))
|
||||
if maxRequest == -1:
|
||||
maxRequest = self.maxBlock
|
||||
# now make list
|
||||
tmpList = []
|
||||
for i in range(numRequests):
|
||||
tmpList.append(int(random.random() * maxRequest) + minRequest)
|
||||
return tmpList
|
||||
else:
|
||||
return addr.split(',')
|
||||
|
||||
#
|
||||
# BUTTONS
|
||||
#
|
||||
def Start(self, event):
|
||||
self.GetNextIO()
|
||||
self.doAnimate = True
|
||||
self.Animate()
|
||||
|
||||
def Pause(self, event):
|
||||
if self.doAnimate == False:
|
||||
self.doAnimate = True
|
||||
else:
|
||||
self.doAnimate = False
|
||||
|
||||
def Exit(self, event):
|
||||
sys.exit(0)
|
||||
|
||||
#
|
||||
# CORE SIMULATION and ANIMATION
|
||||
#
|
||||
def UpdateTime(self):
|
||||
if self.graphics:
|
||||
self.canvas.itemconfig(self.timeID, text='Time: ' + str(self.timer))
|
||||
self.canvas.itemconfig(self.seekID, text='Seek: ' + str(self.seekTotal))
|
||||
self.canvas.itemconfig(self.rotID, text='Rotate: ' + str(self.rotTotal))
|
||||
self.canvas.itemconfig(self.xferID, text='Transfer: ' + str(self.xferTotal))
|
||||
|
||||
def AddRequest(self, block):
|
||||
self.AddQueueEntry(block, len(self.requestQueue))
|
||||
|
||||
def QueueMap(self, index):
|
||||
numPerRow = 400 / self.queueBoxSize
|
||||
return (index % numPerRow, index / numPerRow)
|
||||
|
||||
def DrawWindow(self):
|
||||
if self.window == -1:
|
||||
return
|
||||
(col, row) = self.QueueMap(self.currWindow)
|
||||
if col == 0:
|
||||
(col, row) = (20, row - 1)
|
||||
if self.windowID != -1:
|
||||
self.canvas.delete(self.windowID)
|
||||
self.windowID = self.canvas.create_line(self.queueX + (col * 20) - 10, self.queueY - 13 + (row * 20),
|
||||
self.queueX + (col * 20) - 10, self.queueY + 13 + (row * 20), width=2)
|
||||
|
||||
def AddQueueEntry(self, block, index):
|
||||
self.requestQueue.append((block, index))
|
||||
self.requestState.append(STATE_NULL)
|
||||
if self.graphics:
|
||||
(col, row) = self.QueueMap(index)
|
||||
sizeHalf = self.queueBoxSize / 2.0
|
||||
(cx, cy) = (self.queueX + (col * self.queueBoxSize), self.queueY + (row * self.queueBoxSize))
|
||||
self.queueBoxID[index] = self.canvas.create_rectangle(cx - sizeHalf, cy - sizeHalf, cx + sizeHalf, cy + sizeHalf, fill='white')
|
||||
self.queueTxtID[index] = self.canvas.create_text(cx, cy, anchor='center', text=str(block))
|
||||
|
||||
def SwitchColors(self, c):
|
||||
if self.graphics:
|
||||
self.canvas.itemconfig(self.queueBoxID[self.currentIndex], fill=c)
|
||||
self.canvas.itemconfig(self.targetID, fill=c)
|
||||
|
||||
def SwitchState(self, newState):
|
||||
self.state = newState
|
||||
self.requestState[self.currentIndex] = newState
|
||||
|
||||
def RadiallyCloseTo(self, a1, a2):
|
||||
if a1 > a2:
|
||||
v = a1 - a2
|
||||
else:
|
||||
v = a2 - a1
|
||||
if v < self.rotateSpeed:
|
||||
return True
|
||||
return False
|
||||
|
||||
def DoneWithTransfer(self):
|
||||
angleOffset = self.blockAngleOffset[self.armTrack]
|
||||
# if int(self.angle) == (self.blockToAngleMap[self.currentBlock] + angleOffset) % 360:
|
||||
if self.RadiallyCloseTo(self.angle, Decimal((self.blockToAngleMap[self.currentBlock] + angleOffset) % 360)):
|
||||
# print 'END TRANSFER', self.angle, self.timer
|
||||
self.SwitchState(STATE_DONE)
|
||||
self.requestCount += 1
|
||||
return True
|
||||
return False
|
||||
|
||||
def DoneWithRotation(self):
|
||||
angleOffset = self.blockAngleOffset[self.armTrack]
|
||||
# XXX there is a weird bug in here
|
||||
# print self.timer, 'ROTATE:: ', self.currentBlock, 'currangle: ', self.angle, ' - mapangle: ', self.blockToAngleMap[self.currentBlock]
|
||||
# print ' angleOffset ', angleOffset
|
||||
# print ' blockMap ', (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360
|
||||
# print ' self.angle ', self.angle, int(self.angle)
|
||||
# if int(self.angle) == (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360:
|
||||
if self.RadiallyCloseTo(self.angle, Decimal((self.blockToAngleMap[self.currentBlock] - angleOffset) % 360)):
|
||||
self.SwitchState(STATE_XFER)
|
||||
# print ' --> DONE WITH ROTATION!', self.timer
|
||||
return True
|
||||
return False
|
||||
|
||||
def PlanSeek(self, track):
|
||||
self.seekBegin = self.timer
|
||||
self.SwitchColors('orange')
|
||||
self.SwitchState(STATE_SEEK)
|
||||
if track == self.armTrack:
|
||||
self.rotBegin = self.timer
|
||||
self.SwitchColors('lightblue')
|
||||
self.SwitchState(STATE_ROTATE)
|
||||
return
|
||||
self.armTarget = track
|
||||
self.armTargetX1 = self.spindleX - self.tracks[track] - (self.trackWidth / 2.0)
|
||||
if track >= self.armTrack:
|
||||
self.armSpeed = self.armSpeedBase
|
||||
else:
|
||||
self.armSpeed = - self.armSpeedBase
|
||||
|
||||
def DoneWithSeek(self):
|
||||
# move the disk arm
|
||||
self.armX1 += self.armSpeed
|
||||
self.armX2 += self.armSpeed
|
||||
self.headX1 += self.armSpeed
|
||||
self.headX2 += self.armSpeed
|
||||
# update it on screen
|
||||
if self.graphics:
|
||||
self.canvas.coords(self.armID, self.armX1, self.armY1, self.armX2, self.armY2)
|
||||
self.canvas.coords(self.headID, self.headX1, self.headY1, self.headX2, self.headY2)
|
||||
# check if done
|
||||
if (self.armSpeed > 0.0 and self.armX1 >= self.armTargetX1) or (self.armSpeed < 0.0 and self.armX1 <= self.armTargetX1):
|
||||
self.armTrack = self.armTarget
|
||||
return True
|
||||
return False
|
||||
|
||||
def DoSATF(self, rList):
|
||||
minBlock = -1
|
||||
minIndex = -1
|
||||
minEst = -1
|
||||
|
||||
# print '**** DoSATF ****', rList
|
||||
for (block, index) in rList:
|
||||
if self.requestState[index] == STATE_DONE:
|
||||
continue
|
||||
track = self.blockToTrackMap[block]
|
||||
angle = self.blockToAngleMap[block]
|
||||
# print 'track', track, 'angle', angle
|
||||
|
||||
# estimate seek time
|
||||
dist = int(math.fabs(self.armTrack - track))
|
||||
seekEst = Decimal(self.trackWidth / self.armSpeedBase) * dist
|
||||
|
||||
# estimate rotate time
|
||||
angleOffset = self.blockAngleOffset[track]
|
||||
angleAtArrival = (Decimal(self.angle) + (seekEst * self.rotateSpeed))
|
||||
while angleAtArrival > 360.0:
|
||||
angleAtArrival -= 360.0
|
||||
rotDist = Decimal((angle - angleOffset) - angleAtArrival)
|
||||
while rotDist > 360.0:
|
||||
rotDist -= Decimal(360.0)
|
||||
while rotDist < 0.0:
|
||||
rotDist += Decimal(360.0)
|
||||
rotEst = rotDist / self.rotateSpeed
|
||||
|
||||
# finally, transfer
|
||||
xferEst = (Decimal(angleOffset) * Decimal(2.0)) / self.rotateSpeed
|
||||
|
||||
totalEst = seekEst + rotEst + xferEst
|
||||
|
||||
# should probably pick one on same track in case of a TIE
|
||||
if minEst == -1 or totalEst < minEst:
|
||||
minEst = totalEst
|
||||
minBlock = block
|
||||
minIndex = index
|
||||
# END loop
|
||||
|
||||
# when done
|
||||
self.totalEst = minEst
|
||||
assert(minBlock != -1)
|
||||
assert(minIndex != -1)
|
||||
return (minBlock, minIndex)
|
||||
|
||||
#
|
||||
# actually doesn't quite do SSTF
|
||||
# just finds all the blocks on the nearest track
|
||||
# (whatever that may be) and returns it as a list
|
||||
#
|
||||
def DoSSTF(self, rList):
|
||||
minDist = MAXTRACKS
|
||||
minBlock = -1
|
||||
trackList = [] # all the blocks on a track
|
||||
|
||||
for (block, index) in rList:
|
||||
if self.requestState[index] == STATE_DONE:
|
||||
continue
|
||||
track = self.blockToTrackMap[block]
|
||||
dist = int(math.fabs(self.armTrack - track))
|
||||
if dist < minDist:
|
||||
trackList = []
|
||||
trackList.append((block, index))
|
||||
minDist = dist
|
||||
elif dist == minDist:
|
||||
trackList.append((block, index))
|
||||
assert(trackList != [])
|
||||
return trackList
|
||||
|
||||
def UpdateWindow(self):
|
||||
if self.fairWindow == -1 and self.currWindow > 0 and self.currWindow < len(self.requestQueue):
|
||||
self.currWindow += 1
|
||||
if self.graphics:
|
||||
self.DrawWindow()
|
||||
|
||||
def GetWindow(self):
|
||||
if self.currWindow <= -1:
|
||||
return len(self.requestQueue)
|
||||
else:
|
||||
if self.fairWindow != -1:
|
||||
if self.requestCount > 0 and (self.requestCount % self.fairWindow == 0):
|
||||
self.currWindow = self.currWindow + self.fairWindow
|
||||
if self.currWindow > len(self.requestQueue):
|
||||
self.currWindow = len(self.requestQueue)
|
||||
if self.graphics:
|
||||
self.DrawWindow()
|
||||
return self.currWindow
|
||||
else:
|
||||
return self.currWindow
|
||||
|
||||
def GetNextIO(self):
|
||||
# check if done: if so, print stats and end animation
|
||||
if self.requestCount == len(self.requestQueue):
|
||||
self.UpdateTime()
|
||||
self.PrintStats()
|
||||
self.doAnimate = False
|
||||
self.isDone = True
|
||||
return
|
||||
|
||||
# do policy: should set currentBlock,
|
||||
if self.policy == 'FIFO':
|
||||
(self.currentBlock, self.currentIndex) = self.requestQueue[self.requestCount]
|
||||
self.DoSATF(self.requestQueue[self.requestCount:self.requestCount+1])
|
||||
elif self.policy == 'SATF' or self.policy == 'BSATF':
|
||||
(self.currentBlock, self.currentIndex) = self.DoSATF(self.requestQueue[0:self.GetWindow()])
|
||||
elif self.policy == 'SSTF':
|
||||
# first, find all the blocks on a given track (given window constraints)
|
||||
trackList = self.DoSSTF(self.requestQueue[0:self.GetWindow()])
|
||||
# then, do SATF on those blocks (otherwise, will not do them in obvious order)
|
||||
(self.currentBlock, self.currentIndex) = self.DoSATF(trackList)
|
||||
else:
|
||||
print 'policy (%s) not implemented' % self.policy
|
||||
sys.exit(1)
|
||||
|
||||
# once best block is decided, go ahead and do the seek
|
||||
self.PlanSeek(self.blockToTrackMap[self.currentBlock])
|
||||
|
||||
# add another block?
|
||||
if len(self.lateRequests) > 0 and self.lateCount < len(self.lateRequests):
|
||||
self.AddRequest(self.lateRequests[self.lateCount])
|
||||
self.lateCount += 1
|
||||
|
||||
def Animate(self):
|
||||
if self.graphics == True and self.doAnimate == False:
|
||||
self.root.after(20, self.Animate)
|
||||
return
|
||||
|
||||
# timer
|
||||
self.timer += 1
|
||||
self.UpdateTime()
|
||||
|
||||
# see which blocks are rotating on the disk
|
||||
# print 'SELF ANGLE', self.angle
|
||||
self.angle = Decimal(self.angle + self.rotateSpeed)
|
||||
if self.angle >= 360.0:
|
||||
self.angle = Decimal(0.0)
|
||||
|
||||
# move the blocks
|
||||
if self.graphics:
|
||||
for (track, angle, name, cid) in self.blockInfoList:
|
||||
distFromSpindle = self.tracks[track]
|
||||
na = angle - self.angle
|
||||
xc = self.spindleX + (distFromSpindle * math.cos(math.radians(na)))
|
||||
yc = self.spindleY + (distFromSpindle * math.sin(math.radians(na)))
|
||||
if self.graphics:
|
||||
self.canvas.coords(cid, xc, yc)
|
||||
if self.currentBlock == name:
|
||||
sz = self.targetSize
|
||||
self.canvas.coords(self.targetID, xc-sz, yc-sz, xc+sz, yc+sz)
|
||||
|
||||
# move the arm OR wait for a rotational delay
|
||||
if self.state == STATE_SEEK:
|
||||
if self.DoneWithSeek():
|
||||
self.rotBegin = self.timer
|
||||
self.SwitchState(STATE_ROTATE)
|
||||
self.SwitchColors('lightblue')
|
||||
if self.state == STATE_ROTATE:
|
||||
# check for read (disk arm must be settled)
|
||||
if self.DoneWithRotation():
|
||||
self.xferBegin = self.timer
|
||||
self.SwitchState(STATE_XFER)
|
||||
self.SwitchColors('green')
|
||||
if self.state == STATE_XFER:
|
||||
if self.DoneWithTransfer():
|
||||
self.DoRequestStats()
|
||||
self.SwitchState(STATE_DONE)
|
||||
self.SwitchColors('red')
|
||||
self.UpdateWindow()
|
||||
currentBlock = self.currentBlock
|
||||
self.GetNextIO()
|
||||
nextBlock = self.currentBlock
|
||||
if self.blockToTrackMap[currentBlock] == self.blockToTrackMap[nextBlock]:
|
||||
if (currentBlock == self.tracksBeginEnd[self.armTrack][1] and nextBlock == self.tracksBeginEnd[self.armTrack][0]) or (currentBlock + 1 == nextBlock):
|
||||
# need a special case here: to handle when we stay in transfer mode
|
||||
(self.rotBegin, self.seekBegin, self.xferBegin) = (self.timer, self.timer, self.timer)
|
||||
self.SwitchState(STATE_XFER)
|
||||
self.SwitchColors('green')
|
||||
|
||||
|
||||
|
||||
# make sure to keep the animation going!
|
||||
if self.graphics:
|
||||
self.root.after(20, self.Animate)
|
||||
|
||||
def DoRequestStats(self):
|
||||
seekTime = self.rotBegin - self.seekBegin
|
||||
rotTime = self.xferBegin - self.rotBegin
|
||||
xferTime = self.timer - self.xferBegin
|
||||
totalTime = self.timer - self.seekBegin
|
||||
|
||||
if self.compute == True:
|
||||
print 'Block: %3d Seek:%3d Rotate:%3d Transfer:%3d Total:%4d' % (self.currentBlock, seekTime, rotTime, xferTime, totalTime)
|
||||
|
||||
# if int(totalTime) != int(self.totalEst):
|
||||
# print 'INTERNAL ERROR: estimate was', self.totalEst, 'whereas actual time to access block was', totalTime
|
||||
# print 'Please report this bug and as much information as possible so as to make it easy to recreate. Thanks!'
|
||||
|
||||
# update stats
|
||||
self.seekTotal += seekTime
|
||||
self.rotTotal += rotTime
|
||||
self.xferTotal += xferTime
|
||||
|
||||
|
||||
|
||||
def PrintStats(self):
|
||||
if self.compute == True:
|
||||
print '\nTOTALS Seek:%3d Rotate:%3d Transfer:%3d Total:%4d\n' % (self.seekTotal, self.rotTotal, self.xferTotal, self.timer)
|
||||
|
||||
# END: class Disk
|
||||
|
||||
|
||||
|
||||
#
|
||||
# MAIN SIMULATOR
|
||||
#
|
||||
parser = OptionParser()
|
||||
parser.add_option('-s', '--seed', default='0', help='Random seed', action='store', type='int', dest='seed')
|
||||
parser.add_option('-a', '--addr', default='-1', help='Request list (comma-separated) [-1 -> use addrDesc]', action='store', type='string', dest='addr')
|
||||
parser.add_option('-A', '--addrDesc', default='5,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='addrDesc')
|
||||
parser.add_option('-S', '--seekSpeed', default='1', help='Speed of seek', action='store', type='string', dest='seekSpeed')
|
||||
parser.add_option('-R', '--rotSpeed', default='1', help='Speed of rotation', action='store', type='string', dest='rotateSpeed')
|
||||
parser.add_option('-p', '--policy', default='FIFO', help='Scheduling policy (FIFO, SSTF, SATF, BSATF)', action='store', type='string', dest='policy')
|
||||
parser.add_option('-w', '--schedWindow', default=-1, help='Size of scheduling window (-1 -> all)', action='store', type='int', dest='window')
|
||||
parser.add_option('-o', '--skewOffset', default=0, help='Amount of skew (in blocks)', action='store', type='int', dest='skew')
|
||||
parser.add_option('-z', '--zoning', default='30,30,30', help='Angles between blocks on outer,middle,inner tracks', action='store', type='string', dest='zoning')
|
||||
parser.add_option('-G', '--graphics', default=False, help='Turn on graphics', action='store_true', dest='graphics')
|
||||
parser.add_option('-l', '--lateAddr', default='-1', help='Late: request list (comma-separated) [-1 -> random]', action='store', type='string', dest='lateAddr')
|
||||
parser.add_option('-L', '--lateAddrDesc', default='0,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='lateAddrDesc')
|
||||
parser.add_option('-c', '--compute', default=False, help='Compute the answers', action='store_true', dest='compute')
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
print 'OPTIONS seed', options.seed
|
||||
print 'OPTIONS addr', options.addr
|
||||
print 'OPTIONS addrDesc', options.addrDesc
|
||||
print 'OPTIONS seekSpeed', options.seekSpeed
|
||||
print 'OPTIONS rotateSpeed', options.rotateSpeed
|
||||
print 'OPTIONS skew', options.skew
|
||||
print 'OPTIONS window', options.window
|
||||
print 'OPTIONS policy', options.policy
|
||||
print 'OPTIONS compute', options.compute
|
||||
print 'OPTIONS graphics', options.graphics
|
||||
print 'OPTIONS zoning', options.zoning
|
||||
print 'OPTIONS lateAddr', options.lateAddr
|
||||
print 'OPTIONS lateAddrDesc', options.lateAddrDesc
|
||||
print ''
|
||||
|
||||
if options.window == 0:
|
||||
print 'Scheduling window (%d) must be positive or -1 (which means a full window)' % options.window
|
||||
sys.exit(1)
|
||||
|
||||
if options.graphics and options.compute == False:
|
||||
print '\nWARNING: Setting compute flag to True, as graphics are on\n'
|
||||
options.compute = True
|
||||
|
||||
# set up simulator info
|
||||
d = Disk(addr=options.addr, addrDesc=options.addrDesc, lateAddr=options.lateAddr, lateAddrDesc=options.lateAddrDesc,
|
||||
policy=options.policy, seekSpeed=Decimal(options.seekSpeed), rotateSpeed=Decimal(options.rotateSpeed),
|
||||
skew=options.skew, window=options.window, compute=options.compute, graphics=options.graphics, zoning=options.zoning)
|
||||
|
||||
# run simulation
|
||||
d.Go()
|
||||
133
related_info/ostep/ostep15-disk/disk.md
Normal file
133
related_info/ostep/ostep15-disk/disk.md
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
This homework uses disk.py to familiarize you with how a modern hard
|
||||
drive works. It has a lot of different options, and unlike most of the other
|
||||
simulations, has a graphical animator to show you exactly what happens when
|
||||
the disk is in action.
|
||||
|
||||
[Note: there is also an experimental program, 'disk-precise.py', included
|
||||
in the download. This version of the simulator uses the python Decimal
|
||||
package for precise floating point computation, thus giving slightly
|
||||
better answers in some corner cases than 'disk.py'. However, it has
|
||||
not been very carefully tested, so use at your own caution.]
|
||||
|
||||
Let's do a simple example first. To run the simulator and compute some basic
|
||||
seek, rotation, and transfer times, you first have to give a list of requests
|
||||
to the simulator. This can either be done by specifying the exact requests, or
|
||||
by having the simulator generate some randomly.
|
||||
|
||||
We'll start by specifying a list of requests ourselves. Let's do a single
|
||||
request first:
|
||||
|
||||
prompt> disk.py -a 10
|
||||
|
||||
At this point you'll see:
|
||||
|
||||
...
|
||||
REQUESTS [br '10']
|
||||
|
||||
For the requests above, compute the seek, rotate, and transfer times.
|
||||
Use -c or the graphical mode (-G) to see the answers.
|
||||
|
||||
|
||||
To be able to compute the seek, rotation, and transfer times for this request,
|
||||
you'll have to know a little more information about the layout of sectors, the
|
||||
starting position of the disk head, and so forth. To see much of this
|
||||
information, run the simulator in graphical mode (-G):
|
||||
|
||||
prompt> disk.py -a 10 -G
|
||||
|
||||
|
||||
At this point, a window should appear with our simple disk on it.
|
||||
The disk head is positioned on the outside track, halfway through sector 6.
|
||||
As you can see, sector 10 (our example sector) is on the same track, about a
|
||||
third of the way around. The direction of rotation is counter-clockwise.
|
||||
To run the simulation, press the "s" key while the simulator window is
|
||||
highlighted.
|
||||
|
||||
When the simulation completes, you should be able to see that the disk spent
|
||||
105 time units in rotation and 30 in transfer in order to access sector 10,
|
||||
with no seek time. Press "q" to close the simulator window.
|
||||
|
||||
To calculate this (instead of just running the simulation), you would need to
|
||||
know a few details about the disk. First, the rotational speed is by default
|
||||
set to 1 degree per time unit. Thus, to make a complete revolution, it takes
|
||||
360 time units. Second, transfer begins and ends at the halfway point between
|
||||
sectors. Thus, to read sector 10, the transfer begins halfway between 9 and 10,
|
||||
and ends halfway between 10 and 11. Finally, in the default disk, there are
|
||||
12 sectors per track, meaning that each sector takes up 30 degrees of the
|
||||
rotational space. Thus, to read a sector, it takes 30 time units (given our
|
||||
default speed of rotation).
|
||||
|
||||
With this information in hand, you now should be able to compute the seek,
|
||||
rotation, and transfer times for accessing sector 10. Because the head starts
|
||||
on the same track as 10, there is no seek time. Because the disk rotates at
|
||||
1 degree / time unit, it takes 105 time units to get to the beginning of sector
|
||||
10, halfway between 9 and 10 (note that it is exactly 90 degrees to the middle
|
||||
of sector 9, and another 15 to the halfway point). Finally, to transfer the
|
||||
sector takes 30 time units.
|
||||
|
||||
Now let's do a slightly more complex example:
|
||||
|
||||
prompt> disk.py -a 10,11 -G
|
||||
|
||||
|
||||
In this case, we're transferring two sectors, 10 and 11. How long will it take?
|
||||
Try guessing before running the simulation!
|
||||
|
||||
As you probably guessed, this simulation takes just 30 time units longer, to
|
||||
transfer the next sector 11. Thus, the seek and rotate times remain the same,
|
||||
but the transfer time for the requests is doubled. You can in fact see these
|
||||
sums across the top of the simulator window; they also get printed out to the
|
||||
console as follows:
|
||||
|
||||
...
|
||||
Sector: 10 Seek: 0 Rotate:105 Transfer: 30 Total: 135
|
||||
Sector: 11 Seek: 0 Rotate: 0 Transfer: 30 Total: 30
|
||||
TOTALS Seek: 0 Rotate:105 Transfer: 60 Total: 165
|
||||
|
||||
|
||||
Now let's do an example with a seek. Try the following set of requests:
|
||||
|
||||
prompt> disk.py -a 10,18 -G
|
||||
|
||||
|
||||
To compute how long this will take, you need to know how long a seek will
|
||||
take. The distance between each track is by default 40 distance units, and the
|
||||
default rate of seeking is 1 distance unit per unit time. Thus, a seek from
|
||||
the outer track to the middle track takes 40 time units.
|
||||
|
||||
You'd also have to know the scheduling policy. The default is FIFO, though, so
|
||||
for now you can just compute the request times assuming the processing order
|
||||
matches the list specified via the "-a" flag.
|
||||
|
||||
To compute how long it will take the disk to service these requests, we first
|
||||
compute how long it takes to access sector 10, which we know from above to be
|
||||
135 time units (105 rotating, 30 transferring). Once this request is complete,
|
||||
the disk begins to seek to the middle track where sector 18 lies, taking 40
|
||||
time units. Then the disk rotates to sector 18, and transfers it for 30 time
|
||||
units, thus completing the simulation. But how long does this final rotation
|
||||
take?
|
||||
|
||||
To compute the rotational delay for 18, first figure out how long the disk
|
||||
would take to rotate from the end of the access to sector 10 to the beginning
|
||||
of the access to sector 18, assuming a zero-cost seek. As you can see from the
|
||||
simulator, sector 10 on the outer track is lined up with sector 22 on the middle
|
||||
track, and there are 7 sectors separating 22 from 18 (23, 12, 13, 14, 15, 16,
|
||||
and 17, as the disk spins counter-clockwise). Rotating through 7 sectors takes
|
||||
210 time units (30 per sector). However, the first part of this rotation is
|
||||
actually spent seeking to the middle track, for 40 time units. Thus, the
|
||||
actual rotational delay for accessing sector 18 is 210 minus 40, or 170 time
|
||||
units. Run the simulator to see this for yourself; note that you can run
|
||||
without graphics and with the "-c" flag to just see the results without
|
||||
seeing the graphics.
|
||||
|
||||
prompt> ./disk.py -a 10,18 -c
|
||||
...
|
||||
Sector: 10 Seek: 0 Rotate:105 Transfer: 30 Total: 135
|
||||
Sector: 18 Seek: 40 Rotate:170 Transfer: 30 Total: 240
|
||||
TOTALS Seek: 40 Rotate:275 Transfer: 60 Total: 375
|
||||
|
||||
You should now have a basic idea of how the simulator works. The questions
|
||||
below will explore some of the different options, to better help you build a
|
||||
model of how a disk really works.
|
||||
|
||||
730
related_info/ostep/ostep15-disk/disk.py
Executable file
730
related_info/ostep/ostep15-disk/disk.py
Executable file
@@ -0,0 +1,730 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
from Tkinter import *
|
||||
from types import *
|
||||
import math, random, time, sys, os
|
||||
from optparse import OptionParser
|
||||
|
||||
MAXTRACKS = 1000
|
||||
|
||||
# states that a request/disk go through
|
||||
STATE_NULL = 0
|
||||
STATE_SEEK = 1
|
||||
STATE_ROTATE = 2
|
||||
STATE_XFER = 3
|
||||
STATE_DONE = 4
|
||||
|
||||
#
|
||||
# TODO
|
||||
# XXX transfer time
|
||||
# XXX satf
|
||||
# XXX skew
|
||||
# XXX scheduling window
|
||||
# XXX sstf
|
||||
# XXX specify requests vs. random requests in range
|
||||
# XXX add new requests as old ones complete (starvation)
|
||||
# XXX run in non-graphical mode
|
||||
# XXX better graphical display (show key, long lists of requests, more timings on screen)
|
||||
# XXX be able to do "pure" sequential
|
||||
# XXX add more blocks around outer tracks (zoning)
|
||||
# XXX simple flag to make scheduling window a fairness window (-F)
|
||||
# new algs to scan and c-scan the disk?
|
||||
#
|
||||
|
||||
class Disk:
|
||||
def __init__(self, addr, addrDesc, lateAddr, lateAddrDesc,
|
||||
policy, seekSpeed, rotateSpeed, skew, window, compute,
|
||||
graphics, zoning):
|
||||
self.addr = addr
|
||||
self.addrDesc = addrDesc
|
||||
self.lateAddr = lateAddr
|
||||
self.lateAddrDesc = lateAddrDesc
|
||||
self.policy = policy
|
||||
self.seekSpeed = seekSpeed
|
||||
self.rotateSpeed = rotateSpeed
|
||||
self.skew = skew
|
||||
self.window = window
|
||||
self.compute = compute
|
||||
self.graphics = graphics
|
||||
self.zoning = zoning
|
||||
|
||||
# figure out zones first, to figure out the max possible request
|
||||
self.InitBlockLayout()
|
||||
|
||||
# figure out requests
|
||||
random.seed(options.seed)
|
||||
self.requests = self.MakeRequests(self.addr, self.addrDesc)
|
||||
self.lateRequests = self.MakeRequests(self.lateAddr, self.lateAddrDesc)
|
||||
|
||||
# graphical startup
|
||||
self.width = 500
|
||||
if self.graphics:
|
||||
self.root = Tk()
|
||||
tmpLen = len(self.requests)
|
||||
if len(self.lateRequests) > 0:
|
||||
tmpLen += len(self.lateRequests)
|
||||
self.canvas = Canvas(self.root, width=410, height=460 + ((tmpLen / 20) * 20))
|
||||
self.canvas.pack()
|
||||
|
||||
# fairness stuff
|
||||
if self.policy == 'BSATF' and self.window != -1:
|
||||
self.fairWindow = self.window
|
||||
else:
|
||||
self.fairWindow = -1
|
||||
|
||||
print 'REQUESTS', self.requests
|
||||
print ''
|
||||
|
||||
# for late requests
|
||||
self.lateCount = 0
|
||||
if len(self.lateRequests) > 0:
|
||||
print 'LATE REQUESTS', self.lateRequests
|
||||
print ''
|
||||
|
||||
if self.compute == False:
|
||||
print ''
|
||||
print 'For the requests above, compute the seek, rotate, and transfer times.'
|
||||
print 'Use -c or the graphical mode (-G) to see the answers.'
|
||||
print ''
|
||||
|
||||
# BINDINGS
|
||||
if self.graphics:
|
||||
self.root.bind('s', self.Start)
|
||||
self.root.bind('p', self.Pause)
|
||||
self.root.bind('q', self.Exit)
|
||||
|
||||
# TRACK INFO
|
||||
self.tracks = {}
|
||||
self.trackWidth = 40
|
||||
self.tracks[0] = 140
|
||||
self.tracks[1] = self.tracks[0] - self.trackWidth
|
||||
self.tracks[2] = self.tracks[1] - self.trackWidth
|
||||
|
||||
if (self.seekSpeed > 1 and self.trackWidth % self.seekSpeed != 0):
|
||||
print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth)
|
||||
sys.exit(1)
|
||||
if self.seekSpeed < 1:
|
||||
x = (self.trackWidth / self.seekSpeed)
|
||||
y = int(float(self.trackWidth) / float(self.seekSpeed))
|
||||
if float(x) != float(y):
|
||||
print 'Seek speed (%d) must divide evenly into track width (%d)' % (self.seekSpeed, self.trackWidth)
|
||||
sys.exit(1)
|
||||
|
||||
# DISK SURFACE
|
||||
self.cx = self.width/2.0
|
||||
self.cy = self.width/2.0
|
||||
if self.graphics:
|
||||
self.canvas.create_rectangle(self.cx-175, 30, self.cx - 20, 80, fill='gray', outline='black')
|
||||
self.platterSize = 320
|
||||
ps2 = self.platterSize / 2.0
|
||||
if self.graphics:
|
||||
self.canvas.create_oval(self.cx-ps2, self.cy-ps2, self.cx+ps2, self.cy + ps2, fill='darkgray', outline='black')
|
||||
for i in range(len(self.tracks)):
|
||||
t = self.tracks[i] - (self.trackWidth / 2.0)
|
||||
if self.graphics:
|
||||
self.canvas.create_oval(self.cx - t, self.cy - t, self.cx + t, self.cy + t, fill='', outline='black', width=1.0)
|
||||
|
||||
# SPINDLE
|
||||
self.spindleX = self.cx
|
||||
self.spindleY = self.cy
|
||||
if self.graphics:
|
||||
self.spindleID = self.canvas.create_oval(self.spindleX-3, self.spindleY-3, self.spindleX+3, self.spindleY+3, fill='orange', outline='black')
|
||||
|
||||
# DISK ARM
|
||||
self.armTrack = 0
|
||||
self.armSpeedBase = float(seekSpeed)
|
||||
self.armSpeed = float(seekSpeed)
|
||||
|
||||
distFromSpindle = self.tracks[self.armTrack]
|
||||
self.armWidth = 20
|
||||
self.headWidth = 10
|
||||
|
||||
self.armX = self.spindleX - (distFromSpindle * math.cos(math.radians(0)))
|
||||
self.armX1 = self.armX - self.armWidth
|
||||
self.armX2 = self.armX + self.armWidth
|
||||
self.armY1 = 50.0
|
||||
self.armY2 = self.width / 2.0
|
||||
|
||||
self.headX1 = self.armX - self.headWidth
|
||||
self.headX2 = self.armX + self.headWidth
|
||||
self.headY1 = (self.width/2.0) - self.headWidth
|
||||
self.headY2 = (self.width/2.0) + self.headWidth
|
||||
|
||||
if self.graphics:
|
||||
self.armID = self.canvas.create_rectangle(self.armX1, self.armY1, self.armX2, self.armY2, fill='gray', outline='black')
|
||||
self.headID = self.canvas.create_rectangle(self.headX1, self.headY1, self.headX2, self.headY2, fill='gray', outline='black')
|
||||
|
||||
self.targetSize = 10.0
|
||||
if self.graphics:
|
||||
sz = self.targetSize
|
||||
self.targetID = self.canvas.create_oval(self.armX1-sz, self.armY1-sz, self.armX1+sz, self.armY1+sz, fill='orange', outline='')
|
||||
|
||||
# IO QUEUE
|
||||
self.queueX = 20
|
||||
self.queueY = 450
|
||||
|
||||
self.requestCount = 0
|
||||
self.requestQueue = []
|
||||
self.requestState = []
|
||||
self.queueBoxSize = 20
|
||||
self.queueBoxID = {}
|
||||
self.queueTxtID = {}
|
||||
|
||||
# draw each box
|
||||
for index in range(len(self.requests)):
|
||||
self.AddQueueEntry(int(self.requests[index]), index)
|
||||
if self.graphics:
|
||||
self.canvas.create_text(self.queueX - 5, self.queueY - 20, anchor='w', text='Queue:')
|
||||
|
||||
# scheduling window
|
||||
self.currWindow = self.window
|
||||
|
||||
# draw current limits of queue
|
||||
if self.graphics:
|
||||
self.windowID = -1
|
||||
self.DrawWindow()
|
||||
|
||||
# initial scheduling info
|
||||
self.currentIndex = -1
|
||||
self.currentBlock = -1
|
||||
|
||||
# initial state of disk (vs seeking, rotating, transferring)
|
||||
self.state = STATE_NULL
|
||||
|
||||
# DRAW BLOCKS on the TRACKS
|
||||
for bid in range(len(self.blockInfoList)):
|
||||
(track, angle, name) = self.blockInfoList[bid]
|
||||
if self.graphics:
|
||||
distFromSpindle = self.tracks[track]
|
||||
xc = self.spindleX + (distFromSpindle * math.cos(math.radians(angle)))
|
||||
yc = self.spindleY + (distFromSpindle * math.sin(math.radians(angle)))
|
||||
cid = self.canvas.create_text(xc, yc, text=name, anchor='center')
|
||||
else:
|
||||
cid = -1
|
||||
self.blockInfoList[bid] = (track, angle, name, cid)
|
||||
|
||||
# angle of rotation
|
||||
self.angle = 0.0
|
||||
|
||||
# TIME INFO
|
||||
if self.graphics:
|
||||
self.timeID = self.canvas.create_text(10, 10, text='Time: 0.00', anchor='w')
|
||||
self.canvas.create_rectangle(95,0,200,18, fill='orange', outline='orange')
|
||||
self.seekID = self.canvas.create_text(100, 10, text='Seek: 0.00', anchor='w')
|
||||
self.canvas.create_rectangle(195,0,300,18, fill='lightblue', outline='lightblue')
|
||||
self.rotID = self.canvas.create_text(200, 10, text='Rotate: 0.00', anchor='w')
|
||||
self.canvas.create_rectangle(295,0,400,18, fill='green', outline='green')
|
||||
self.xferID = self.canvas.create_text(300, 10, text='Transfer: 0.00', anchor='w')
|
||||
self.canvas.create_text(320, 40, text='"s" to start', anchor='w')
|
||||
self.canvas.create_text(320, 60, text='"p" to pause', anchor='w')
|
||||
self.canvas.create_text(320, 80, text='"q" to quit', anchor='w')
|
||||
self.timer = 0
|
||||
|
||||
# STATS
|
||||
self.seekTotal = 0.0
|
||||
self.rotTotal = 0.0
|
||||
self.xferTotal = 0.0
|
||||
|
||||
# set up animation loop
|
||||
if self.graphics:
|
||||
self.doAnimate = True
|
||||
else:
|
||||
self.doAnimate = False
|
||||
self.isDone = False
|
||||
|
||||
# call this to start simulation
|
||||
def Go(self):
|
||||
if options.graphics:
|
||||
self.root.mainloop()
|
||||
else:
|
||||
self.GetNextIO()
|
||||
while self.isDone == False:
|
||||
self.Animate()
|
||||
|
||||
# crappy error message
|
||||
def PrintAddrDescMessage(self, value):
|
||||
print 'Bad address description (%s)' % value
|
||||
print 'The address description must be a comma-separated list of length three, without spaces.'
|
||||
print 'For example, "10,100,0" would indicate that 10 addresses should be generated, with'
|
||||
print '100 as the maximum value, and 0 as the minumum. A max of -1 means just use the highest'
|
||||
print 'possible value as the max address to generate.'
|
||||
sys.exit(1)
|
||||
|
||||
#
|
||||
# ZONES AND BLOCK LAYOUT
|
||||
#
|
||||
def InitBlockLayout(self):
|
||||
self.blockInfoList = []
|
||||
self.blockToTrackMap = {}
|
||||
self.blockToAngleMap = {}
|
||||
self.tracksBeginEnd = {}
|
||||
self.blockAngleOffset = []
|
||||
|
||||
zones = self.zoning.split(',')
|
||||
assert(len(zones) == 3)
|
||||
for i in range(len(zones)):
|
||||
self.blockAngleOffset.append(int(zones[i]) / 2)
|
||||
|
||||
track = 0 # outer track
|
||||
angleOffset = 2 * self.blockAngleOffset[track]
|
||||
for angle in range(0, 360, angleOffset):
|
||||
block = angle / angleOffset
|
||||
self.blockToTrackMap[block] = track
|
||||
self.blockToAngleMap[block] = angle
|
||||
self.blockInfoList.append((track, angle, block))
|
||||
self.tracksBeginEnd[track] = (0, block)
|
||||
pblock = block + 1
|
||||
|
||||
track = 1 # middle track
|
||||
skew = self.skew
|
||||
angleOffset = 2 * self.blockAngleOffset[track]
|
||||
for angle in range(0, 360, angleOffset):
|
||||
block = (angle / angleOffset) + pblock
|
||||
self.blockToTrackMap[block] = track
|
||||
self.blockToAngleMap[block] = angle + (angleOffset * skew)
|
||||
self.blockInfoList.append((track, angle + (angleOffset * skew), block))
|
||||
self.tracksBeginEnd[track] = (pblock, block)
|
||||
pblock = block + 1
|
||||
|
||||
track = 2 # inner track
|
||||
skew = 2 * self.skew
|
||||
angleOffset = 2 * self.blockAngleOffset[track]
|
||||
for angle in range(0, 360, angleOffset):
|
||||
block = (angle / angleOffset) + pblock
|
||||
self.blockToTrackMap[block] = track
|
||||
self.blockToAngleMap[block] = angle + (angleOffset * skew)
|
||||
self.blockInfoList.append((track, angle + (angleOffset * skew), block))
|
||||
self.tracksBeginEnd[track] = (pblock, block)
|
||||
self.maxBlock = pblock
|
||||
# print 'MAX BLOCK:', self.maxBlock
|
||||
|
||||
# adjust angle to starting position relative
|
||||
for i in self.blockToAngleMap:
|
||||
self.blockToAngleMap[i] = (self.blockToAngleMap[i] + 180) % 360
|
||||
|
||||
# print 'btoa map', self.blockToAngleMap
|
||||
# print 'btot map', self.blockToTrackMap
|
||||
# print 'bao', self.blockAngleOffset
|
||||
|
||||
def MakeRequests(self, addr, addrDesc):
|
||||
(numRequests, maxRequest, minRequest) = (0, 0, 0)
|
||||
if addr == '-1':
|
||||
# first extract values from descriptor
|
||||
desc = addrDesc.split(',')
|
||||
if len(desc) != 3:
|
||||
self.PrintAddrDescMessage(addrDesc)
|
||||
(numRequests, maxRequest, minRequest) = (int(desc[0]), int(desc[1]), int(desc[2]))
|
||||
if maxRequest == -1:
|
||||
maxRequest = self.maxBlock
|
||||
# now make list
|
||||
tmpList = []
|
||||
for i in range(numRequests):
|
||||
tmpList.append(int(random.random() * maxRequest) + minRequest)
|
||||
return tmpList
|
||||
else:
|
||||
return addr.split(',')
|
||||
|
||||
#
|
||||
# BUTTONS
|
||||
#
|
||||
def Start(self, event):
|
||||
self.GetNextIO()
|
||||
self.doAnimate = True
|
||||
self.Animate()
|
||||
|
||||
def Pause(self, event):
|
||||
if self.doAnimate == False:
|
||||
self.doAnimate = True
|
||||
else:
|
||||
self.doAnimate = False
|
||||
|
||||
def Exit(self, event):
|
||||
sys.exit(0)
|
||||
|
||||
#
|
||||
# CORE SIMULATION and ANIMATION
|
||||
#
|
||||
def UpdateTime(self):
|
||||
if self.graphics:
|
||||
self.canvas.itemconfig(self.timeID, text='Time: ' + str(self.timer))
|
||||
self.canvas.itemconfig(self.seekID, text='Seek: ' + str(self.seekTotal))
|
||||
self.canvas.itemconfig(self.rotID, text='Rotate: ' + str(self.rotTotal))
|
||||
self.canvas.itemconfig(self.xferID, text='Transfer: ' + str(self.xferTotal))
|
||||
|
||||
def AddRequest(self, block):
|
||||
self.AddQueueEntry(block, len(self.requestQueue))
|
||||
|
||||
def QueueMap(self, index):
|
||||
numPerRow = 400 / self.queueBoxSize
|
||||
return (index % numPerRow, index / numPerRow)
|
||||
|
||||
def DrawWindow(self):
|
||||
if self.window == -1:
|
||||
return
|
||||
(col, row) = self.QueueMap(self.currWindow)
|
||||
if col == 0:
|
||||
(col, row) = (20, row - 1)
|
||||
if self.windowID != -1:
|
||||
self.canvas.delete(self.windowID)
|
||||
self.windowID = self.canvas.create_line(self.queueX + (col * 20) - 10, self.queueY - 13 + (row * 20),
|
||||
self.queueX + (col * 20) - 10, self.queueY + 13 + (row * 20), width=2)
|
||||
|
||||
def AddQueueEntry(self, block, index):
|
||||
self.requestQueue.append((block, index))
|
||||
self.requestState.append(STATE_NULL)
|
||||
if self.graphics:
|
||||
(col, row) = self.QueueMap(index)
|
||||
sizeHalf = self.queueBoxSize / 2.0
|
||||
(cx, cy) = (self.queueX + (col * self.queueBoxSize), self.queueY + (row * self.queueBoxSize))
|
||||
self.queueBoxID[index] = self.canvas.create_rectangle(cx - sizeHalf, cy - sizeHalf, cx + sizeHalf, cy + sizeHalf, fill='white')
|
||||
self.queueTxtID[index] = self.canvas.create_text(cx, cy, anchor='center', text=str(block))
|
||||
|
||||
def SwitchColors(self, c):
|
||||
if self.graphics:
|
||||
self.canvas.itemconfig(self.queueBoxID[self.currentIndex], fill=c)
|
||||
self.canvas.itemconfig(self.targetID, fill=c)
|
||||
|
||||
def SwitchState(self, newState):
|
||||
self.state = newState
|
||||
self.requestState[self.currentIndex] = newState
|
||||
|
||||
def RadiallyCloseTo(self, a1, a2):
|
||||
if a1 > a2:
|
||||
v = a1 - a2
|
||||
else:
|
||||
v = a2 - a1
|
||||
if v < self.rotateSpeed:
|
||||
return True
|
||||
return False
|
||||
|
||||
def DoneWithTransfer(self):
|
||||
angleOffset = self.blockAngleOffset[self.armTrack]
|
||||
# if int(self.angle) == (self.blockToAngleMap[self.currentBlock] + angleOffset) % 360:
|
||||
if self.RadiallyCloseTo(self.angle, float((self.blockToAngleMap[self.currentBlock] + angleOffset) % 360)):
|
||||
# print 'END TRANSFER', self.angle, self.timer
|
||||
self.SwitchState(STATE_DONE)
|
||||
self.requestCount += 1
|
||||
return True
|
||||
return False
|
||||
|
||||
def DoneWithRotation(self):
|
||||
angleOffset = self.blockAngleOffset[self.armTrack]
|
||||
# XXX there is a weird bug in here
|
||||
# print self.timer, 'ROTATE:: ', self.currentBlock, 'currangle: ', self.angle, ' - mapangle: ', self.blockToAngleMap[self.currentBlock]
|
||||
# print ' angleOffset ', angleOffset
|
||||
# print ' blockMap ', (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360
|
||||
# print ' self.angle ', self.angle, int(self.angle)
|
||||
# if int(self.angle) == (self.blockToAngleMap[self.currentBlock] - angleOffset) % 360:
|
||||
if self.RadiallyCloseTo(self.angle, float((self.blockToAngleMap[self.currentBlock] - angleOffset) % 360)):
|
||||
self.SwitchState(STATE_XFER)
|
||||
# print ' --> DONE WITH ROTATION!', self.timer
|
||||
return True
|
||||
return False
|
||||
|
||||
def PlanSeek(self, track):
|
||||
self.seekBegin = self.timer
|
||||
self.SwitchColors('orange')
|
||||
self.SwitchState(STATE_SEEK)
|
||||
if track == self.armTrack:
|
||||
self.rotBegin = self.timer
|
||||
self.SwitchColors('lightblue')
|
||||
self.SwitchState(STATE_ROTATE)
|
||||
return
|
||||
self.armTarget = track
|
||||
self.armTargetX1 = self.spindleX - self.tracks[track] - (self.trackWidth / 2.0)
|
||||
if track >= self.armTrack:
|
||||
self.armSpeed = self.armSpeedBase
|
||||
else:
|
||||
self.armSpeed = - self.armSpeedBase
|
||||
|
||||
def DoneWithSeek(self):
|
||||
# move the disk arm
|
||||
self.armX1 += self.armSpeed
|
||||
self.armX2 += self.armSpeed
|
||||
self.headX1 += self.armSpeed
|
||||
self.headX2 += self.armSpeed
|
||||
# update it on screen
|
||||
if self.graphics:
|
||||
self.canvas.coords(self.armID, self.armX1, self.armY1, self.armX2, self.armY2)
|
||||
self.canvas.coords(self.headID, self.headX1, self.headY1, self.headX2, self.headY2)
|
||||
# check if done
|
||||
if (self.armSpeed > 0.0 and self.armX1 >= self.armTargetX1) or (self.armSpeed < 0.0 and self.armX1 <= self.armTargetX1):
|
||||
self.armTrack = self.armTarget
|
||||
return True
|
||||
return False
|
||||
|
||||
def DoSATF(self, rList):
|
||||
minBlock = -1
|
||||
minIndex = -1
|
||||
minEst = -1
|
||||
|
||||
# print '**** DoSATF ****', rList
|
||||
for (block, index) in rList:
|
||||
if self.requestState[index] == STATE_DONE:
|
||||
continue
|
||||
track = self.blockToTrackMap[block]
|
||||
angle = self.blockToAngleMap[block]
|
||||
# print 'track', track, 'angle', angle
|
||||
|
||||
# estimate seek time
|
||||
dist = int(math.fabs(self.armTrack - track))
|
||||
seekEst = (self.trackWidth / self.armSpeedBase) * dist
|
||||
# print 'dist', dist
|
||||
# print 'seekEst', seekEst
|
||||
|
||||
# estimate rotate time
|
||||
angleOffset = self.blockAngleOffset[track]
|
||||
# print 'angleOffset', angleOffset
|
||||
# print 'self.angle', self.angle
|
||||
angleAtArrival = (self.angle + (seekEst * self.rotateSpeed))
|
||||
while angleAtArrival > 360.0:
|
||||
angleAtArrival -= 360.0
|
||||
# print 'self.rotateSpeed', self.rotateSpeed
|
||||
# print 'angleAtArrival', angleAtArrival
|
||||
rotDist = ((angle - angleOffset) - angleAtArrival)
|
||||
while rotDist > 360.0:
|
||||
rotDist -= 360.0
|
||||
while rotDist < 0.0:
|
||||
rotDist += 360.0
|
||||
rotEst = rotDist / self.rotateSpeed
|
||||
# print 'rotEst', rotDist, self.rotateSpeed, ' -> ', rotEst
|
||||
|
||||
# finally, transfer
|
||||
xferEst = (angleOffset * 2.0) / self.rotateSpeed
|
||||
|
||||
# print 'xferEst', xferEst
|
||||
|
||||
totalEst = seekEst + rotEst + xferEst
|
||||
# print 'totalEst', seekEst, rotEst, xferEst, ' -> ', totalEst
|
||||
|
||||
# print '--> block:%d seek:%d rotate:%d xfer:%d est:%d' % (block, seekEst, rotEst, xferEst, totalEst)
|
||||
|
||||
# should probably pick one on same track in case of a TIE
|
||||
if minEst == -1 or totalEst < minEst:
|
||||
minEst = totalEst
|
||||
minBlock = block
|
||||
minIndex = index
|
||||
# END loop
|
||||
|
||||
# when done
|
||||
self.totalEst = minEst
|
||||
assert(minBlock != -1)
|
||||
assert(minIndex != -1)
|
||||
return (minBlock, minIndex)
|
||||
|
||||
#
|
||||
# actually doesn't quite do SSTF
|
||||
# just finds all the blocks on the nearest track
|
||||
# (whatever that may be) and returns it as a list
|
||||
#
|
||||
def DoSSTF(self, rList):
|
||||
minDist = MAXTRACKS
|
||||
minBlock = -1
|
||||
trackList = [] # all the blocks on a track
|
||||
|
||||
for (block, index) in rList:
|
||||
if self.requestState[index] == STATE_DONE:
|
||||
continue
|
||||
track = self.blockToTrackMap[block]
|
||||
dist = int(math.fabs(self.armTrack - track))
|
||||
if dist < minDist:
|
||||
trackList = []
|
||||
trackList.append((block, index))
|
||||
minDist = dist
|
||||
elif dist == minDist:
|
||||
trackList.append((block, index))
|
||||
assert(trackList != [])
|
||||
return trackList
|
||||
|
||||
def UpdateWindow(self):
|
||||
if self.fairWindow == -1 and self.currWindow > 0 and self.currWindow < len(self.requestQueue):
|
||||
self.currWindow += 1
|
||||
if self.graphics:
|
||||
self.DrawWindow()
|
||||
|
||||
def GetWindow(self):
|
||||
if self.currWindow <= -1:
|
||||
return len(self.requestQueue)
|
||||
else:
|
||||
if self.fairWindow != -1:
|
||||
if self.requestCount > 0 and (self.requestCount % self.fairWindow == 0):
|
||||
self.currWindow = self.currWindow + self.fairWindow
|
||||
if self.currWindow > len(self.requestQueue):
|
||||
self.currWindow = len(self.requestQueue)
|
||||
if self.graphics:
|
||||
self.DrawWindow()
|
||||
return self.currWindow
|
||||
else:
|
||||
return self.currWindow
|
||||
|
||||
def GetNextIO(self):
|
||||
# check if done: if so, print stats and end animation
|
||||
if self.requestCount == len(self.requestQueue):
|
||||
self.UpdateTime()
|
||||
self.PrintStats()
|
||||
self.doAnimate = False
|
||||
self.isDone = True
|
||||
return
|
||||
|
||||
# do policy: should set currentBlock,
|
||||
if self.policy == 'FIFO':
|
||||
(self.currentBlock, self.currentIndex) = self.requestQueue[self.requestCount]
|
||||
self.DoSATF(self.requestQueue[self.requestCount:self.requestCount+1])
|
||||
elif self.policy == 'SATF' or self.policy == 'BSATF':
|
||||
(self.currentBlock, self.currentIndex) = self.DoSATF(self.requestQueue[0:self.GetWindow()])
|
||||
elif self.policy == 'SSTF':
|
||||
# first, find all the blocks on a given track (given window constraints)
|
||||
trackList = self.DoSSTF(self.requestQueue[0:self.GetWindow()])
|
||||
# then, do SATF on those blocks (otherwise, will not do them in obvious order)
|
||||
(self.currentBlock, self.currentIndex) = self.DoSATF(trackList)
|
||||
else:
|
||||
print 'policy (%s) not implemented' % self.policy
|
||||
sys.exit(1)
|
||||
|
||||
# once best block is decided, go ahead and do the seek
|
||||
self.PlanSeek(self.blockToTrackMap[self.currentBlock])
|
||||
|
||||
# add another block?
|
||||
if len(self.lateRequests) > 0 and self.lateCount < len(self.lateRequests):
|
||||
self.AddRequest(self.lateRequests[self.lateCount])
|
||||
self.lateCount += 1
|
||||
|
||||
def Animate(self):
|
||||
if self.graphics == True and self.doAnimate == False:
|
||||
self.root.after(20, self.Animate)
|
||||
return
|
||||
|
||||
# timer
|
||||
self.timer += 1
|
||||
self.UpdateTime()
|
||||
|
||||
# see which blocks are rotating on the disk
|
||||
# print 'SELF ANGLE', self.angle
|
||||
self.angle = self.angle + self.rotateSpeed
|
||||
if self.angle >= 360.0:
|
||||
self.angle = 0.0
|
||||
|
||||
# move the blocks
|
||||
if self.graphics:
|
||||
for (track, angle, name, cid) in self.blockInfoList:
|
||||
distFromSpindle = self.tracks[track]
|
||||
na = angle - self.angle
|
||||
xc = self.spindleX + (distFromSpindle * math.cos(math.radians(na)))
|
||||
yc = self.spindleY + (distFromSpindle * math.sin(math.radians(na)))
|
||||
if self.graphics:
|
||||
self.canvas.coords(cid, xc, yc)
|
||||
if self.currentBlock == name:
|
||||
sz = self.targetSize
|
||||
self.canvas.coords(self.targetID, xc-sz, yc-sz, xc+sz, yc+sz)
|
||||
|
||||
# move the arm OR wait for a rotational delay
|
||||
if self.state == STATE_SEEK:
|
||||
if self.DoneWithSeek():
|
||||
self.rotBegin = self.timer
|
||||
self.SwitchState(STATE_ROTATE)
|
||||
self.SwitchColors('lightblue')
|
||||
if self.state == STATE_ROTATE:
|
||||
# check for read (disk arm must be settled)
|
||||
if self.DoneWithRotation():
|
||||
self.xferBegin = self.timer
|
||||
self.SwitchState(STATE_XFER)
|
||||
self.SwitchColors('green')
|
||||
if self.state == STATE_XFER:
|
||||
if self.DoneWithTransfer():
|
||||
self.DoRequestStats()
|
||||
self.SwitchState(STATE_DONE)
|
||||
self.SwitchColors('red')
|
||||
self.UpdateWindow()
|
||||
currentBlock = self.currentBlock
|
||||
self.GetNextIO()
|
||||
nextBlock = self.currentBlock
|
||||
if self.blockToTrackMap[currentBlock] == self.blockToTrackMap[nextBlock]:
|
||||
if (currentBlock == self.tracksBeginEnd[self.armTrack][1] and nextBlock == self.tracksBeginEnd[self.armTrack][0]) or (currentBlock + 1 == nextBlock):
|
||||
# need a special case here: to handle when we stay in transfer mode
|
||||
(self.rotBegin, self.seekBegin, self.xferBegin) = (self.timer, self.timer, self.timer)
|
||||
self.SwitchState(STATE_XFER)
|
||||
self.SwitchColors('green')
|
||||
|
||||
|
||||
|
||||
# make sure to keep the animation going!
|
||||
if self.graphics:
|
||||
self.root.after(20, self.Animate)
|
||||
|
||||
def DoRequestStats(self):
|
||||
seekTime = self.rotBegin - self.seekBegin
|
||||
rotTime = self.xferBegin - self.rotBegin
|
||||
xferTime = self.timer - self.xferBegin
|
||||
totalTime = self.timer - self.seekBegin
|
||||
|
||||
if self.compute == True:
|
||||
print 'Block: %3d Seek:%3d Rotate:%3d Transfer:%3d Total:%4d' % (self.currentBlock, seekTime, rotTime, xferTime, totalTime)
|
||||
|
||||
# if int(totalTime) != int(self.totalEst):
|
||||
# print 'INTERNAL ERROR: estimate was', self.totalEst, 'whereas actual time to access block was', totalTime
|
||||
# print 'Please report this bug and as much information as possible so as to make it easy to recreate. Thanks!'
|
||||
|
||||
# update stats
|
||||
self.seekTotal += seekTime
|
||||
self.rotTotal += rotTime
|
||||
self.xferTotal += xferTime
|
||||
|
||||
|
||||
|
||||
def PrintStats(self):
|
||||
if self.compute == True:
|
||||
print '\nTOTALS Seek:%3d Rotate:%3d Transfer:%3d Total:%4d\n' % (self.seekTotal, self.rotTotal, self.xferTotal, self.timer)
|
||||
|
||||
# END: class Disk
|
||||
|
||||
|
||||
|
||||
#
|
||||
# MAIN SIMULATOR
|
||||
#
|
||||
parser = OptionParser()
|
||||
parser.add_option('-s', '--seed', default='0', help='Random seed', action='store', type='int', dest='seed')
|
||||
parser.add_option('-a', '--addr', default='-1', help='Request list (comma-separated) [-1 -> use addrDesc]', action='store', type='string', dest='addr')
|
||||
parser.add_option('-A', '--addrDesc', default='5,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='addrDesc')
|
||||
parser.add_option('-S', '--seekSpeed', default='1', help='Speed of seek', action='store', type='string', dest='seekSpeed')
|
||||
parser.add_option('-R', '--rotSpeed', default='1', help='Speed of rotation', action='store', type='string', dest='rotateSpeed')
|
||||
parser.add_option('-p', '--policy', default='FIFO', help='Scheduling policy (FIFO, SSTF, SATF, BSATF)', action='store', type='string', dest='policy')
|
||||
parser.add_option('-w', '--schedWindow', default=-1, help='Size of scheduling window (-1 -> all)', action='store', type='int', dest='window')
|
||||
parser.add_option('-o', '--skewOffset', default=0, help='Amount of skew (in blocks)', action='store', type='int', dest='skew')
|
||||
parser.add_option('-z', '--zoning', default='30,30,30', help='Angles between blocks on outer,middle,inner tracks', action='store', type='string', dest='zoning')
|
||||
parser.add_option('-G', '--graphics', default=False, help='Turn on graphics', action='store_true', dest='graphics')
|
||||
parser.add_option('-l', '--lateAddr', default='-1', help='Late: request list (comma-separated) [-1 -> random]', action='store', type='string', dest='lateAddr')
|
||||
parser.add_option('-L', '--lateAddrDesc', default='0,-1,0', help='Num requests, max request (-1->all), min request', action='store', type='string', dest='lateAddrDesc')
|
||||
parser.add_option('-c', '--compute', default=False, help='Compute the answers', action='store_true', dest='compute')
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
print 'OPTIONS seed', options.seed
|
||||
print 'OPTIONS addr', options.addr
|
||||
print 'OPTIONS addrDesc', options.addrDesc
|
||||
print 'OPTIONS seekSpeed', options.seekSpeed
|
||||
print 'OPTIONS rotateSpeed', options.rotateSpeed
|
||||
print 'OPTIONS skew', options.skew
|
||||
print 'OPTIONS window', options.window
|
||||
print 'OPTIONS policy', options.policy
|
||||
print 'OPTIONS compute', options.compute
|
||||
print 'OPTIONS graphics', options.graphics
|
||||
print 'OPTIONS zoning', options.zoning
|
||||
print 'OPTIONS lateAddr', options.lateAddr
|
||||
print 'OPTIONS lateAddrDesc', options.lateAddrDesc
|
||||
print ''
|
||||
|
||||
if options.window == 0:
|
||||
print 'Scheduling window (%d) must be positive or -1 (which means a full window)' % options.window
|
||||
sys.exit(1)
|
||||
|
||||
if options.graphics and options.compute == False:
|
||||
print '\nWARNING: Setting compute flag to True, as graphics are on\n'
|
||||
options.compute = True
|
||||
|
||||
# set up simulator info
|
||||
d = Disk(addr=options.addr, addrDesc=options.addrDesc, lateAddr=options.lateAddr, lateAddrDesc=options.lateAddrDesc,
|
||||
policy=options.policy, seekSpeed=float(options.seekSpeed), rotateSpeed=float(options.rotateSpeed),
|
||||
skew=options.skew, window=options.window, compute=options.compute, graphics=options.graphics, zoning=options.zoning)
|
||||
|
||||
# run simulation
|
||||
d.Go()
|
||||
Reference in New Issue
Block a user