Most scheduler now use estimated finish time instead of predicted execution time.
135 lines
6.0 KiB
C++
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 */
|