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