def greedy(wrkld, spd, pwrusg, idle, idleusg, pwrcap, plcy): """Greedly schedules tasks according to a certain policy (no transitions while executing a task) Arguments: wrkld: list of workloads of each task spd: list of speeds of configurations states - for every task i, every machine j, and every configuration k, - spd[i][j][k] is the speed of task i in machine j while in configuration k pwrusg: list of power usages of configuration states - for every task i, every machine j, and every configuration k, - pwrusg[i][j][k] is the power usage of task i in machine j while in configuration k idle: list of idle configuration states - for every machine j - idle[j] is the idle configuration of machine j (i.e., the state with less power usage) idleusg: list of power usages of idle configuration states - for every machine j - idleusg[j] is the power usage of the idle configuration of machine j when no task is running pwrcap: power limit of the system plcy: function that receives wrkld, spd, pwrusg and two triples (i1,j1,k1) and (i2,j2,k2) and returns True if and only if (i1,j1,k1) < (i2,j2,k2) in the policy used to greedly allocate the task (i.e., triple (i1,j1,k1) is preferred over (i2,j2,k2)) --> plcy may assume that it will receive triples that don't yield zero speed Returns: A triple (order, trn, run) order: list of orderings of tasks on machines - for every machine j, - order[j] is a list [i1, ..., ik] that describes the tasks that will be run on machine j and their order trn: list of state transitions - for every t in range(len(trn)), - trn[t].time is the time of transition t - trn[t].config is a list of machine configurations -- for every machine j, -- trn[t].config[j] is the configuration to which machine j has transitioned run: list of completion times of tasks - for every machine j, and every t in len(order[j]) - task order[j] runs on machine j from time run[j][t] to time run[j][t+1] Exceptions raised: - no_solution if pwrcap cannot be satisfied Assumptions: Compatibility of sizes Non-negative values Non-zero tasks, machines and configurations Idle state usages are less than when tasks are being run Every task i on idle state idle[j] has same usage pwrusg[i][j][idle[j]] as idleusg[j] Every task on an idle state has speed zero """ tasks = len(spd) machines = len(spd[0]) configs = len(spd[0][0]) order = [None] * machines trn = [] run = [None] * machines for j in range(machines): order[j] = [] run[j] = [0] possib = [(i,j,k) for i in range(tasks) for j in range(machines) for k in range(configs) if pwrusg[i][j][k] <= pwrcap and spd[i][j][k] > 0 #exclude violating configs and zero speeds ] possib.sort(key=less2key(lambda t1,t2: plcy(wrkld,spd,pwrusg,t1,t2), tuple)) taskcompleted = [False] * tasks events = [(0, #time 0)]#machine that became free (first value is irrelevant) currentconfigs = [idle[j] for j in range(machines)] currentpwrusgs = [idleusg[j] for j in range(machines)] currenttotalpwrusg = sum(currentpwrusgs) currentfree = [True] * machines numberoffree = machines while True: #Equivalent to while len(events) != 0 here (time, becamefree) = heapq.heappop(events) # currentfree update block if time != 0: #to exclude first iteration while True: currentfree[becamefree] = True numberoffree += 1 currenttotalpwrusg += idleusg[becamefree] - currentpwrusgs[becamefree] currentpwrusgs[becamefree] = idleusg[becamefree] currentconfigs[becamefree] = idle[becamefree] if len(events) == 0 or events[0][0] != time: break #next event has the same time (i.e., more machines may have become free at time) becamefree = heapq.heappop(events)[1] # End currentfree update block psbind = 0 #since deletion in list possib is involved, we must do it like this while numberoffree: while psbind < len(possib): i,j,k = possib[psbind] if taskcompleted[i]: # task is completed: can be removed from possibilities del possib[psbind] continue # avoid incrementing index psbind if currentfree[j] and currenttotalpwrusg - currentpwrusgs[j] + pwrusg[i][j][k] <= pwrcap: break # found best valid triple! psbind += 1 else: # (while's else) cannot run any more tasks at this time if numberoffree < machines: #at least one occupied machine, wait for it to finish #(that is leave the while loop skipping the "Run task block") break #no occupied machine: either we are done or there is no solution if False in taskcompleted: #some task is left: no solution raise no_solution for j in range(machines):# Fix run for machines that never ran any task if len(order[j]) == 0: run[j] = [] trn.append(stateTran(time,currentconfigs))#add final transition to idle states at the end return (order,trn,run) #RETURN IS HERE!! # Run i on j with config k block taskcompleted[i] = True del possib[psbind] order[j].append(i) currenttotalpwrusg += pwrusg[i][j][k] - currentpwrusgs[j] currentconfigs[j] = k currentpwrusgs[j] = pwrusg[i][j][k] #transition left to be updated after all tasks of this time are processed completiontime = time + float(wrkld[i]) / spd[i][j][k] #speed guaranteed to be non-zero run[j].append(completiontime) heapq.heappush(events,(completiontime,j)) #machine j will become free at time completiontime currentfree[j] = False numberoffree -= 1 # End of run i on j with config k block #update transition of this time trn.append(stateTran(time,currentconfigs[:])) #the slicing is to generate a copy
def halfHeartedGreedy(wrkld, spd, pwrusg, idle, idleusg, pwrcap, plcies, makecopy=True, reorder=True): """Greedly schedules tasks according to a certain policy (allows transitions while executing a task) Arguments: wrkld: list of workloads of each task spd: list of speeds of configurations states - for every task i, every machine j, and every configuration k, - spd[i][j][k] is the speed of task i in machine j while in configuration k pwrusg: list of power usages of configuration states - for every task i, every machine j, and every configuration k, - pwrusg[i][j][k] is the power usage of task i in machine j while in configuration k idle: list of idle configuration states - for every machine j - idle[j] is the idle configuration of machine j (i.e., the state with less power usage) idleusg: list of power usages of idle configuration states - for every machine j - idleusg[j] is the power usage of the idle configuration of machine j when no task is running pwrcap: power limit of the system plcies: list of functions that receive wrkld, spd, pwrusg and two triples (i1,j1,k1) and (i2,j2,k2) and return True if and only if (i1,j1,k1) < (i2,j2,k2) in the policy used to greedly allocate the task (i.e., triple (i1,j1,k1) is preferred over (i2,j2,k2)) --> plcy may assume that it will receive triples that don't yield zero speed makecopy: boolean, if True, algorithm does not change any of its parameters (see Data races) reorder: reorder triples (task, machine, configuration) after each task completion (needed if policy is to use workload left instead of initial workload of tasks) Returns: A triple (order, trn, run) order: list of orderings of tasks on machines - for every machine j, - order[j] is a list [i1, ..., ik] that describes the tasks that will be run on machine j and their order trn: list of state transitions - for every t in range(len(trn)), - trn[t].time is the time of transition t - trn[t].config is a list of machine configurations -- for every machine j, -- trn[t].config[j] is the configuration to which machine j has transitioned run: list of completion times of tasks - for every machine j, and every t in len(order[j]) - task order[j] runs on machine j from time run[j][t] to time run[j][t+1] Exceptions raised: - no_solution if pwrcap cannot be satisfied Assumptions: Compatibility of sizes Non-negative values Non-zero tasks, machines and configurations Idle state usages are less than when tasks are being run Every task i on idle state idle[j] has same usage pwrusg[i][j][idle[j]] as idleusg[j] Every task on an idle state has speed zero plcies non-empty Data races: If makecopy = False, then - wrkld is modified """ if makecopy: wrkld = wrkld[:] tasks = len(spd) machines = len(spd[0]) configs = len(spd[0][0]) order = [None] * machines trn = [] run = [None] * machines for j in range(machines): order[j] = [] run[j] = [0] nplcies = len(plcies) possibs = [None] * nplcies possibs[0] = [(i,j,k) for i in range(tasks) for j in range(machines) for k in range(configs) if pwrusg[i][j][k] <= pwrcap and spd[i][j][k] > 0 #exclude violating configs and zero speeds ] keys = [None] * nplcies for p in range(1,nplcies): possibs[p] = possibs[0][:] #make copy keys[p]=less2key(lambda t1,t2: plcies[p](wrkld,spd,pwrusg,t1,t2), tuple) possibs[p].sort(key=keys[p]) taskstatus = [0] * tasks #0: waiting, 1: running, 2: completed events = [(0, #time 0)]#machine that became free (first value is irrelevant) idletotalpwrusg = sum(idleusg) currentrunning = [None] * machines time = 0 #The below will be true in the first iteration, but do not need to be initialized here: #currentconfigs = [idle[j] for j in range(machines)] #currentpwrusgs = [idleusg[j] for j in range(machines)] #currentfree = [True] * machines #numberoffree = machines #currenttotalpwrusg = idletotalpwrusg while True: #Equivalent to while len(events) != 0 here prevtime = time time = events[0][0] # taskstatus update block if prevtime != time: #to exclude first iteration # determine the minimum completion time for (t,mc) in events: if t < time: time = t # consider tasks that finish at time time completed for (t,machinecompleted) in events: if t == time: taskstatus[currentrunning[machinecompleted]] = 2 #completed run[machinecompleted].append(time) currentrunning[machinecompleted] = None # End taskstatus update block # wrkld update block deltat = time - prevtime for j in range(machines): if currentrunning[j] != None: wrkld[currentrunning[j]] -= spd[currentrunning[j]][j][currentconfigs[j]] * deltat # End wrkld update block # Falsely transition every occupied machine to idle state (so that the algorithm can choose new configs) currentconfigs = [idle[j] for j in range(machines)] currentpwrusgs = [idleusg[j] for j in range(machines)] currenttotalpwrusg = idletotalpwrusg currentcompletiontimes = [None] * machines someOccupied = False for p in range(nplcies): # Since wrklds might have been updated, we may need to sort the triples again if (reorder): possibs[p].sort(key=keys[p]) psbind = 0 #since deletion in list possibs[p] is involved, we must do it like this numberofnotconsidered = machines currentnotconsidered = [True] * machines while numberofnotconsidered: while psbind < len(possibs[p]): i,j,k = possibs[p][psbind] if taskstatus[i] == 2: # task is completed: can be removed from possibilities del possibs[p][psbind] continue # avoid incrementing index psbind if (currentnotconsidered[j] and ((currentrunning[j] == None and taskstatus[i] == 0) or currentrunning[j] == i) and currenttotalpwrusg - currentpwrusgs[j] + pwrusg[i][j][k] <= pwrcap): break # found best valid triple! psbind += 1 else: # (while's else) cannot run any more tasks at this time if p+1 < nplcies or someOccupied: #we are not finished deciding or there is at least one occupied machine #(that is leave the while loop skipping the "Run task block") break #no occupied machine: either we are done or there is no solution if 0 in taskstatus: #some task is left: no solution raise no_solution for j in range(machines):# Fix run for machines that never ran any task if len(order[j]) == 0: run[j] = [] trn.append(stateTran(time,currentconfigs))#add final transition to idle states at the end return (order,trn,run) #RETURN IS HERE!! # Consider running i on j with config k block taskstatus[i] = 1 #running if currentrunning[j] == None: order[j].append(i) currentrunning[j] = i currenttotalpwrusg += pwrusg[i][j][k] - currentpwrusgs[j] currentconfigs[j] = k currentpwrusgs[j] = pwrusg[i][j][k] #transition left to be updated after all tasks of this time are processed completiontime = time + float(wrkld[i]) / spd[i][j][k] #speed guaranteed to be non-zero #if we don't change our mind, machine j will become free at time completiontime: currentcompletiontimes[j] = completiontime someOccupied = True currentnotconsidered[j] = False numberofnotconsidered -= 1 # End of consider running i on j with config k block #out of for: commit to decision and update transition of this time events = [(currentcompletiontimes[j],j) for j in range(machines) if currentcompletiontimes[j] != None] trn.append(stateTran(time,currentconfigs[:])) #the slicing is to generate a copy