LCOV - code coverage report
Current view: top level - Src/Data - SpDataHandle.hpp (source / functions) Hit Total Coverage
Test: Coverage example Lines: 87 100 87.0 %
Date: 2021-12-02 17:21:05 Functions: 16 17 94.1 %

          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

Generated by: LCOV version 1.14