#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace wfr { namespace scheduling { double SufferageScheduler::_getResourceEstimatedAvailableTime(resource::ComputingResource* resource) { if (resource->getFreeSlotCount() > 0) { return 0.0; } else { // Calculating the estimated available time for resources with more than 1 slot is not trivial // Here we are employing a heuristic that partitions the scheduled tasks into chunks the size of the maximal slot count, // takes the minimum time of each chunk and adds them together. unsigned i = 0; double eat = 0; double minTime = -1; auto& tasks = scheduledTasks[resource]; for (auto t : tasks) { i++; if (minTime < 0) { minTime = t->getPrediction().getExecutionTime().getValue(); } else { if (i > resource->getMaxSlotCount()) { eat += minTime; i = 0; minTime = t->getPrediction().getExecutionTime().getValue(); } else { if (t->getPrediction().getExecutionTime().getValue() < minTime) { minTime = t->getPrediction().getExecutionTime().getValue(); } } } } return eat; } } std::pair SufferageScheduler::_getNextTaskToSchedule(std::map& 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 readyToExecute, std::unique_lock& schedulerLock) { while (!readyToExecute.empty()) { std::map 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 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().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(); 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(&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); scheduledTasks[sd->resource].push_back(sd); executeSchedulingDecision(wepe, sd, schedulerLock); readyToExecute.erase(selectedTask.first); } } } void SufferageScheduler::_notifySchedulingDecisionStatusUpdate(WepMapEntry& wepe, std::shared_ptr sd, std::unique_lock& schedulerLock) { auto& innerMap = scheduledTasks[sd->resource]; for (auto i = innerMap.begin(); i != innerMap.end(); i++) { if (*i == sd) { innerMap.erase(i); break; } } } } /* namespace scheduling */ } /* namespace wfr */