LCOV - code coverage report
Current view: top level - Src/Data - SpDependence.hpp (source / functions) Hit Total Coverage
Test: Coverage example Lines: 79 79 100.0 %
Date: 2021-12-02 17:21:05 Functions: 10 10 100.0 %

          Line data    Source code
       1             : ///////////////////////////////////////////////////////////////////////////
       2             : // Spetabaru - Berenger Bramas MPCDF - 2017
       3             : // Under LGPL Licence, please you must read the LICENCE file.
       4             : ///////////////////////////////////////////////////////////////////////////
       5             : #ifndef SPDEPENDENCE_HPP
       6             : #define SPDEPENDENCE_HPP
       7             : 
       8             : 
       9             : #include <vector>
      10             : #include <cassert>
      11             : #include <algorithm>
      12             : 
      13             : #include "Data/SpDataAccessMode.hpp"
      14             : #include "Utils/small_vector.hpp"
      15             : 
      16             : // Reference
      17             : class SpAbstractTask;
      18             : 
      19             : //! This is the relation between the data and the task
      20             : class SpDependence{
      21             :     //! The access mode of the tasks on the data
      22             :     SpDataAccessMode accessMode;
      23             : 
      24             :     //! Id of task if it is a write mode
      25             :     SpAbstractTask* idTaskWrite;
      26             :     //! Ids of all tasks for all other modes (that are concurent safe)
      27             :     small_vector<SpAbstractTask*> idTasksMultiple;
      28             : 
      29             :     //! Number of tasks that use and have used the data
      30             :     long int nbTasksInUsed;
      31             :     //! Number of tasks that release the data
      32             :     long int nbTasksReleased;
      33             : 
      34             : public:
      35        1806 :     explicit SpDependence(SpAbstractTask* inFirstTaskId, const SpDataAccessMode inMode)
      36        1806 :         : accessMode(inMode) ,idTaskWrite(nullptr),  nbTasksInUsed(0) , nbTasksReleased(0){
      37        1806 :         SpDebugPrint() << "[SpDependence] => " << inFirstTaskId << " mode " << SpModeToStr(inMode);
      38        1806 :         if(accessMode == SpDataAccessMode::WRITE || accessMode == SpDataAccessMode::POTENTIAL_WRITE){
      39        1307 :             idTaskWrite = inFirstTaskId;
      40             :         }
      41             :         else{
      42         499 :             idTasksMultiple.push_back(inFirstTaskId);
      43             :         }
      44        1806 :     }
      45             : 
      46             :     //! Can be copied and moved
      47             :     SpDependence(const SpDependence&) = default;
      48          64 :     SpDependence(SpDependence&&) = default;
      49             :     SpDependence& operator=(const SpDependence&) = default;
      50             :     SpDependence& operator=(SpDependence&&) = default;
      51             : 
      52             :     //! The access mode
      53       27978 :     SpDataAccessMode getMode() const {
      54       27978 :         return accessMode;
      55             :     }
      56             : 
      57             :     //! Add a task to the list of users
      58         580 :     void addTaskForMultiple(SpAbstractTask* inOtherTaskId){
      59         580 :         assert(accessMode != SpDataAccessMode::WRITE && accessMode != SpDataAccessMode::POTENTIAL_WRITE);
      60         580 :         idTasksMultiple.push_back(inOtherTaskId);
      61         580 :     }
      62             : 
      63             :     //! To know if a task can use the current data dep
      64             :     //! the task must be valid and registered
      65        7719 :     bool canBeUsedByTask(const SpAbstractTask* useTaskId) const {
      66        7719 :         SpDebugPrint() << "[SpDependence]canBeUsedByTask " << useTaskId;
      67        7717 :         if(accessMode == SpDataAccessMode::WRITE || accessMode == SpDataAccessMode::POTENTIAL_WRITE){
      68             :             // If it is write
      69        4248 :             SpDebugPrint() << "[SpDependence]Is in write, next task is " << idTaskWrite;
      70             :             // Must not have been already used
      71        4249 :             assert(nbTasksInUsed == 0);
      72             :             // The task id must be the one register
      73        4250 :             assert(idTaskWrite == useTaskId);
      74        4250 :             return true;
      75             :         }
      76             :         // If it is commutative
      77        3469 :         else if(accessMode == SpDataAccessMode::COMMUTATIVE_WRITE){
      78             :             // The number of already user tasks must be less than the number of register tasks
      79          88 :             assert(nbTasksInUsed < static_cast<long int>(idTasksMultiple.size()));
      80             :             // In commutative mode there must be only 0 or 1 usage at a time
      81          88 :             assert(nbTasksInUsed-nbTasksReleased >= 0 && nbTasksInUsed-nbTasksReleased <= 1);
      82             :             // The given task must exist in the list
      83          88 :             assert(std::find(idTasksMultiple.begin(), idTasksMultiple.end(), useTaskId) != idTasksMultiple.end());
      84          88 :             SpDebugPrint() << "[SpDependence]Is in commute, test existence among " << idTasksMultiple.size() << " tasks";
      85          88 :             SpDebugPrint() << "[SpDependence]Found " << (std::find(idTasksMultiple.begin(), idTasksMultiple.end(), useTaskId) != idTasksMultiple.end());
      86          88 :             SpDebugPrint() << "[SpDependence]nbTasksInUsed " << nbTasksInUsed << " nbTasksReleased " << nbTasksReleased;
      87             :             // Return true if no task uses the data
      88          88 :             return nbTasksInUsed-nbTasksReleased == 0;
      89             :         }
      90             :         // If it is not write and not commutative
      91             :         else {
      92             :             // The number of already user tasks must be less than the number of register tasks
      93        3381 :             assert(nbTasksInUsed < static_cast<long int>(idTasksMultiple.size()));
      94             :             // The given task must exist in the list
      95        3381 :             assert(std::find(idTasksMultiple.begin(), idTasksMultiple.end(), useTaskId) != idTasksMultiple.end());
      96        3381 :             SpDebugPrint() << "[SpDependence]Is not in commutative and not in write, test existence among " << idTasksMultiple.size() << " tasks";
      97        3382 :             SpDebugPrint() << "[SpDependence]Found " << (std::find(idTasksMultiple.begin(), idTasksMultiple.end(), useTaskId) != idTasksMultiple.end());
      98        3382 :             return true;
      99             :         }
     100             :     }
     101             : 
     102             :     //! Mark the dependence as used
     103        2386 :     void setUsedByTask([[maybe_unused]] SpAbstractTask* useTaskId){
     104        2386 :         assert(canBeUsedByTask(useTaskId) == true);
     105        2386 :         nbTasksInUsed += 1;
     106        2386 :     }
     107             :     
     108        2289 :     void fillWithTaskList(small_vector_base<SpAbstractTask*>* potentialReady) const {
     109        2289 :         if(accessMode == SpDataAccessMode::WRITE || accessMode == SpDataAccessMode::POTENTIAL_WRITE){
     110        1835 :             potentialReady->push_back(idTaskWrite);
     111             :         }
     112             :         else{
     113         454 :             potentialReady->reserve(potentialReady->size() + idTasksMultiple.size());
     114        1328 :             for(auto&& ptr : idTasksMultiple){
     115         874 :                 potentialReady->push_back(ptr);
     116             :             }
     117             :         }
     118        2289 :     }
     119             : 
     120             :     //! Copy all the tasks related to the dependence into the given vector
     121        1101 :     void fillWithListOfPotentiallyReadyTasks(small_vector_base<SpAbstractTask*>* potentialReady) const {
     122        1101 :         if(accessMode == SpDataAccessMode::WRITE || accessMode == SpDataAccessMode::POTENTIAL_WRITE){
     123        1655 :             if(idTaskWrite->isState(SpTaskState::WAITING_TO_BE_READY)) {
     124         821 :                 potentialReady->push_back(idTaskWrite);
     125             :             }
     126             :         }
     127             :         else{
     128         267 :             potentialReady->reserve(potentialReady->size() + idTasksMultiple.size());
     129         822 :             for(auto&& ptr : idTasksMultiple){
     130         555 :                 if(ptr->isState(SpTaskState::WAITING_TO_BE_READY)) {
     131         504 :                     potentialReady->push_back(ptr);
     132             :                 }
     133             :             }
     134             :         }
     135        1101 :     }
     136             : 
     137             :     //! Marks the dependence as release by the given task
     138             :     //! Must be called after setUsedByTask
     139        2386 :     bool releaseByTask([[maybe_unused]] SpAbstractTask* useTaskId){
     140        2386 :         if(accessMode == SpDataAccessMode::WRITE || accessMode == SpDataAccessMode::POTENTIAL_WRITE){
     141        1307 :             assert(nbTasksReleased == 0);
     142        1307 :             assert(nbTasksInUsed == 1);
     143        1307 :             assert(idTaskWrite == useTaskId);
     144        1307 :             nbTasksReleased += 1;
     145             :             // The dependency slot can only be used by the task currently releasing the
     146             :             // the dependency slot, since write and maybe-write accesses are exclusive.
     147             :             // After this dependency slot has been released it can't be reused by another task, 
     148             :             // that's why we are returning false in this case, basically saying this dependency slot 
     149             :             // is not available for any further memory access requests and we should move onto the next dependency slot.
     150        1307 :             return false;
     151             :         }
     152             :         else{
     153        1079 :             assert(std::find(idTasksMultiple.begin(), idTasksMultiple.end(), useTaskId) != idTasksMultiple.end());
     154        1078 :             assert(0 < nbTasksInUsed);
     155        1078 :             assert(nbTasksReleased < nbTasksInUsed);
     156        1078 :             nbTasksReleased += 1;
     157        1078 :             assert(nbTasksReleased <= int(idTasksMultiple.size()));
     158        1078 :             if(accessMode == SpDataAccessMode::COMMUTATIVE_WRITE){
     159          14 :                 assert(nbTasksReleased == nbTasksInUsed);
     160             :                 // Return true if there still are any unfulfilled commutative write access requests
     161             :                 // on the data handle. So basically, by returning true in this case we notify the caller
     162             :                 // that the data handle is now available for another task to request its
     163             :                 // commutative write access onto. If all commutative write access requests on the data handle have
     164             :                 // been fulfilled we return false, basically saying this dependency slot
     165             :                 // is not available for any further memory accesses and we should move onto the next dependency slot.
     166          14 :                 return (nbTasksReleased != int(idTasksMultiple.size()));
     167             :             }
     168             :             else{
     169             :                 // Tasks that want to read can read however they please,
     170             :                 // they don't have to wait for a previous task reading from the data handle
     171             :                 // to give them permission to access to the data handle through the dependency slot.
     172             :                 // So basically we are saying read requests are already "fulfilled" by default (note that this does not
     173             :                 // necessarily mean that the read request has already been released from the dependency slot).
     174        1064 :                 return false;
     175             :             }
     176             :         }
     177             :     }
     178             : 
     179             :     //! Return true if all tasks have used the dependence (and finished to use)
     180             :     //! Must be called after release
     181        2385 :     bool isOver() const{
     182        2385 :         if(accessMode == SpDataAccessMode::WRITE || accessMode == SpDataAccessMode::POTENTIAL_WRITE){
     183        1307 :             assert(nbTasksReleased == 1);
     184        1307 :             assert(nbTasksInUsed == 1);
     185        1307 :             return true;
     186             :         }
     187             :         else{
     188        1078 :             assert(0 < nbTasksInUsed);
     189        1078 :             assert(0 < nbTasksReleased);
     190        1078 :             assert(nbTasksReleased <= nbTasksInUsed);
     191        1078 :             return (nbTasksReleased == static_cast<long int>(idTasksMultiple.size()));
     192             :         }
     193             :     }
     194             : 
     195             :     //! If not over, return true if can be used by another task now
     196             :     //! Must be called after release
     197             :     bool isAvailable() const {
     198             :         assert(isOver() == false);
     199             :         assert(nbTasksReleased <= nbTasksInUsed);
     200             :         if(accessMode == SpDataAccessMode::WRITE || accessMode == SpDataAccessMode::POTENTIAL_WRITE){
     201             :             return nbTasksInUsed == 0;
     202             :         }
     203             :         else if(accessMode == SpDataAccessMode::COMMUTATIVE_WRITE){
     204             :             return nbTasksInUsed == nbTasksReleased && (nbTasksReleased < static_cast<long int>(idTasksMultiple.size()));
     205             :         }
     206             :         else{
     207             :             return (nbTasksInUsed < static_cast<long int>(idTasksMultiple.size()));
     208             :         }
     209             :     }
     210             : };
     211             : 
     212             : #endif

Generated by: LCOV version 1.14