Files
WFR-lib/src/scheduling/scheduler/sufferage/SufferageScheduler.cpp
T
Matthias 19d695d4d8 Added lookahead scheduler.
Most scheduler now use estimated finish time instead of predicted execution time.
2017-07-26 18:56:44 +02:00

135 lines
6.0 KiB
C++

#include <wfr/scheduling/scheduler/sufferage/SufferageScheduler.h>
#include <wfr/common/wep/WorkflowExecutionPlan.h>
#include <wfr/common/wep/AbstractWepNode.h>
#include <wfr/common/wep/activities/AtomicWepNode.h>
#include <wfr/common/wfs/Activity.h>
#include <wfr/common/wfs/ActivityInstance.h>
#include <wfr/common/wfs/AtomicActivityImplementation.h>
#include <wfr/common/wfs/instances/AtomicActivityInstance.h>
#include <wfr/common/wfs/templates/AtomicActivityTemplate.h>
#include <wfr/common/resource/ResourceManager.h>
#include <wfr/enactment/EnactmentEngine.h>
#include <wfr/common/scheduling/SchedulingDecision.h>
#include <wfr/execution/ExecutionManager.h>
#include <wfr/WorkflowRuntime.h>
#include <wfr/wep/utils/WepSearch.h>
#include <iostream>
#include <set>
#include <queue>
namespace wfr {
namespace scheduling {
std::pair<wep::AtomicWepNode*, SufferageScheduler::ECTEntry> SufferageScheduler::_getNextTaskToSchedule(std::map<wep::AtomicWepNode*, ECTEntry>& estimatedTaskCompletionTimes) {
wep::AtomicWepNode* selectedNode = nullptr;
ECTEntry selectedEntry;
bool firstIteration = true;
for (auto n : estimatedTaskCompletionTimes) {
if (firstIteration) {
selectedNode = n.first;
selectedEntry = n.second;
firstIteration = false;
} else {
if (std::get<2>(n.second) > std::get<2>(selectedEntry)) {
selectedNode = n.first;
selectedEntry = n.second;
}
}
}
return std::make_pair(selectedNode, selectedEntry);
}
void SufferageScheduler::_notifyWEPUpdate(SchedulerBase::WepMapEntry& wepe, std::set<wep::AtomicWepNode*> readyToExecute, std::unique_lock<std::mutex>& schedulerLock) {
while (!readyToExecute.empty()) {
std::map<wep::AtomicWepNode*, ECTEntry> estimatedTaskCompletionTimes;
auto resourceTypes = resourceManager.getComputingResourceTypes();
// Iterate over all tasks
for (auto n : readyToExecute) {
resource::ComputingResource* selectedResource = nullptr;
double selectedEarliestEstimatedCompletionTime = -1;
wfs::AtomicActivityImplementation* selectedImpl = nullptr;
prediction::ExecutionTimeResult selectedPred;
double selectedEarliestEstimatedCompletionTime2 = -1;
std::map<resource::ComputingResource*, double> estimatedCompletionTimes;
// Iterate over all resource types
for (auto restype : resourceTypes) {
// Get fastest implementation and predicted time for given resource type
wfs::AtomicActivityImplementation* fastestImpl = nullptr;
prediction::ExecutionTimeResult fastestPred;
// Check cache first
bool cacheHit = false;
auto aaType = n->activity.baseActivity.baseTemplate.as<wfs::AtomicActivityTemplate>().atomicActivityType.get();
auto& innerMap = implementationCache[aaType];
auto cacheEntry = innerMap.find(restype);
if (cacheEntry != innerMap.end()) {
fastestImpl = cacheEntry->second.first;
fastestPred = cacheEntry->second.second;
cacheHit = true;
}
if (!cacheHit) {
// Cache miss
auto& aat = n->activity.baseActivity.baseTemplate.as<wfs::AtomicActivityTemplate>();
for (auto i : aat.atomicActivityType->getImplementations()) {
auto execPred = predictionManager.predictExecutionTime(n->activity, *i, *restype);
if (execPred.isValid() && (!fastestPred.isValid() || execPred < fastestPred)) {
fastestPred = execPred;
fastestImpl = i;
}
}
// store into cache
if (fastestPred.isValid()) {
innerMap.insert({restype, {fastestImpl, fastestPred}});
}
}
if (fastestPred.isValid()) {
// Iterate over all resources and calculate earliest completion time
for (auto res : restype->getResources()) {
auto ect = _getResourceEstimatedAvailableTime(res) + fastestPred.executionTime.getValue();
if (selectedEarliestEstimatedCompletionTime < 0 || ect < selectedEarliestEstimatedCompletionTime) {
selectedEarliestEstimatedCompletionTime2 = selectedEarliestEstimatedCompletionTime;
selectedEarliestEstimatedCompletionTime = ect;
selectedResource = res;
selectedImpl = fastestImpl;
selectedPred = fastestPred;
} else if (selectedEarliestEstimatedCompletionTime2 < 0 || ect < selectedEarliestEstimatedCompletionTime2) {
selectedEarliestEstimatedCompletionTime2 = ect;
}
} // End: Iterate over all resources
}
} // End: Iterate over all resource types
// Calculate sufferage
double sufferage = 0;
if (selectedEarliestEstimatedCompletionTime > 0 && selectedEarliestEstimatedCompletionTime2 > 0) {
sufferage = selectedEarliestEstimatedCompletionTime2 - selectedEarliestEstimatedCompletionTime;
}
//std::cout << "selectedEarliestEstimatedCompletionTime " << n->activity.baseActivity.baseTemplate.activityName << ": " << selectedEarliestEstimatedCompletionTime << std::endl;
//std::cout << "selectedEarliestEstimatedCompletionTime2 " << n->activity.baseActivity.baseTemplate.activityName << ": " << selectedEarliestEstimatedCompletionTime2 << std::endl;
//std::cout << "Sufferage " << n->activity.baseActivity.baseTemplate.activityName << ": " << sufferage << std::endl;
if (selectedResource) {
estimatedTaskCompletionTimes.insert({n, std::make_tuple(selectedResource, selectedImpl, sufferage, selectedPred)});
}
} // End: Iterate over all tasks
// Select next task to schedule
auto selectedTask = _getNextTaskToSchedule(estimatedTaskCompletionTimes);
// Schedule task
if (selectedTask.first) {
auto reservation = resourceManager.reserveComputingResource(std::get<0>(selectedTask.second), 1, false);
auto sd = std::make_shared<SchedulingDecision>(&selectedTask.first->activity, std::get<1>(selectedTask.second), wepe.wep, reservation);
sd->setPrediction(std::get<3>(selectedTask.second));
selectedTask.first->activity.setInstanceStatus(wfs::ActivityInstanceStatus::Scheduled);
executeSchedulingDecision(wepe, sd, schedulerLock);
readyToExecute.erase(selectedTask.first);
}
}
}
void SufferageScheduler::_notifySchedulingDecisionStatusUpdate(WepMapEntry& wepe, std::shared_ptr<SchedulingDecision> sd, std::unique_lock<std::mutex>& schedulerLock) {}
} /* namespace scheduling */
} /* namespace wfr */