|
|
- #! /usr/bin/env python
-
- import sys
- from optparse import OptionParser
- import random
-
- # finds the highest nonempty queue
- # -1 if they are all empty
- def FindQueue():
- q = hiQueue
- while q > 0:
- if len(queue[q]) > 0:
- return q
- q -= 1
- if len(queue[0]) > 0:
- return 0
- return -1
-
- def LowerQueue(currJob, currQueue, issuedIO):
- if currQueue > 0:
- # in this case, have to change the priority of the job
- job[currJob]['currPri'] = currQueue - 1
- if issuedIO == False:
- queue[currQueue-1].append(currJob)
- job[currJob]['ticksLeft'] = quantum[currQueue-1]
- else:
- if issuedIO == False:
- queue[currQueue].append(currJob)
- job[currJob]['ticksLeft'] = quantum[currQueue]
-
- def Abort(str):
- sys.stderr.write(str + '\n')
- exit(1)
-
-
- #
- # PARSE ARGUMENTS
- #
-
- parser = OptionParser()
- parser.add_option('-s', '--seed', default=0, help='the random seed',
- action='store', type='int', dest='seed')
- parser.add_option('-n', '--numQueues', help='number of queues in MLFQ (if not using -Q)', default=3,
- action='store', type='int', dest='numQueues')
- parser.add_option('-q', '--quantum', help='length of time slice (if not using -Q)', default=10,
- action='store', type='int', dest='quantum')
- parser.add_option('-Q', '--quantumList', help='length of time slice per queue level, specified as x,y,z,... where x is the quantum length for the highest priority queue, y the next highest, and so forth',
- default='', action='store', type='string', dest='quantumList')
- parser.add_option('-j', '--numJobs', default=3, help='number of jobs in the system',
- action='store', type='int', dest='numJobs')
- parser.add_option('-m', '--maxlen', default=100, help='max run-time of a job (if randomly generating)',
- action='store', type='int', dest='maxlen')
- parser.add_option('-M', '--maxio', default=10, help='max I/O frequency of a job (if randomly generating)',
- action='store', type='int', dest='maxio')
- parser.add_option('-B', '--boost', default=0, help='how often to boost the priority of all jobs back to high priority',
- action='store', type='int', dest='boost')
- parser.add_option('-i', '--iotime', default=5, help='how long an I/O should last (fixed constant)',
- action='store', type='int', dest='ioTime')
- parser.add_option('-S', '--stay', default=False, help='reset and stay at same priority level when issuing I/O',
- action='store_true', dest='stay')
- parser.add_option('-I', '--iobump', default=False, help='if specified, jobs that finished I/O move immediately to front of current queue',
- action='store_true', dest='iobump')
- parser.add_option('-l', '--jlist', default='', help='a comma-separated list of jobs to run, in the form x1,y1,z1:x2,y2,z2:... where x is start time, y is run time, and z is how often the job issues an I/O request',
- action='store', type='string', dest='jlist')
- parser.add_option('-c', help='compute answers for me', action='store_true', default=False, dest='solve')
-
- (options, args) = parser.parse_args()
-
- random.seed(options.seed)
-
-
- # MLFQ: How Many Queues
- numQueues = options.numQueues
-
- quantum = {}
- if options.quantumList != '':
- # instead, extract number of queues and their time slic
- quantumLengths = options.quantumList.split(',')
- numQueues = len(quantumLengths)
- qc = numQueues - 1
- for i in range(numQueues):
- quantum[qc] = int(quantumLengths[i])
- qc -= 1
- else:
- for i in range(numQueues):
- quantum[i] = int(options.quantum)
-
- hiQueue = numQueues - 1
-
- # MLFQ: I/O Model
- # the time for each IO: not great to have a single fixed time but...
- ioTime = int(options.ioTime)
-
- # This tracks when IOs and other interrupts are complete
- ioDone = {}
-
- # This stores all info about the jobs
- job = {}
-
- # seed the random generator
- random.seed(options.seed)
-
- # jlist 'startTime,runTime,ioFreq:startTime,runTime,ioFreq:...'
- jobCnt = 0
- if options.jlist != '':
- allJobs = options.jlist.split(':')
- for j in allJobs:
- jobInfo = j.split(',')
- if len(jobInfo) != 3:
- sys.stderr.write('Badly formatted job string. Should be x1,y1,z1:x2,y2,z2:...\n')
- sys.stderr.write('where x is the startTime, y is the runTime, and z is the I/O frequency.\n')
- exit(1)
- assert(len(jobInfo) == 3)
- startTime = int(jobInfo[0])
- runTime = int(jobInfo[1])
- ioFreq = int(jobInfo[2])
- job[jobCnt] = {'currPri':hiQueue, 'ticksLeft':quantum[hiQueue], 'startTime':startTime,
- 'runTime':runTime, 'timeLeft':runTime, 'ioFreq':ioFreq, 'doingIO':False,
- 'firstRun':-1}
- if startTime not in ioDone:
- ioDone[startTime] = []
- ioDone[startTime].append((jobCnt, 'JOB BEGINS'))
- jobCnt += 1
- else:
- # do something random
- for j in range(options.numJobs):
- startTime = 0
- runTime = int(random.random() * options.maxlen)
- ioFreq = int(random.random() * options.maxio)
-
- job[jobCnt] = {'currPri':hiQueue, 'ticksLeft':quantum[hiQueue], 'startTime':startTime,
- 'runTime':runTime, 'timeLeft':runTime, 'ioFreq':ioFreq, 'doingIO':False,
- 'firstRun':-1}
- if startTime not in ioDone:
- ioDone[startTime] = []
- ioDone[startTime].append((jobCnt, 'JOB BEGINS'))
- jobCnt += 1
-
-
- numJobs = len(job)
-
- print 'Here is the list of inputs:'
- print 'OPTIONS jobs', numJobs
- print 'OPTIONS queues', numQueues
- for i in range(len(quantum)-1,-1,-1):
- print 'OPTIONS quantum length for queue %2d is %3d' % (i, quantum[i])
- print 'OPTIONS boost', options.boost
- print 'OPTIONS ioTime', options.ioTime
- print 'OPTIONS stayAfterIO', options.stay
- print 'OPTIONS iobump', options.iobump
-
- print '\n'
- print 'For each job, three defining characteristics are given:'
- print ' startTime : at what time does the job enter the system'
- print ' runTime : the total CPU time needed by the job to finish'
- print ' ioFreq : every ioFreq time units, the job issues an I/O'
- print ' (the I/O takes ioTime units to complete)\n'
-
- print 'Job List:'
- for i in range(numJobs):
- print ' Job %2d: startTime %3d - runTime %3d - ioFreq %3d' % (i, job[i]['startTime'],
- job[i]['runTime'], job[i]['ioFreq'])
- print ''
-
- if options.solve == False:
- print 'Compute the execution trace for the given workloads.'
- print 'If you would like, also compute the response and turnaround'
- print 'times for each of the jobs.'
- print ''
- print 'Use the -c flag to get the exact results when you are finished.\n'
- exit(0)
-
- # initialize the MLFQ queues
- queue = {}
- for q in range(numQueues):
- queue[q] = []
-
- # TIME IS CENTRAL
- currTime = 0
-
- # use these to know when we're finished
- totalJobs = len(job)
- finishedJobs = 0
-
- print '\nExecution Trace:\n'
-
- while finishedJobs < totalJobs:
- # find highest priority job
- # run it until either
- # (a) the job uses up its time quantum
- # (b) the job performs an I/O
-
- # check for priority boost
- if options.boost > 0 and currTime != 0:
- if currTime % options.boost == 0:
- print '[ time %d ] BOOST ( every %d )' % (currTime, options.boost)
- # remove all jobs from queues (except high queue)
- for q in range(numQueues-1):
- for j in queue[q]:
- if job[j]['doingIO'] == False:
- queue[hiQueue].append(j)
- queue[q] = []
- # print 'BOOST: QUEUES look like:', queue
-
- # change priority to high priority
- # reset number of ticks left for all jobs (XXX just for lower jobs?)
- # add to highest run queue (if not doing I/O)
- for j in range(numJobs):
- # print '-> Boost %d (timeLeft %d)' % (j, job[j]['timeLeft'])
- if job[j]['timeLeft'] > 0:
- # print '-> FinalBoost %d (timeLeft %d)' % (j, job[j]['timeLeft'])
- job[j]['currPri'] = hiQueue
- job[j]['ticksLeft'] = quantum[hiQueue]
- # print 'BOOST END: QUEUES look like:', queue
-
- # check for any I/Os done
- if currTime in ioDone:
- for (j, type) in ioDone[currTime]:
- q = job[j]['currPri']
- job[j]['doingIO'] = False
- print '[ time %d ] %s by JOB %d' % (currTime, type, j)
- if options.iobump == False:
- queue[q].append(j)
- else:
- queue[q].insert(0, j)
-
- # now find the highest priority job
- currQueue = FindQueue()
- if currQueue == -1:
- print '[ time %d ] IDLE' % (currTime)
- currTime += 1
- continue
- #print 'FOUND QUEUE: %d' % currQueue
- #print 'ALL QUEUES:', queue
-
- # there was at least one runnable job, and hence ...
- currJob = queue[currQueue][0]
- if job[currJob]['currPri'] != currQueue:
- Abort('currPri[%d] does not match currQueue[%d]' % (job[currJob]['currPri'], currQueue))
-
- job[currJob]['timeLeft'] -= 1
- job[currJob]['ticksLeft'] -= 1
-
- if job[currJob]['firstRun'] == -1:
- job[currJob]['firstRun'] = currTime
-
- runTime = job[currJob]['runTime']
- ioFreq = job[currJob]['ioFreq']
- ticksLeft = job[currJob]['ticksLeft']
- timeLeft = job[currJob]['timeLeft']
-
- print '[ time %d ] Run JOB %d at PRIORITY %d [ TICKSLEFT %d RUNTIME %d TIMELEFT %d ]' % (currTime, currJob, currQueue, ticksLeft, runTime, timeLeft)
-
- if timeLeft < 0:
- Abort('Error: should never have less than 0 time left to run')
-
-
- # UPDATE TIME
- currTime += 1
-
- # CHECK FOR JOB ENDING
- if timeLeft == 0:
- print '[ time %d ] FINISHED JOB %d' % (currTime, currJob)
- finishedJobs += 1
- job[currJob]['endTime'] = currTime
- # print 'BEFORE POP', queue
- done = queue[currQueue].pop(0)
- # print 'AFTER POP', queue
- assert(done == currJob)
- continue
-
- # CHECK FOR IO
- issuedIO = False
- if ioFreq > 0 and (((runTime - timeLeft) % ioFreq) == 0):
- # time for an IO!
- print '[ time %d ] IO_START by JOB %d' % (currTime, currJob)
- issuedIO = True
- desched = queue[currQueue].pop(0)
- assert(desched == currJob)
- job[currJob]['doingIO'] = True
- # this does the bad rule -- reset your tick counter if you stay at the same level
- if options.stay == True:
- job[currJob]['ticksLeft'] = quantum[currQueue]
- # add to IO Queue: but which queue?
- futureTime = currTime + ioTime
- if futureTime not in ioDone:
- ioDone[futureTime] = []
- ioDone[futureTime].append((currJob, 'IO_DONE'))
- # print 'NEW IO EVENT at ', futureTime, ' is ', ioDone[futureTime]
-
- # CHECK FOR QUANTUM ENDING AT THIS LEVEL
- if ticksLeft == 0:
- # print '--> DESCHEDULE %d' % currJob
- if issuedIO == False:
- # print '--> BUT IO HAS NOT BEEN ISSUED (therefor pop from queue)'
- desched = queue[currQueue].pop(0)
- assert(desched == currJob)
- # move down one queue! (unless lowest queue)
- LowerQueue(currJob, currQueue, issuedIO)
-
-
- # print out statistics
- print ''
- print 'Final statistics:'
- responseSum = 0
- turnaroundSum = 0
- for i in range(numJobs):
- response = job[i]['firstRun'] - job[i]['startTime']
- turnaround = job[i]['endTime'] - job[i]['startTime']
- print ' Job %2d: startTime %3d - response %3d - turnaround %3d' % (i, job[i]['startTime'],
- response, turnaround)
- responseSum += response
- turnaroundSum += turnaround
-
- print '\n Avg %2d: startTime n/a - response %.2f - turnaround %.2f' % (i,
- float(responseSum)/numJobs,
- float(turnaroundSum)/numJobs)
-
- print '\n'
-
-
-
-
-
-
-
|