package resa.evaluation.migrate;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Created by ding on 15/10/19.
*/
public class Flux {
public static MigPlan calc(double[] dataSizes, double[] workloads, List<int[]> currAlloc, int newSize) {
if (newSize == currAlloc.size()) {
throw new IllegalArgumentException("size equal");
}
return newSize > currAlloc.size() ? inc(dataSizes, workloads, currAlloc, newSize)
: dec(dataSizes, workloads, currAlloc, newSize);
}
private static MigPlan dec(double[] dataSizes, double[] workloads, List<int[]> currAlloc, int newSize) {
Node[] nodes = currAlloc.stream().limit(newSize).map(alloc -> new Node(alloc, workloads)).toArray(Node[]::new);
Set<Integer> toMoveTasks = new HashSet<>();
for (int i = newSize; i < currAlloc.size(); i++) {
int[] alloc = currAlloc.get(i);
for (int j = 0; j < alloc.length; j++) {
nodes[(i + j) % newSize].addTask(alloc[j]);
toMoveTasks.add(alloc[j]);
}
}
double dataToMove = toMoveTasks.stream().mapToDouble(t -> dataSizes[t]).sum()
+ adjust(nodes, dataSizes, toMoveTasks);
return new MigPlan(convertNodes(nodes), dataToMove, null);
}
private static List<int[]> convertNodes(Node[] nodes) {
return Arrays.stream(nodes).map(node -> node.alloc.stream().mapToInt(i -> i).toArray())
.collect(Collectors.toList());
}
private static double adjust(Node[] nodes, double[] dataSizes, Set<Integer> excludeTasks) {
double totalToMove = 0;
while (true) {
Arrays.sort(nodes);
Integer taskToMove = nodes[nodes.length - 1].findBestTask(nodes[0].currWorkload);
if (taskToMove == null) {
break;
}
nodes[nodes.length - 1].takeTask(taskToMove);
nodes[0].addTask(taskToMove);
if (excludeTasks.add(taskToMove)) {
totalToMove += dataSizes[taskToMove];
}
}
return totalToMove;
}
private static MigPlan inc(double[] dataSizes, double[] workloads, List<int[]> currAlloc, int newSize) {
Node[] nodes = new Node[newSize];
int i = 0;
for (int[] alloc : currAlloc) {
nodes[i++] = new Node(alloc, workloads);
}
while (i < newSize) {
nodes[i++] = new Node(new int[0], workloads);
}
double dataToMove = adjust(nodes, dataSizes, new HashSet<>());
return new MigPlan(convertNodes(nodes), dataToMove, null);
}
private static class Node implements Comparable<Node> {
List<Integer> alloc;
double[] workloads;
double currWorkload;
public Node(int[] alloc, double[] workloads) {
this.alloc = Arrays.stream(alloc).boxed().collect(Collectors.toList());
this.workloads = workloads;
currWorkload = Arrays.stream(alloc).mapToDouble(i -> workloads[i]).sum();
}
@Override
public int compareTo(Node o) {
return Double.compare(currWorkload, o.currWorkload);
}
public Integer findBestTask(double destWorkload) {
int best = -1;
double diff = currWorkload - destWorkload;
for (int i = 0; i < alloc.size(); i++) {
double currDiff = Math.abs((destWorkload + workloads[alloc.get(i)]) -
(currWorkload - workloads[alloc.get(i)]));
if (diff > currDiff) {
best = i;
diff = currDiff;
}
}
return best < 0 ? null : alloc.get(best);
}
public void takeTask(int task) {
alloc.remove((Object) task);
currWorkload -= workloads[task];
}
public void addTask(int task) {
alloc.add(task);
currWorkload += workloads[task];
}
}
}