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 SPTASK_HPP
6 : #define SPTASK_HPP
7 :
8 : #include <tuple>
9 : #include <unordered_map>
10 : #include <typeinfo>
11 :
12 : #include "SpAbstractTask.hpp"
13 : #include "Data/SpDataHandle.hpp"
14 : #include "Utils/SpUtils.hpp"
15 : #include "Utils/small_vector.hpp"
16 :
17 : #ifdef __GNUG__
18 : #include <cxxabi.h>
19 : #endif
20 :
21 : class SpAbstractTaskGraph;
22 :
23 : template <class RetType, class DataDependencyTupleTy, class CallableTupleTy>
24 : class SpTask : public SpAbstractTaskWithReturn<RetType> {
25 : using Parent = SpAbstractTask;
26 : using ParentReturn = SpAbstractTaskWithReturn<RetType>;
27 :
28 : //! Number of parameters in the task function prototype
29 : static const long int NbParams = std::tuple_size<DataDependencyTupleTy>::value;
30 :
31 : //! Internal value for undefined dependences
32 18770 : static constexpr long int UndefinedKey(){
33 18770 : return -1;
34 : }
35 :
36 : //! Data handles
37 : std::array<SpDataHandle*,NbParams> dataHandles;
38 : //! Dependences' keys for each handle
39 : std::array<long int,NbParams> dataHandlesKeys;
40 :
41 : //! Extra handles
42 : small_vector<SpDataHandle*> dataHandlesExtra;
43 : //! Extra handles's dependences keys
44 : small_vector<long int> dataHandlesKeysExtra;
45 :
46 : //! DataDependency objects
47 : DataDependencyTupleTy tupleParams;
48 :
49 : //! Callables
50 : CallableTupleTy callables;
51 :
52 : ///////////////////////////////////////////////////////////////////////////////
53 : /// Methods to call the task function with a conversion from handle to data
54 : ///////////////////////////////////////////////////////////////////////////////
55 :
56 : //! Expand the tuple with the index and call getView
57 : template <class CallableTy, std::size_t... Is>
58 875 : static RetType SpTaskCoreWrapper(CallableTy &callable, DataDependencyTupleTy &dataDep, std::index_sequence<Is...>){
59 875 : return std::invoke(callable.getCallableRef(), std::get<Is>(dataDep).getView()...);
60 : }
61 :
62 : //! Dispatch use if RetType is not void (will set parent value with the return from function)
63 : template <class SRetType, class CallableTy>
64 126 : static void executeCore(SpAbstractTaskWithReturn<SRetType>* taskObject, CallableTy &callable, DataDependencyTupleTy &dataDep) {
65 126 : taskObject->setValue(SpTaskCoreWrapper(callable, dataDep, std::make_index_sequence<std::tuple_size<DataDependencyTupleTy>::value>{}));
66 126 : }
67 :
68 : //! Dispatch use if RetType is void (will not set parent value with the return from function)
69 : template <class CallableTy>
70 749 : static void executeCore(SpAbstractTaskWithReturn<void>* /*taskObject*/, CallableTy &callable, DataDependencyTupleTy &dataDep) {
71 749 : SpTaskCoreWrapper(callable, dataDep, std::make_index_sequence<NbParams>{});
72 749 : }
73 :
74 : //! Called by parent abstract task class
75 875 : void executeCore([[maybe_unused]] SpCallableType ct) final {
76 875 : executeCore(this, std::get<0>(callables), tupleParams);
77 875 : }
78 :
79 : public:
80 : //! Constructor from a task function
81 : template <typename... T>
82 1073 : explicit SpTask(SpAbstractTaskGraph* const inAtg, const SpTaskActivation initialActivationState,
83 : const SpPriority &inPriority,
84 : DataDependencyTupleTy &&inDataDepTuple,
85 : CallableTupleTy &&inCallableTuple, T... t)
86 : : SpAbstractTaskWithReturn<RetType>(inAtg, initialActivationState, inPriority),
87 : tupleParams(inDataDepTuple),
88 1073 : callables(std::move(inCallableTuple)) {
89 : ((void) t, ...);
90 1073 : std::fill_n(dataHandles.data(), NbParams, nullptr);
91 1073 : std::fill_n(dataHandlesKeys.data(), NbParams, UndefinedKey());
92 :
93 : #ifdef __GNUG__
94 : // if GCC then we ask for a clean type as default task name
95 : int status;
96 1073 : char *demangledName = abi::__cxa_demangle(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name(), 0, 0, &status);
97 1073 : if(status == 0){
98 1073 : assert(demangledName);
99 1073 : Parent::setTaskName(demangledName);
100 : }
101 : else{
102 0 : Parent::setTaskName(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name());
103 : }
104 1073 : free(demangledName);
105 : #else
106 : Parent::setTaskName(typeid(std::remove_reference_t<decltype(std::get<0>(callables))>).name());
107 : #endif
108 1073 : }
109 :
110 1073 : DataDependencyTupleTy& getDataDependencyTupleRef() {
111 1073 : return tupleParams;
112 : }
113 :
114 : //! Set the dependence at position HandleIdx in the prototype
115 : template <long int HandleIdx>
116 1872 : void setDataHandle(SpDataHandle* inData, const long int inHandleKey){
117 : static_assert(HandleIdx < NbParams, "Cannot set more handle the NbParams");
118 1872 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " setDataHandle " << HandleIdx << " at " << inData;
119 1872 : assert(dataHandles[HandleIdx] == nullptr);
120 1872 : dataHandles[HandleIdx] = inData;
121 1872 : dataHandlesKeys[HandleIdx] = inHandleKey;
122 1872 : }
123 :
124 : //! Add a dependence (extra), can be done at runtime
125 : template <long int HandleIdx>
126 514 : void addDataHandleExtra(SpDataHandle* inData, const long int inHandleKey){
127 514 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " addDataHandleExtra at " << inData;
128 514 : assert(dataHandles[HandleIdx] != nullptr);
129 514 : dataHandlesExtra.emplace_back(inData);
130 514 : dataHandlesKeysExtra.emplace_back(inHandleKey);
131 514 : }
132 :
133 :
134 : //! For speculation in order to update the memory pointer
135 : //! that will be used when the function will be called
136 : template <long int HandleIdx, class ParamType>
137 290 : void updatePtr(const long int position, ParamType* ptr){
138 290 : std::get<HandleIdx>(tupleParams).updatePtr(position, ptr);
139 290 : }
140 :
141 : //! The number of parameters in the task prototype
142 0 : long int getNbParams() final {
143 0 : return NbParams;
144 : }
145 :
146 : //! Return true if all dependences are ready
147 : //! Dependence are not use after this call, it is just a check
148 2319 : bool dependencesAreReady() const final{
149 4618 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
150 3470 : assert(dataHandles[idxDeps]);
151 3470 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
152 3470 : if(dataHandles[idxDeps]->canBeUsedByTask(this, dataHandlesKeys[idxDeps]) == false){
153 1171 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady FALSE, at index " << idxDeps << " " << dataHandles[idxDeps]
154 1171 : << " address " << dataHandles[idxDeps]->template castPtr<int>() << "\n";
155 1171 : return false;
156 : }
157 2299 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady TRUE, at index " << idxDeps << " " << dataHandles[idxDeps] << "\n";
158 : }
159 1148 : if(dataHandlesExtra.size()){
160 794 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
161 678 : if(dataHandlesExtra[idxDeps]->canBeUsedByTask(this, dataHandlesKeysExtra[idxDeps]) == false){
162 75 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady FALSE, at index extra " << idxDeps << " " << dataHandlesExtra[idxDeps]
163 75 : << " address " << dataHandlesExtra[idxDeps]->template castPtr<int>() << "\n";
164 75 : return false;
165 : }
166 : }
167 : }
168 1073 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " dependencesAreReady TRUE";
169 1073 : return true;
170 : }
171 :
172 : //! Tell the dependences that they are used by the current task
173 1073 : void useDependences(std::unordered_set<SpDataHandle*>* exceptionList) final{
174 1073 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " useDependences";
175 2944 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
176 1872 : assert(dataHandles[idxDeps]);
177 1872 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
178 1872 : if((exceptionList == nullptr || exceptionList->find(dataHandles[idxDeps]) == exceptionList->end())){
179 1872 : dataHandles[idxDeps]->setUsedByTask(this, dataHandlesKeys[idxDeps]);
180 3743 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " useDependences at index " << idxDeps << " " << dataHandles[idxDeps]
181 1872 : << " address " << dataHandles[idxDeps]->template castPtr<int>() << "\n";
182 : }
183 : }
184 1072 : if(dataHandlesExtra.size()){
185 630 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
186 514 : if( exceptionList == nullptr || exceptionList->find(dataHandles[idxDeps]) == exceptionList->end()){
187 514 : dataHandlesExtra[idxDeps]->setUsedByTask(this, dataHandlesKeysExtra[idxDeps]);
188 1028 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " useDependences at index " << idxDeps << " " << dataHandlesExtra[idxDeps]
189 514 : << " address " << dataHandlesExtra[idxDeps]->template castPtr<int>() << "\n";
190 : }
191 : }
192 : }
193 1072 : }
194 :
195 : //! Tell the dependences that they are no longer use by the current task,
196 : //! and fill with potential candidate.
197 : //! The algorithm will first release all the dependences, such that
198 : //! when filling with potentialReady we are able to find tasks that have more
199 : //! than one dependence in common with the current task.
200 1073 : void releaseDependences(small_vector_base<SpAbstractTask*>* potentialReady) final {
201 : // Arrays of boolean flags indicating for each released dependency whether the "after release" pointed to
202 : // dependency slot in the corresponding data handle contains any unfullfilled memory access
203 : // requests.
204 : std::array<bool, NbParams> curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandles;
205 2146 : small_vector<bool> curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandlesExtra(dataHandlesExtra.size());
206 :
207 1073 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " releaseDependences";
208 2945 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
209 1872 : assert(dataHandles[idxDeps]);
210 1872 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
211 1872 : curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandles[idxDeps] = dataHandles[idxDeps]->releaseByTask(this, dataHandlesKeys[idxDeps]);
212 3744 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " releaseDependences FALSE, at index " << idxDeps << " " << dataHandles[idxDeps]
213 1871 : << " address " << dataHandles[idxDeps]->template castPtr<int>() << "\n";
214 : }
215 1073 : if(dataHandlesExtra.size()){
216 630 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
217 514 : curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandlesExtra[idxDeps] = dataHandlesExtra[idxDeps]->releaseByTask(this, dataHandlesKeysExtra[idxDeps]);
218 1027 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " releaseDependences FALSE, at index " << idxDeps << " " << dataHandlesExtra[idxDeps]
219 514 : << " address " << dataHandlesExtra[idxDeps]->template castPtr<int>();
220 : }
221 : }
222 :
223 2945 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
224 1872 : assert(dataHandles[idxDeps]);
225 1872 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
226 1872 : if(curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandles[idxDeps]){
227 1063 : dataHandles[idxDeps]->fillCurrentTaskList(potentialReady);
228 : }
229 : }
230 1073 : if(dataHandlesExtra.size()){
231 630 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
232 514 : if(curPoinToDepSlotContainsAnyUnfulMemoryAccReqDataHandlesExtra[idxDeps]){
233 38 : dataHandlesExtra[idxDeps]->fillCurrentTaskList(potentialReady);
234 : }
235 : }
236 : }
237 1073 : }
238 :
239 : //! Fill with the tasks that depend on the current task
240 1370 : virtual void getDependences(small_vector_base<SpAbstractTask*>* allDeps) const final {
241 3847 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
242 2477 : assert(dataHandles[idxDeps]);
243 2477 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
244 2477 : dataHandles[idxDeps]->getDependences(allDeps, dataHandlesKeys[idxDeps]);
245 : }
246 1370 : if(dataHandlesExtra.size()){
247 932 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
248 748 : dataHandlesExtra[idxDeps]->getDependences(allDeps, dataHandlesKeysExtra[idxDeps]);
249 : }
250 : }
251 1370 : }
252 :
253 : //! Fill with the tasks that current task depend on
254 0 : virtual void getPredecessors(small_vector_base<SpAbstractTask*>* allPredecessors) const final {
255 0 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
256 0 : assert(dataHandles[idxDeps]);
257 0 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
258 0 : dataHandles[idxDeps]->getPredecessors(allPredecessors, dataHandlesKeys[idxDeps]);
259 : }
260 0 : if(dataHandlesExtra.size()){
261 0 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
262 0 : dataHandlesExtra[idxDeps]->getPredecessors(allPredecessors, dataHandlesKeysExtra[idxDeps]);
263 : }
264 : }
265 0 : }
266 :
267 : //! Return true if at least one dependence is in mode inMode
268 2319 : bool hasMode(const SpDataAccessMode inMode) const final{
269 2319 : SpDebugPrint() << "SpTask -- " << Parent::getId() << " hasMode";
270 6520 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
271 4263 : assert(dataHandles[idxDeps]);
272 4263 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
273 4263 : if(dataHandles[idxDeps]->getModeByTask(dataHandlesKeys[idxDeps]) == inMode){
274 62 : return true;
275 : }
276 : }
277 2257 : if(dataHandlesExtra.size()){
278 2195 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
279 1766 : if(dataHandlesExtra[idxDeps]->getModeByTask(dataHandlesKeysExtra[idxDeps]) == inMode){
280 0 : return true;
281 : }
282 : }
283 : }
284 2257 : return false;
285 : }
286 :
287 0 : small_vector<std::pair<SpDataHandle*,SpDataAccessMode>> getDataHandles() const final{
288 0 : small_vector<std::pair<SpDataHandle*,SpDataAccessMode>> data;
289 0 : data.reserve(NbParams + dataHandlesExtra.size());
290 :
291 0 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
292 0 : assert(dataHandles[idxDeps]);
293 0 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
294 0 : data.emplace_back(dataHandles[idxDeps], dataHandles[idxDeps]->getModeByTask(dataHandlesKeys[idxDeps]));
295 : }
296 0 : if(dataHandlesExtra.size()){
297 0 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
298 0 : data.emplace_back(dataHandlesExtra[idxDeps], dataHandlesExtra[idxDeps]->getModeByTask(dataHandlesKeysExtra[idxDeps]));
299 : }
300 : }
301 0 : return data;
302 : }
303 :
304 : //! Check that dependences are correct
305 : //! this is a simple check to ensure that no dependences are
306 : //! incompatible mode (like having a variable both in read and write)
307 1073 : bool areDepsCorrect() const{
308 2146 : std::unordered_map<SpDataHandle*,SpDataAccessMode> existingDeps;
309 :
310 2945 : for(long int idxDeps = 0 ; idxDeps < NbParams ; ++idxDeps){
311 1872 : assert(dataHandles[idxDeps]);
312 1872 : assert(dataHandlesKeys[idxDeps] != UndefinedKey());
313 1872 : auto testDep = existingDeps.find(dataHandles[idxDeps]);
314 1872 : const SpDataAccessMode testMode = dataHandles[idxDeps]->getModeByTask(dataHandlesKeys[idxDeps]);
315 1872 : if(testDep == existingDeps.end()){
316 1872 : existingDeps[dataHandles[idxDeps]] = testMode;
317 : }
318 0 : else if(testMode == SpDataAccessMode::READ && testDep->second == SpDataAccessMode::READ){
319 : }
320 0 : else if(testMode == SpDataAccessMode::PARALLEL_WRITE && testDep->second == SpDataAccessMode::PARALLEL_WRITE){
321 : }
322 : else{
323 0 : return false;
324 : }
325 1872 : existingDeps[dataHandles[idxDeps]] = testMode;
326 : }
327 :
328 1073 : if(dataHandlesExtra.size()){
329 630 : for(long int idxDeps = 0 ; idxDeps < static_cast<long int>(dataHandlesExtra.size()) ; ++idxDeps){
330 514 : auto testDep = existingDeps.find(dataHandlesExtra[idxDeps]);
331 514 : const SpDataAccessMode testMode = dataHandlesExtra[idxDeps]->getModeByTask(dataHandlesKeysExtra[idxDeps]);
332 514 : if(testDep == existingDeps.end()){
333 514 : existingDeps[dataHandlesExtra[idxDeps]] = testMode;
334 : }
335 0 : else if(testMode == SpDataAccessMode::READ && testDep->second == SpDataAccessMode::READ){
336 : }
337 0 : else if(testMode == SpDataAccessMode::PARALLEL_WRITE && testDep->second == SpDataAccessMode::PARALLEL_WRITE){
338 : }
339 : else{
340 0 : return false;
341 : }
342 514 : existingDeps[dataHandlesExtra[idxDeps]] = testMode;
343 : }
344 : }
345 :
346 1073 : return true;
347 : }
348 :
349 0 : bool hasCallableOfType([[maybe_unused]] const SpCallableType sct) const override final {
350 : if constexpr(std::tuple_size_v<CallableTupleTy> == 1) {
351 0 : return std::tuple_element_t<0, CallableTupleTy>::callable_type == sct;
352 : } else {
353 : return true;
354 : }
355 : }
356 :
357 125 : std::string getTaskBodyString() override {
358 :
359 250 : std::ostringstream os;
360 :
361 340 : for(size_t i=0; i < NbParams; i++) {
362 215 : os << SpModeToStr(dataHandles[i]->getModeByTask(dataHandlesKeys[i])) << " " << dataHandles[i]->getRawPtr() << std::endl;
363 : }
364 :
365 125 : for(size_t i=0; i < dataHandlesExtra.size(); i++) {
366 0 : os << SpModeToStr(dataHandlesExtra[i]->getModeByTask(dataHandlesKeysExtra[i])) << " " << dataHandlesExtra[i]->getRawPtr() << std::endl;
367 : }
368 250 : return os.str();
369 : }
370 : };
371 :
372 : template <class RetType, class DataDependencyTupleTy, class CallableTupleTy>
373 : class SpSelectTask : public SpTask<RetType, DataDependencyTupleTy, CallableTupleTy>
374 : {
375 : using Parent = SpTask<RetType, DataDependencyTupleTy, CallableTupleTy>;
376 :
377 : // flag indicating if the select task is carrying surely written values over
378 : bool isCarrSurWrittValuesOver;
379 :
380 : public:
381 147 : explicit SpSelectTask(SpAbstractTaskGraph* const inAtg, const SpTaskActivation initialActivationState,
382 : const SpPriority& inPriority,
383 : DataDependencyTupleTy &&inDataDepTuple,
384 : CallableTupleTy &&inCallableTuple, bool iCSWVO)
385 : : Parent(inAtg, initialActivationState, inPriority,
386 147 : std::move(inDataDepTuple), std::move(inCallableTuple)), isCarrSurWrittValuesOver(iCSWVO) {}
387 :
388 57 : void setEnabledDelegate(const SpTaskActivation inIsEnable) override final {
389 57 : if((inIsEnable == SpTaskActivation::DISABLE && !isCarrSurWrittValuesOver)
390 6 : || inIsEnable == SpTaskActivation::ENABLE) {
391 51 : this->setEnabled(inIsEnable);
392 : }
393 57 : }
394 : };
395 :
396 : #endif
|