package edu.usc.enl.dynamicmeasurement.algorithms.tasks.multitask.singleswitch.resourceallocation.dream;
import edu.usc.enl.dynamicmeasurement.algorithms.tasks.multitask.singleswitch.resourceallocation.AllocationTaskView;
import edu.usc.enl.dynamicmeasurement.algorithms.tasks.multitask.singleswitch.resourceallocation.MultiTaskResourceControl;
import edu.usc.enl.dynamicmeasurement.algorithms.tasks.multitask.singleswitch.resourceallocation.TaskRecord2;
import edu.usc.enl.dynamicmeasurement.algorithms.tasks.multitask.singleswitch.resourceallocation.dream.distributor.AbstractResourceDistributor;
import edu.usc.enl.dynamicmeasurement.algorithms.tasks.multitask.singleswitch.resourceallocation.dream.step.RichPoorStepUpdater;
import edu.usc.enl.dynamicmeasurement.algorithms.tasks.multitask.singleswitch.resourceallocation.dream.step.StepUpdater;
import edu.usc.enl.dynamicmeasurement.data.ConfigReader;
import edu.usc.enl.dynamicmeasurement.model.monitorpoint.MonitorPoint;
import edu.usc.enl.dynamicmeasurement.util.ControlledBufferWriter;
import edu.usc.enl.dynamicmeasurement.util.Util;
import org.w3c.dom.Element;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* User: masoud
* Date: 8/30/13
* Time: 3:23 PM <br/>
* Take resource from tasks above high threshold to tasks below low threshold.
* It should also try to dream tasks that cannot be brought up a few epochs
* <p>The XML constructor requires the following Property children tags: <ul>
* <li> name attribute as "LowThreshold", low threshold for accuracy bound</li>
* <li> name attribute as "HighThreshold", high threshold for accuracy bound</li>
* <li> name attribute as "PotentialDrop", should it use a potential-based drop in the local drop algorithm or not (Optional, not used)</li>
* <li> name attribute as "Headroom", the percentage of headroom</li>
* <li> name attribute as "MinResource", minimum resources for tasks and minimum size of step</li>
* <li> name attribute as "Distributor", Refers to the class for distributing resources among tasks</li>
* <li> name attribute as "StepUpdater", Refers to the class that implements the updating step policy</li>
* </ul></p>
*/
public class ThresholdGuaranteeAlgorithm2 extends MultiTaskResourceControl {
protected static final double probAdd2 = 0.05;
protected static final int probDiv = 2;
protected final double lowThreshold;
protected final double highThreshold;
protected final int headroom;
protected final int minResource;
protected final int maxResource;
protected final Map<AllocationTaskView, DreamTaskRecord> taskRecords;
protected final DummyTaskRecord dummyTaskRecord;
protected final List<AllocationTaskView> joiningTasks = new LinkedList<>();
protected int step;
// private DropFlagRaiser dropFlagRaiser;
private AbstractResourceDistributor resourceDistributor;
private StepUpdater stepUpdater;
private int tasksNumForPriority = 0;
private ControlledBufferWriter logWriter;
public ThresholdGuaranteeAlgorithm2(Element element, MonitorPoint monitorPoint) {
int capacity = monitorPoint.getCapacity();
Map<String, Element> properties = Util.getChildrenProperties(element, "Property");
this.lowThreshold = Double.parseDouble(properties.get("LowThreshold").getAttribute(ConfigReader.PROPERTY_VALUE));
this.highThreshold = Double.parseDouble(properties.get("HighThreshold").getAttribute(ConfigReader.PROPERTY_VALUE));
boolean potentialDrop = false;
if (properties.containsKey("PotentialDrop")) {
potentialDrop = Integer.parseInt(properties.get("PotentialDrop").getAttribute(ConfigReader.PROPERTY_VALUE)) > 0;
}
if (properties.containsKey("Headroom")) {
headroom = (int) (capacity * Double.parseDouble(properties.get("Headroom").getAttribute(ConfigReader.PROPERTY_VALUE)));
} else {
headroom = 0;
}
this.minResource = Integer.parseInt(properties.get("MinResource").getAttribute(ConfigReader.PROPERTY_VALUE));
this.maxResource = capacity;
// int dropEpochs = Integer.parseInt(properties.get("DropEpochs").getAttribute(ConfigReader.PROPERTY_VALUE));
// PriorityDropPolicy dropPolicy = new PriorityDropPolicy(potentialDrop, dropEpochs);
try {
// dropFlagRaiser = (DropFlagRaiser) Class.forName(properties.get("DropPolicy").getAttribute(ConfigReader.PROPERTY_VALUE)).getConstructor(
// ThresholdGuaranteeAlgorithm2.class, PriorityDropPolicy.class).newInstance(this, dropPolicy);
resourceDistributor = (AbstractResourceDistributor) Class.forName(properties.get("Distributor").
getAttribute(ConfigReader.PROPERTY_VALUE)).getConstructor(ThresholdGuaranteeAlgorithm2.class).newInstance(this);
stepUpdater = (StepUpdater) Class.forName(properties.get("StepUpdater").getAttribute(ConfigReader.PROPERTY_VALUE)).getConstructor().newInstance();
stepUpdater.init((int) (capacity * probAdd2), probDiv, this);
} catch (Exception e) {
e.printStackTrace();
}
taskRecords = new HashMap<>();
{
dummyTaskRecord = resourceDistributor.getDummy();
dummyTaskRecord.getTask().setResourceShare(maxResource);
dummyTaskRecord.setReductionStep(maxResource);
}
step = 0;
}
public int getHeadroom() {
return headroom;
}
private void addJoining(List<DreamTaskRecord> poorTaskRecords) {
int newTaskNum = taskRecords.size() + joiningTasks.size();
for (AllocationTaskView task : joiningTasks) {
DreamTaskRecord newTaskRecord = new DreamTaskRecord(task, getNewTaskPriority(), this);
// if (task.getAggregatedAccuracy() < lowThreshold) {
poorTaskRecords.add(newTaskRecord);
// newTaskRecord.setAdditionStep((maxResource / newTaskNum) - minResource );//I already got minResource. it sinks all resources in TCAM
newTaskRecord.setAdditionStep(Math.min(10 * minResource, (maxResource / newTaskNum) - minResource));
newTaskRecord.setReductionStep(newTaskRecord.getAdditionStep() / 2);
newTaskRecord.setWasRich(false);
newTaskRecord.setWasPoor(true);
// }
// else {
// newTaskRecord.setAdditionStep(minResource);
// newTaskRecord.setReductionStep(minResource);
// newTaskRecord.setWasRich(task.getAggregatedAccuracy() > highThreshold);
// newTaskRecord.setWasPoor(false);
// }
taskRecords.put(task, newTaskRecord);
tasksNumForPriority++;
}
joiningTasks.clear();
}
public double getLowThreshold() {
return lowThreshold;
}
public double getHighThreshold() {
return highThreshold;
}
@Override
public void allocate() {
// int sum = 0;
// for (AllocationTaskView allocationTaskView : taskRecords.keySet()) {
// sum += allocationTaskView.getResourceShare();
// }
// for (AllocationTaskView joiningTask : joiningTasks) {
// sum += joiningTask.getResourceShare();
// }
// if (sum + dummyTaskRecord.getTask().getResourceShare() != (1 << 15)) {
// System.out.println("Wasted resource");
// System.exit(1);
// }
List<DreamTaskRecord> richTaskRecords = new ArrayList<>();
List<DreamTaskRecord> poorTaskRecords = new ArrayList<>();
for (DreamTaskRecord dreamTaskRecord : taskRecords.values()) {
dreamTaskRecord.updateState(this);
}
for (DreamTaskRecord taskRecord : taskRecords.values()) {
if (taskRecord.getDelayedResourceChange() != null) {
taskRecord.setLastResourceChange(taskRecord.getLastResourceChange() + taskRecord.getDelayedResourceChange());
}
if (taskRecord.amRich()) {
richTaskRecords.add(taskRecord);
taskRecord.setDelayedResourceChange(null); //No delay in richness to poorness
} else if (taskRecord.amPoor()) {
if (taskRecord.notUsingAll()) {
taskRecord.setDelayedResourceChange(taskRecord.getLastResourceChange());
} else {
poorTaskRecords.add(taskRecord);
taskRecord.setDelayedResourceChange(null);
}
} else {
taskRecord.setDelayedResourceChange(null); //middle case
}
}
stepUpdater.updateStep(taskRecords.values());
addJoining(poorTaskRecords);
logWriter.println(step);
for (DreamTaskRecord taskRecord : taskRecords.values()) {
logWriter.println(taskRecord + " agg acc=" + taskRecord.getTask().getAggregatedAccuracy());
}
logWriter.println(dummyTaskRecord.toString());
prepareForDistribute(richTaskRecords, poorTaskRecords);
for (DreamTaskRecord taskRecord : taskRecords.values()) {
taskRecord.setWasPoor(taskRecord.amPoor());
taskRecord.setWasRich(taskRecord.amRich());
taskRecord.setLastAccuracy(taskRecord.getTask().getAggregatedAccuracy());
}
step++;
}
private void prepareForDistribute(List<DreamTaskRecord> richTaskRecords, List<DreamTaskRecord> poorTaskRecords) {
for (DreamTaskRecord taskRecord : taskRecords.values()) {
taskRecord.setLastResourceChange(0);
}
Map<DreamTaskRecord, Integer> taskLastResource = new HashMap<>();
for (DreamTaskRecord task : taskRecords.values()) {
taskLastResource.put(task, task.getTask().getResourceShare());
}
resourceDistributor.distribute(richTaskRecords, poorTaskRecords);
for (Map.Entry<DreamTaskRecord, Integer> entry : taskLastResource.entrySet()) {
DreamTaskRecord taskRecord = entry.getKey();
taskRecord.setLastResourceChange(taskRecord.getTask().getResourceShare() - entry.getValue());
}
}
@Override
public boolean addTask(AllocationTaskView task) {
//check headroom
double currentHeadroom = calculateHeadroom(false);
if (headroom > 0 && headroom > currentHeadroom) { //not enough headroom
//need some logs
logWriter.println("not enough headroom (" + currentHeadroom + ") for task " + task);
// calculateHeadroom(true);
return false;
}
//just give min resource from the one with max resource
double max = 0;
DreamTaskRecord maxTask = null;
if (dummyTaskRecord.getTask().getResourceShare() >= minResource) {
maxTask = dummyTaskRecord;
} else {
for (Map.Entry<AllocationTaskView, DreamTaskRecord> entry : taskRecords.entrySet()) {
AllocationTaskView task1 = entry.getKey();
if (maxTask == null || max < task1.getResourceShare() ||
(max == task1.getResourceShare() && maxTask.compareTo(entry.getValue()) > 0)) {//to always take resource from lower priority in case of equal max
max = task1.getResourceShare();
maxTask = entry.getValue();
}
}
}
if (maxTask == null || !maxTask.canOfferNewComer()) {
return false;
}
task.setResourceShare(minResource);
maxTask.getTask().setResourceShare(maxTask.getTask().getResourceShare() - minResource);
joiningTasks.add(task);
return true;
}
private double calculateHeadroom(boolean log) {
double currentHeadroom = maxResource;
int newTaskPriority = getNewTaskPriority();
for (DreamTaskRecord dreamTaskRecord : taskRecords.values()) {
if (dreamTaskRecord.getDropPriority() < newTaskPriority) {
continue;
}
AllocationTaskView task1 = dreamTaskRecord.getTask();
double aggregatedAccuracy = task1.getAggregatedAccuracy();
if (aggregatedAccuracy < lowThreshold) { //cannot use the was poor as it may be in the middle of two allocation phase
int oldAdd = dreamTaskRecord.getAdditionStep();
int oldRed = dreamTaskRecord.getReductionStep();
((RichPoorStepUpdater) stepUpdater).runForTask(dreamTaskRecord, dreamTaskRecord.findAmRich(this), dreamTaskRecord.findAmPoor(this));
currentHeadroom -= task1.getUsedResourceShare() + dreamTaskRecord.getAdditionStep2();
if (log) {
logWriter.println(currentHeadroom + "," + task1);
}
dreamTaskRecord.setAdditionStep(oldAdd);
dreamTaskRecord.setReductionStep(oldRed);
// }
} else if (aggregatedAccuracy > highThreshold) { // rich
int oldAdd = dreamTaskRecord.getAdditionStep();
int oldRed = dreamTaskRecord.getReductionStep();
((RichPoorStepUpdater) stepUpdater).runForTask(dreamTaskRecord, dreamTaskRecord.findAmRich(this), dreamTaskRecord.findAmPoor(this));
currentHeadroom -= Math.max(minResource, task1.getUsedResourceShare() - dreamTaskRecord.getReductionStep2());
if (log) {
logWriter.println(currentHeadroom + "," + task1);
}
dreamTaskRecord.setAdditionStep(oldAdd);
dreamTaskRecord.setReductionStep(oldRed);
} else {
currentHeadroom -= task1.getUsedResourceShare();
if (log) {
logWriter.println(currentHeadroom + "," + task1);
}
}
}
currentHeadroom -= joiningTasks.size() *
(taskRecords.size() > 0 ?
Math.min(
(maxResource - Math.max(0, currentHeadroom)) / taskRecords.size(),
10 * minResource) :
10 * minResource);
if (log) {
logWriter.println(currentHeadroom + "," + joiningTasks.size() + "," + (taskRecords.size() > 0 ?
Math.min(
(maxResource - Math.max(0, currentHeadroom)) / taskRecords.size(),
10 * minResource) :
10 * minResource));
}
return currentHeadroom;
}
@Override
public void removeTask(AllocationTaskView task) {
DreamTaskRecord remove = taskRecords.remove(task);
if (remove == null) {//may be it just joined
for (Iterator<AllocationTaskView> iterator = joiningTasks.iterator(); iterator.hasNext(); ) {
AllocationTaskView next = iterator.next();
if (next.equals(task)) {
iterator.remove();
break;
}
}
}
dummyTaskRecord.getTask().setResourceShare(dummyTaskRecord.getTask().getResourceShare() + task.getResourceShare());
task.setResourceShare(0);
}
public ControlledBufferWriter getLogWriter() {
return logWriter;
}
public void setLogWriter(ControlledBufferWriter log) {
logWriter = log;
}
public DummyTaskRecord getDummyTaskRecord() {
return dummyTaskRecord;
}
protected DreamTaskRecord getTask(AllocationTaskView richTask) {
if (richTask.equals(dummyTaskRecord.getTask())) {
return dummyTaskRecord;
}
return taskRecords.get(richTask);
}
public int getMinResource() {
return minResource;
}
public int getMaxResource() {
return maxResource;
}
public Collection<? extends TaskRecord2> getTasks() {
return taskRecords.values();
}
public int getNewTaskPriority() {
return -tasksNumForPriority;
}
}