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 SPDATAHANDLE_HPP 6 : #define SPDATAHANDLE_HPP 7 : 8 : #include <vector> 9 : #include <cassert> 10 : #include <mutex> 11 : #include <atomic> 12 : 13 : #include "SpDependence.hpp" 14 : #include "Utils/SpUtils.hpp" 15 : #include "Data/SpDataDuplicator.hpp" 16 : #include "Utils/small_vector.hpp" 17 : 18 : //! This is a register data to apply the 19 : //! dependences on it. 20 : class SpDataHandle { 21 : //! Generic pointer to the data 22 : void* ptrToData; 23 : //! Original data type name 24 : const std::string datatypeName; 25 : 26 : //! All the dependences on the current data 27 : small_vector<SpDependence> dependencesOnData; 28 : 29 : //! To ensure safe access to the dependencesOnData vector 30 : mutable std::mutex mutexDependences; 31 : 32 : //! Current execution state in the dependences list 33 : long int currentDependenceCursor; 34 : 35 : public: 36 : template <class Datatype> 37 497 : explicit SpDataHandle(Datatype* inPtrToData) 38 : : ptrToData(inPtrToData), datatypeName(typeid(Datatype).name()), 39 497 : currentDependenceCursor(0){ 40 497 : SpDebugPrint() << "[SpDataHandle] Create handle for data " << inPtrToData << " of type " << datatypeName; 41 497 : } 42 : 43 : //! Cannot be copied or moved 44 : SpDataHandle(const SpDataHandle&) = delete; 45 : SpDataHandle(SpDataHandle&&) = delete; 46 : SpDataHandle& operator=(const SpDataHandle&) = delete; 47 : SpDataHandle& operator=(SpDataHandle&&) = delete; 48 : 49 215 : void *getRawPtr() { 50 215 : return ptrToData; 51 : } 52 : 53 : //! Convert to pointer to Datatype 54 : template <class Datatype> 55 16254 : std::remove_reference_t<Datatype>* castPtr(){ 56 16254 : return reinterpret_cast<std::remove_reference_t<Datatype>*>(ptrToData); 57 : } 58 : 59 : //! Add a new dependence to the data 60 : //! it returns the dependence position 61 2386 : long int addDependence(SpAbstractTask* inTask, const SpDataAccessMode inAccessMode){ 62 : // protect dependencesOnData 63 2386 : std::unique_lock<std::mutex> lock(mutexDependences); 64 : // If no dependence exist, or they have been all consumed already, or the new dependence 65 : // mode is difference from the last one 66 4275 : if(dependencesOnData.size() == 0 || currentDependenceCursor == static_cast<long int>(dependencesOnData.size()) 67 4275 : || inAccessMode != dependencesOnData.back().getMode()){ 68 : // Add a new one 69 1399 : dependencesOnData.emplace_back(inTask, inAccessMode); 70 : } 71 : // From here we now that the new dependence mode is the same as the last one 72 987 : else if(inAccessMode == SpDataAccessMode::WRITE){ 73 402 : assert(dependencesOnData.back().getMode() == SpDataAccessMode::WRITE); 74 : // Write cannot not be done concurently, so create a new dependence 75 402 : dependencesOnData.emplace_back(inTask, inAccessMode); 76 : } 77 585 : else if(inAccessMode == SpDataAccessMode::POTENTIAL_WRITE){ 78 5 : assert(dependencesOnData.back().getMode() == SpDataAccessMode::POTENTIAL_WRITE); 79 : // Write cannot not be done concurently, so create a new dependence 80 5 : dependencesOnData.emplace_back(inTask, inAccessMode); 81 : } 82 580 : else if(inAccessMode == SpDataAccessMode::PARALLEL_WRITE){ 83 19 : assert(dependencesOnData.back().getMode() == SpDataAccessMode::PARALLEL_WRITE); 84 : // append to the last dependence, because can be done concurrently 85 19 : dependencesOnData.back().addTaskForMultiple(inTask); 86 : } 87 561 : else if(inAccessMode == SpDataAccessMode::COMMUTATIVE_WRITE){ 88 11 : assert(dependencesOnData.back().getMode() == SpDataAccessMode::COMMUTATIVE_WRITE); 89 : // append to the last dependence, because can be done concurrently 90 11 : dependencesOnData.back().addTaskForMultiple(inTask); 91 : } 92 : else /*if(inAccessMode == SpDataAccessMode::READ && dependencesOnData.back().getMode() == SpDataAccessMode::READ)*/{ 93 550 : assert(inAccessMode == SpDataAccessMode::READ); 94 550 : assert(dependencesOnData.back().getMode() == SpDataAccessMode::READ); 95 : // append to the last dependence, because can be done concurrently 96 550 : dependencesOnData.back().addTaskForMultiple(inTask); 97 : } 98 : // Return the corresponding dependence idx 99 4772 : return static_cast<int>(dependencesOnData.size()-1); 100 : } 101 : 102 : //! Return the data mode access for dependence at position inDependenceIdx 103 8628 : SpDataAccessMode getModeByTask(const long int inDependenceIdx) const { 104 17258 : std::unique_lock<std::mutex> lock(mutexDependences); 105 8630 : assert(inDependenceIdx < static_cast<long int>(dependencesOnData.size())); 106 17260 : return dependencesOnData[inDependenceIdx].getMode(); 107 : } 108 : 109 : //! To know if a dependence is ready for a given task 110 6531 : bool canBeUsedByTask(const SpAbstractTask* inTask, const long int inDependenceIdx) const { 111 13064 : std::unique_lock<std::mutex> lock(mutexDependences); 112 6534 : assert(inDependenceIdx < static_cast<long int>(dependencesOnData.size())); 113 6534 : SpDebugPrint() << "[SpDataHandle] " << this << " canBeUsedByTask inDependenceIdx " << inDependenceIdx << " currentDependenceCursor " << currentDependenceCursor 114 6525 : << " mode " << SpModeToStr(dependencesOnData[inDependenceIdx].getMode()) << " address " << ptrToData; 115 6534 : assert(currentDependenceCursor <= inDependenceIdx); 116 : // Return true if current execution cursor is set to the dependence and that dependence return true to canBeUsedByTask 117 6534 : if(inDependenceIdx == currentDependenceCursor && dependencesOnData[inDependenceIdx].canBeUsedByTask(inTask)){ 118 5287 : return true; 119 : } 120 1246 : return false; 121 : } 122 : 123 : //! Mark the dependence as used by the given task 124 : //! canBeUsedByTask must be true for the same parameter 125 2386 : void setUsedByTask(SpAbstractTask* inTask, const long int inDependenceIdx){ 126 2386 : assert(canBeUsedByTask(inTask, inDependenceIdx)); 127 2386 : SpDebugPrint() << "[SpDataHandle] " << this << " setUsedByTask inDependenceIdx " << inDependenceIdx << " currentDependenceCursor " << currentDependenceCursor 128 2386 : << " mode " << SpModeToStr(dependencesOnData[inDependenceIdx].getMode()) << " address " << ptrToData; 129 4772 : std::unique_lock<std::mutex> lock(mutexDependences); 130 2386 : dependencesOnData[inDependenceIdx].setUsedByTask(inTask); 131 2386 : } 132 : 133 : //! Release the dependence, the dependence must have been set to used by 134 : //! the input task. 135 : //! The method returns true if there still are any unfulfilled memory access 136 : //! requests on the data handle. 137 2386 : bool releaseByTask(SpAbstractTask* inTask, const long int inDependenceIdx){ 138 4772 : std::unique_lock<std::mutex> lock(mutexDependences); 139 2385 : assert(inDependenceIdx == currentDependenceCursor); 140 2385 : SpDebugPrint() << "[SpDataHandle] " << this << " releaseByTask " << inTask << " inDependenceIdx " << inDependenceIdx << " address " << ptrToData; 141 : 142 : // Release memory access request on dependency slot. As a return value we get a boolean flag 143 : // telling us if there are still any unfulfilled memory access requests registered on the dependency slot. 144 2386 : const bool thereStillAreUnfulfilledMemoryAccessRequestsOnTheDependencySlot = dependencesOnData[inDependenceIdx].releaseByTask(inTask); 145 : 146 : // Have all memory accesses registered on the dependency slot been performed on the data ? 147 2385 : if(dependencesOnData[inDependenceIdx].isOver()){ 148 1806 : assert(thereStillAreUnfulfilledMemoryAccessRequestsOnTheDependencySlot == false); 149 : 150 : // Move on to the next dependency slot 151 1806 : currentDependenceCursor += 1; 152 : 153 : // Mask as available 154 1806 : SpDebugPrint() << "[SpDataHandle] releaseByTask isOver dependencesOnData true currentDependenceCursor " << currentDependenceCursor << " dependencesOnData.size() " << dependencesOnData.size(); 155 : 156 : // Return true if we still have not fulfilled all memory access requests on the data handle 157 1806 : return (currentDependenceCursor != static_cast<long int>(dependencesOnData.size())); 158 : 159 : } else { // There still are unfulfilled or unreleased memory access requests registered on the dependency slot 160 579 : SpDebugPrint() << "[SpDataHandle] releaseByTask isAvailable dependencesOnData true currentDependenceCursor " 161 580 : << currentDependenceCursor << " dependencesOnData.size() " << dependencesOnData.size(); 162 580 : return thereStillAreUnfulfilledMemoryAccessRequestsOnTheDependencySlot; 163 : } 164 : 165 : // All memory access requests on the data handle have been released. 166 : return false; 167 : } 168 : 169 : //! Get the potential ready tasks on the current cursor 170 1101 : void fillCurrentTaskList(small_vector_base<SpAbstractTask*>* potentialReady) const { 171 2202 : std::unique_lock<std::mutex> lock(mutexDependences); 172 1101 : if(currentDependenceCursor != static_cast<long int>(dependencesOnData.size())) { 173 1101 : dependencesOnData[currentDependenceCursor].fillWithListOfPotentiallyReadyTasks(potentialReady); 174 : } 175 1101 : } 176 : 177 : //! Get the list of tasks that depend on dependence at idx afterIdx 178 3225 : void getDependences(small_vector_base<SpAbstractTask*>* dependences, const long int afterIdx) const { 179 6450 : std::unique_lock<std::mutex> lock(mutexDependences); 180 3225 : if(afterIdx != static_cast<long int>(dependencesOnData.size()-1)){ 181 2289 : if(dependencesOnData[afterIdx].getMode() != SpDataAccessMode::WRITE 182 2289 : && dependencesOnData[afterIdx].getMode() != SpDataAccessMode::POTENTIAL_WRITE){ 183 923 : long int skipConcatDeps = afterIdx; 184 938 : while(skipConcatDeps != static_cast<long int>(dependencesOnData.size()-1) 185 938 : && dependencesOnData[skipConcatDeps].getMode() == dependencesOnData[skipConcatDeps+1].getMode()){ 186 15 : skipConcatDeps += 1; 187 : } 188 923 : if(skipConcatDeps != static_cast<long int>(dependencesOnData.size()-1)){ 189 915 : dependencesOnData[skipConcatDeps+1].fillWithTaskList(dependences); 190 : } 191 : } 192 : else{ 193 1366 : long int skipConcatDeps = afterIdx+1; 194 1366 : bool isCommutativeAccess = dependencesOnData[skipConcatDeps].getMode() != SpDataAccessMode::WRITE 195 1366 : && dependencesOnData[skipConcatDeps].getMode() != SpDataAccessMode::POTENTIAL_WRITE; 196 8 : do { 197 1374 : dependencesOnData[skipConcatDeps].fillWithTaskList(dependences); 198 1374 : skipConcatDeps += 1; 199 454 : }while(isCommutativeAccess && skipConcatDeps != static_cast<long int>(dependencesOnData.size()) 200 1828 : && dependencesOnData[skipConcatDeps-1].getMode() == dependencesOnData[skipConcatDeps].getMode()); 201 : } 202 : } 203 3225 : } 204 : 205 : //! Get the list of tasks that the given task depend on 206 0 : void getPredecessors(small_vector_base<SpAbstractTask*>* dependences, const long int beforeIdx) const { 207 0 : std::unique_lock<std::mutex> lock(mutexDependences); 208 0 : if(beforeIdx != 0){ 209 0 : if(dependencesOnData[beforeIdx].getMode() != SpDataAccessMode::WRITE 210 0 : && dependencesOnData[beforeIdx].getMode() != SpDataAccessMode::POTENTIAL_WRITE){ 211 0 : long int skipConcatDeps = beforeIdx-1; 212 0 : while(skipConcatDeps != -1 213 0 : && dependencesOnData[skipConcatDeps].getMode() == dependencesOnData[skipConcatDeps+1].getMode()){ 214 0 : skipConcatDeps -= 1; 215 : } 216 0 : if(skipConcatDeps != -1){ 217 0 : dependencesOnData[skipConcatDeps].fillWithTaskList(dependences); 218 : } 219 : } 220 : else{ 221 0 : dependencesOnData[beforeIdx-1].fillWithTaskList(dependences); 222 : } 223 : } 224 0 : } 225 : 226 : }; 227 : 228 : // For variadic expansion 229 : template <class NewType> 230 : auto SpDataHandleToObject(SpDataHandle* inHandle) -> typename std::remove_reference<NewType>::type::HandleType { 231 : return *inHandle->template castPtr<typename std::remove_reference_t<NewType>::HandleType>(); 232 : } 233 : 234 : #endif