/**
* Copyright 2012-2013 University Of Southern California
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.workflowsim.clustering;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.workflowsim.Task;
/**
* BlockClustering groups tasks in both horizontal and vertical direction
*
* @author Weiwei Chen
* @since WorkflowSim Toolkit 1.0
* @date Apr 9, 2013
*/
public class BlockClustering extends BasicClustering {
/**
* The number of clustered jobs per level.
*/
private final int clusterNum;
/**
* The size of a clustered job (number of tasks in a job).
*/
private final int clusterSize;
/**
* The map stores whether a task has checked.
*/
private final Map<Integer, Boolean> mHasChecked;
/**
* The map stores tasks per level.
*/
private final Map<Integer, List> mDepth2Task;
/**
* Initialize a BlockClustering object
*
* @param cNum clusters.num
* @param cSize clusters.size
*/
public BlockClustering(int cNum, int cSize) {
super();
clusterNum = cNum;
clusterSize = cSize;
this.mHasChecked = new HashMap<>();
this.mDepth2Task = new HashMap<>();
}
/**
* Set the check point of a task.
*
* @param index id of a task
*/
private void setCheck(int index) {
if (mHasChecked.containsKey(index)) {
mHasChecked.remove(index);
}
mHasChecked.put(index, true);
}
/**
* Gets the check point of a task
*
* @param index id of a task
* @return
*/
private boolean getCheck(int index) {
if (mHasChecked.containsKey(index)) {
return mHasChecked.get(index);
}
return false;
}
/**
* The main function
*/
@Override
public void run() {
// level by level
if (clusterNum > 0 || clusterSize > 0) {
for (Task task : getTaskList()) {
int depth = task.getDepth();
if (!mDepth2Task.containsKey(depth)) {
mDepth2Task.put(depth, new ArrayList<>());
}
List list = mDepth2Task.get(depth);
if (!list.contains(task)) {
list.add(task);
}
}
}
if (clusterNum > 0) {
bundleClustering();
} else if (clusterSize > 0) {
collapseClustering();
}
mHasChecked.clear();
super.clean();
updateDependencies();
addClustDelay();
}
/**
* Provides the potential candidate to merge
*
* @param taskList the seed tasks
* @return candidate tasks
*/
private List searchList(List<Task> taskList) {
List<Task> sucList = new ArrayList<>();
for (Task task : taskList) {
if (!getCheck(task.getCloudletId())) {
setCheck(task.getCloudletId());
sucList.add(task);
//add all of its successors
Task node = task;
while (node != null) {
if (node.getChildList().size() == 1) {
Task child = node.getChildList().get(0);
if (!getCheck(child.getCloudletId()) && child.getParentList().size() == 1) {
setCheck(child.getCloudletId());
sucList.add(child);
node = child;
} else {
node = null;
}
} else {
node = null;
}
}
}
}
return sucList;
}
/**
* Merges tasks into a fixed number of jobs.
*/
private void bundleClustering() {
for (Map.Entry<Integer, List> pairs : mDepth2Task.entrySet()) {
List list = pairs.getValue();
int num = list.size();
int avg_a = num / this.clusterNum;
int avg_b = avg_a;
if (avg_a * this.clusterNum < num) {
avg_b++;
}
int mid = num - this.clusterNum * avg_a;
if (avg_a <= 0) {
avg_a = 1;
}
if (avg_b <= 0) {
avg_b = 1;
}
int start = 0, end = -1;
for (int i = 0; i < this.clusterNum; i++) {
start = end + 1;
if (i < mid) {
//use avg_b
end = start + avg_b - 1;
} else {
//use avg_a
end = start + avg_a - 1;
}
if (end >= num) {
end = num - 1;
}
if (end < start) {
break;
}
addTasks2Job(searchList(list.subList(start, end + 1)));
}
}
}
/**
* Merges a fixed number of tasks into a job
*/
private void collapseClustering() {
for (Map.Entry<Integer, List> pairs : mDepth2Task.entrySet()) {
List list = pairs.getValue();
int num = list.size();
int avg = this.clusterSize;
int start = 0;
int end = 0;
int i = 0;
do {
start = i * avg;
end = start + avg - 1;
i++;
if (end >= num) {
end = num - 1;
}
addTasks2Job(searchList(list.subList(start, end + 1)));
} while (end < num - 1);
}
}
}