Simulator
This exercise 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 (for you to run locally) to show you exactly what happens when the disk is in action.
#! /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()
Simulator
Questions
- Compute the seek, rotation, and transfer times for the following sets of requests:
-a 0
,-a 6
,-a 30
, -a 7,30,8,
and finally-a 10, 11, 12, 13
. - Do the same requests above, but change the seek rate to different values:
-S 2
,-S 4
,-S 8
,-S 10
,-S 40
,-S 0.1
. How do the times change? - Do the same requests above, but change the rotation rate:
-R 0.1
,-R 0.5
,-R 0.01
. How do the times change? - FIFO is not always best, e.g., with the request stream
-a 7, 30, 8
, what order should the requests be processed in? Run the shortest seek time first (SSTF) scheduler (-p SSTF
) on this workload; how long should it take (seek, rotation, transfer) for each request to be served? - Now use the shortest access-time first (SATF) scheduler (
-p SATF
). Does it make any difference for-a 7, 30, 8
workload? Find a set of requests where SATF outperforms SSTF; more generally, when is SATF better than SSTF? - Here is a request stream to try:
-a 10, 11, 12, 13
. What goes poorly when it runs? Try adding track skew to address this problem (-o skew
). Given the default seek rate, what should the skew be to maximize performance? What about for different seek rates (e.g.,-S 2
,-S 4
)? In general, could you write a formula to figure out the skew? - Specify a disk with different density per zone, e.g.,
-z 10, 20, 30
, which specifies the angular difference between blocks on the outer, middle, and inner tracks. Run some random requests (e.g.,-a -1 -A 5, -1, 0
, which specifies that random requests should be used via the-a -1
flag and that five requests ranging from 0 to the max be generated), and compute the seek, rotation, and transfer times. Use different random seeds. What is the bandwidth (in sectors per unit time) on the outer, middle, and inner tracks? - A scheduling window determines how many requests the disk can examine at once. Generate random workloads (e.g.,
-A 1000, -1, 0
, with different seeds) and see how long the SATF scheduler takes when the scheduling window is changed from 1 up to the number of requests. How big of a window is needed to maximize performance? Hint: use the-c
flag and don’t turn on graphics (-G
) to run these quickly. When the scheduling window is set to 1, does it matter which policy you are using? - Create a series of requests to starve a particular request, assuming an SATF policy. Given that sequence, how does it perform if you use a bounded SATF (BSATF) scheduling approach? In this approach, you specify the scheduling window (e.g.,
-w 4
); the scheduler only moves onto the next window of requests when all requests in the current window have been serviced. Does this solve starvation? How does it perform, as compared to SATF? In general, how should a disk make this trade-off between performance and starvation avoidance? - All the scheduling policies we have looked at thus far are greedy; they pick the next best option instead of looking for an optimal schedule. Can you find a set of requests in which greedy is not optimal?
Get hands-on with 1400+ tech skills courses.