diff --git a/include/aidge/scheduler/Scheduler.hpp b/include/aidge/scheduler/Scheduler.hpp index 6e08885fc3f8966fba48be1c55a6965ac9e70775..28ecde6d9319ae05be20f591cc9a6a4e2a29acc0 100644 --- a/include/aidge/scheduler/Scheduler.hpp +++ b/include/aidge/scheduler/Scheduler.hpp @@ -82,6 +82,14 @@ protected: std::chrono::time_point<std::chrono::high_resolution_clock> end; /** Actual end time of execution */ }; public: + enum class AvailableDataStatus { + Connected, + UpperNodeInputFound, + UpperNodeInputConnected, + ValidTensor, + NotConnected + }; + /** * @struct PriorProducersConsumers * @brief Manages producer-consumer relationships for nodes. @@ -179,7 +187,7 @@ protected: */ std::set<std::shared_ptr<Node>> getConsumers(const std::set<std::shared_ptr<Node>>& producers) const; - Elts_t getNbAvailableData(const std::shared_ptr<Node>& node, const IOIndex_t inputIdx) const; + Elts_t getNbAvailableData(const std::shared_ptr<Node>& node, const IOIndex_t inputIdx, AvailableDataStatus& status) const; /** * @brief Get the prior producers and consumers for a node. @@ -233,4 +241,23 @@ protected: }; } // namespace Aidge +namespace Aidge { +inline auto format_as(Scheduler::AvailableDataStatus status) { + switch (status) { + case Scheduler::AvailableDataStatus::Connected: + return "The input is connected to a Node."; + case Scheduler::AvailableDataStatus::UpperNodeInputFound: + return "The input is an upper node input, but is not connected in any GraphView."; + case Scheduler::AvailableDataStatus::UpperNodeInputConnected: + return "The input is an upper node input and is connected to a Node."; + case Scheduler::AvailableDataStatus::ValidTensor: + return "The input is not connected in the current GraphView but has a valid tensor assigned."; + case Scheduler::AvailableDataStatus::NotConnected: + return "The input is not connected in the current GraphView."; + default: + return "UNKNOWN STATUS."; + } +} +} + #endif /* AIDGE_CORE_SCHEDULER_SCHEDULER_H_ */ diff --git a/src/scheduler/Scheduler.cpp b/src/scheduler/Scheduler.cpp index 7af3c62c5d0af33b01e596ecf4c91c35ab3e17b7..2e9dc034ef0bf2b0be2ee27c26b1995a2d0e4244 100644 --- a/src/scheduler/Scheduler.cpp +++ b/src/scheduler/Scheduler.cpp @@ -82,6 +82,8 @@ std::vector<Aidge::Scheduler::StaticSchedulingElement*> Aidge::Scheduler::genera // the requiredProducers list. std::set<std::shared_ptr<Node>> consumers = mGraphView->outputNodes(); std::set<std::shared_ptr<Node>> producers; + std::string level1Diagnostic; + std::string level2Diagnostic; do { // 2) From the current consumers list, check if any prior consumer node @@ -144,22 +146,37 @@ std::vector<Aidge::Scheduler::StaticSchedulingElement*> Aidge::Scheduler::genera // there is multiple successive priors for example). std::set<std::shared_ptr<Node>> runnableConsumers; Log::debug("Updated list of consumers:"); + level1Diagnostic.clear(); + level2Diagnostic.clear(); for (const auto& consumer : consumers) { summarizeConsumerState(consumer, namePtrTable.at(consumer)); // debug print bool isRunnable = true; for (IOIndex_t inputIdx = 0; inputIdx < consumer->nbInputs(); ++inputIdx) { AIDGE_LOG_CONTEXT("Consumer node {} input #{}", namePtrTable.at(consumer), inputIdx); + AvailableDataStatus status; if ((consumer->getOperator()->getNbConsumedData(inputIdx) + consumer->getOperator()->getNbRequiredData(inputIdx)) > - getNbAvailableData(consumer, inputIdx)) { + getNbAvailableData(consumer, inputIdx, status)) { Log::debug(" not runnable: C{} + R{} > P{} for input #{}", consumer->getOperator()->getNbConsumedData(inputIdx), consumer->getOperator()->getNbRequiredData(inputIdx), - getNbAvailableData(consumer, inputIdx), inputIdx); + getNbAvailableData(consumer, inputIdx, status), inputIdx); // not enough data to run isRunnable = false; + if (status == Scheduler::AvailableDataStatus::UpperNodeInputFound + || status == Scheduler::AvailableDataStatus::NotConnected) + { + level1Diagnostic += fmt::format("- No data available for node {} input #{}. {}\n", namePtrTable.at(consumer), inputIdx, fmt::styled(status, fmt::fg(fmt::color::red))); + } + else { + level2Diagnostic += fmt::format("- No data available for node {} input #{}. {}\n", namePtrTable.at(consumer), inputIdx, fmt::styled(status, fmt::fg(fmt::color::green))); + level2Diagnostic += fmt::format(" ↳ Available data is {}, but {} was already consummed and {} more is required.\n", + getNbAvailableData(consumer, inputIdx, status), + consumer->getOperator()->getNbConsumedData(inputIdx), + consumer->getOperator()->getNbRequiredData(inputIdx)); + } break; } } @@ -204,12 +221,13 @@ std::vector<Aidge::Scheduler::StaticSchedulingElement*> Aidge::Scheduler::genera for (IOIndex_t inputIdx = 0; inputIdx < consumer->nbInputs(); ++inputIdx) { if (consumer->inputCategory(inputIdx) == InputCategory::Data) { AIDGE_LOG_CONTEXT("Consumer node {} input #{}", namePtrTable.at(consumer), inputIdx); + AvailableDataStatus status; if (consumer->getOperator()->getNbConsumedData(inputIdx) < - getNbAvailableData(consumer, inputIdx)) { + getNbAvailableData(consumer, inputIdx, status)) { Log::debug(" still consumer: C{} < P{} for input #{}", consumer->getOperator()->getNbConsumedData(inputIdx), - getNbAvailableData(consumer, inputIdx), inputIdx); + getNbAvailableData(consumer, inputIdx, status), inputIdx); // there is still data to consume isStillConsumer = true; @@ -293,7 +311,15 @@ std::vector<Aidge::Scheduler::StaticSchedulingElement*> Aidge::Scheduler::genera std::back_inserter(consumersName), [&namePtrTable](auto val){ return namePtrTable.at(val); }); - Log::warn("Remaining consumers: {}. Possible dead-lock.", consumersName); + Log::warn("Remaining consumers: {}.", consumersName); + + Log::info("Reasons:"); + if (!level1Diagnostic.empty()) { + Log::info(level1Diagnostic); + } + else { + Log::info(level2Diagnostic); + } } return schedule; @@ -650,23 +676,27 @@ std::set<std::shared_ptr<Aidge::Node>> Aidge::Scheduler::getConsumers( return consumers; } -Aidge::Elts_t Aidge::Scheduler::getNbAvailableData(const std::shared_ptr<Node>& node, const IOIndex_t inputIdx) const { +Aidge::Elts_t Aidge::Scheduler::getNbAvailableData(const std::shared_ptr<Node>& node, const IOIndex_t inputIdx, AvailableDataStatus& status) const { const auto parent = node->inputs()[inputIdx]; if (parent.first) { // Parent is connected, everything if fine! + status = AvailableDataStatus::Connected; return parent.first->getOperator()->getNbProducedData(parent.second); } else if (std::shared_ptr<Node> upperNode = mUpperNode.lock()) { - // We are inside an upper operator (for instance a MetaOperator) - // We need to connect the "local" producer-consumer model to the upper - // one, by mapping local node inputs to the upper node inputs. + // We are inside an upper operator (for instance a MetaOperator). + // Check if the node input is also an upper node input... IOIndex_t upperInputIdx = 0; for (const auto& input : mGraphView->getOrderedInputs()) { if (input.first == node && input.second == inputIdx) { - // Current node is an input + // Current node is an input! + // We need to connect the "local" producer-consumer model to the upper + // one, by mapping local node inputs to the upper node inputs. + status = AvailableDataStatus::UpperNodeInputFound; const auto upperInput = upperNode->inputs()[upperInputIdx]; if (upperInput.first) { + status = AvailableDataStatus::UpperNodeInputConnected; return upperInput.first->getOperator()->getNbProducedData(upperInput.second); } } @@ -678,6 +708,7 @@ Aidge::Elts_t Aidge::Scheduler::getNbAvailableData(const std::shared_ptr<Node>& // - There is no data, it is assumed to be an optional input // - A valid tensor exists: if (node->getOperator()->getRawInput(inputIdx)) { + status = AvailableDataStatus::ValidTensor; // => This means data was fed manually to the input, without a Producer // In this case, we assume a single-use data (unlike a Producer, which // keep producing the data each time it is needed). @@ -685,6 +716,7 @@ Aidge::Elts_t Aidge::Scheduler::getNbAvailableData(const std::shared_ptr<Node>& return Elts_t::DataElts(std::static_pointer_cast<Tensor>(node->getOperator()->getRawInput(inputIdx))->size()); } + status = AvailableDataStatus::NotConnected; return Elts_t::NoneElts(); }