MADNESS 0.10.1
macrotaskq.h
Go to the documentation of this file.
1/**
2 \file macrotaskq.h
3 \brief Declares the \c macrotaskq and MacroTaskBase classes
4 \ingroup mra
5
6 A MacroTaskq executes tasks on World objects, e.g. differentiation of a function or other
7 arithmetic. Complex algorithms can be implemented.
8
9 The universe world is split into subworlds, each of them executing macrotasks of the task queue.
10 This improves locality and speedups for large number of compute nodes, by reducing communications
11 within worlds.
12
13 The user defines a macrotask (an example is found in test_vectormacrotask.cc), the tasks are
14 lightweight and carry only bookkeeping information, actual input and output are stored in a
15 cloud (see cloud.h)
16
17 The user-defined macrotask is derived from MacroTaskIntermediate and must implement the run()
18 method. A heterogeneous task queue is possible.
19
20 The result of a macrotask is an object that lives in the universe and that is accessible from all
21 subworlds. The result is accumulated in the universe and must therefore be a WorldObject. Currently we
22 have implemented
23 - Function<T,NDIM>
24 - std::vector<Function<T,NDIM>> (a vector of Function<T,NDIM>)
25 - ScalarResultImpl<T> (a scalar value)
26 - std::vector<std::shared_ptr<ScalarResultImpl<T>>> (a vector of scalar values), shared_ptr for technical reasons
27
28 - std::tuple<std::vector<XXX>, std::vector<YYY>> (a tuple of n vectors of WorldObjects: XXX, YYY, .. = {Function, ScalarResultImpl, ...})
29
30
31 TODO: priority q
32 TODO: task submission from inside task (serialize task instead of replicate)
33 TODO: update documentation
34 TODO: consider serializing task member variables
35
36*/
37
38
39
40#ifndef SRC_MADNESS_MRA_MACROTASKQ_H_
41#define SRC_MADNESS_MRA_MACROTASKQ_H_
42
43#include <madness/world/cloud.h>
44#include <madness/world/world.h>
47
48namespace madness {
49
50/// helper class for returning the result of a task, which is not a madness Function, but a simple scalar
51
52/// the result value is accumulated via gaxpy in universe rank=0, after completion of the taskq the final
53/// value can be obtained via get(), which includes a broadcast of the final value to all processes
54template<typename T>
55class ScalarResultImpl : public WorldObject<ScalarResultImpl<T>> {
56public:
57 typedef T value_type;
61
62 /// Disable the default copy constructor
63 ScalarResultImpl(const ScalarResultImpl& other) = delete;
64 // ScalarResultImpl<T>(ScalarResultImpl<T>&& ) = default;
65
66 /// disable assignment operator
68
71
72 /// simple assignment of the scalar value
74 value = x;
75 return *this;
76 }
77
79 gaxpy(1.0, x, 1.0,true);
80 return *this;
81 }
82
83 /// accumulate, optional fence
84 void gaxpy(const double a, const T& right, double b, const bool fence=true) {
85 if (this->get_world().rank()==0) {
86 value =a*value + b * right;
87 }
88 else this->send(0, &ScalarResultImpl<T>::gaxpy, a, right, b, fence);
89 }
90
91 template<typename Archive>
92 void serialize(Archive &ar) {
93 ar & value;
94 }
95
96 /// after completion of the taskq get the final value
97 T get() {
98 this->get_world().gop.broadcast_serializable(*this, 0);
99 return value;
100 }
101
102 /// get the local value of this rank, which might differ for different ranks
103 /// for the final value use get()
104 T get_local() const {
105 return value;
106 }
107
108private:
109 /// the scalar value
110 T value=T();
111};
112
113template<typename T=double>
115public:
117 std::shared_ptr<implT> impl;
118
119 ScalarResult() = default;
120 ScalarResult(World &world) : impl(new implT(world)) {}
121 ScalarResult(const std::shared_ptr<implT>& impl) : impl(impl) {}
122 ScalarResult& operator=(const T& x) {
123 *(this->impl) = x;
124 return *this;
125 }
126
127 std::shared_ptr<implT> get_impl() const {
128 return impl;
129 }
130
131 void set_impl(const std::shared_ptr<implT>& newimpl) {
132 impl=newimpl;
133 }
134
135 uniqueidT id() const {
136 return impl->id();
137 }
138
139 /// accumulate, optional fence
140 void gaxpy(const double a, const T& right, double b, const bool fence=true) {
141 impl->gaxpy(a,right,b,fence);
142 }
143
144 template<typename Archive>
145 void serialize(Archive &ar) {
146 ar & impl;
147 }
148
149 /// after completion of the taskq get the final value
150 T get() {
151 return impl->get();
152 }
153
154 /// after completion of the taskq get the final value
155 T get_local() const {
156 return impl->get_local();
157 }
158
159};
160
161/// helper function to create a vector of ScalarResultImpl, circumventing problems with the constructors
162template<typename T>
163std::vector<ScalarResult<T>> scalar_result_vector(World& world, std::size_t n) {
164 std::vector<ScalarResult<T>> v;
165 for (std::size_t i=0; i<n; ++i) v.emplace_back(ScalarResult<T>(world));
166 return v;
167}
168
169
170// type traits to check if a template parameter is a WorldContainer
171template<typename>
172struct is_scalar_result_ptr : std::false_type {};
173
174template <typename T>
175struct is_scalar_result_ptr<std::shared_ptr<madness::ScalarResultImpl<T>>> : std::true_type {};
176
177template<typename>
178struct is_scalar_result_ptr_vector : std::false_type {
179};
180
181template<typename T>
182struct is_scalar_result_ptr_vector<std::vector<std::shared_ptr<typename madness::ScalarResultImpl<T>>>> : std::true_type {
183};
184
185// type traits to check if a template parameter is a WorldContainer
186template<typename>
187struct is_scalar_result : std::false_type {};
188
189template <typename T>
190struct is_scalar_result<madness::ScalarResult<T>> : std::true_type {};
191
192template<typename>
193struct is_scalar_result_impl : std::false_type {};
194
195template <typename T>
196struct is_scalar_result<madness::ScalarResultImpl<T>> : std::true_type {};
197
198template<typename>
199struct is_scalar_result_vector : std::false_type {
200};
201
202template<typename T>
203struct is_scalar_result_vector<std::vector<typename madness::ScalarResult<T>>> : std::true_type {
204};
205
206/// check if type is a valid task result: it must be a WorldObject and must implement gaxpy
207template <typename T>
208inline constexpr bool is_valid_task_result_v =
209 is_madness_function<T>::value // Function<T,NDIM>
210 || is_madness_function_vector<T>::value // std::vector<Function<T,NDIM>>
211 || is_scalar_result<T>::value // ScalarResultImpl<T>
212 || is_scalar_result_vector<T>::value // std::vector<std::shared_ptr<ScalarResultImpl<T>>>
213 || is_scalar_result_ptr<T>::value // ScalarResultImpl<T>
214 || is_scalar_result_ptr_vector<T>::value; // std::vector<std::shared_ptr<ScalarResultImpl<T>>>
215
216
217template<typename> struct is_tuple : std::false_type { };
218template<typename ...T> struct is_tuple<std::tuple<T...>> : std::true_type { };
219
220/// given a tuple check recursively if all elements are valid task results
221template<typename tupleT, std::size_t I>
223
224 typedef decay_tuple <tupleT> argtupleT; // removes const, &, etc
225
226 if constexpr(I >= std::tuple_size_v<tupleT>) {
227 // Last case, if nothing is left to iterate, then exit the function
228 return true;
229 } else {
230 using typeT = typename std::tuple_element<I, argtupleT>::type;// use decay types for determining a vector
231 if constexpr (not is_valid_task_result_v<typeT>) {
232 return false;
233 } else {
234 // Going for next element.
235 return check_tuple_is_valid_task_result<tupleT,I+1>();
236 }
237 }
238}
239
240
241
242/// the result type of a macrotask must implement gaxpy
243template<typename T>
244void gaxpy(const double a, ScalarResult<T>& left, const double b, const T& right, const bool fence=true) {
245 left.gaxpy(a, right, b, fence);
246}
247
248template <class Archive, typename T>
249struct madness::archive::ArchiveStoreImpl<Archive, std::shared_ptr<ScalarResultImpl<T>>> {
250 static void store(const Archive& ar, const std::shared_ptr<ScalarResultImpl<T>>& ptr) {
251 bool exists=(ptr) ? true : false;
252 ar & exists;
253 if (exists) ar & ptr->id();
254 }
255};
256
257
258template <class Archive, typename T>
259struct madness::archive::ArchiveLoadImpl<Archive, std::shared_ptr<ScalarResultImpl<T>>> {
260 static void load(const Archive& ar, std::shared_ptr<ScalarResultImpl<T>>& ptr) {
261 bool exists=false;
262 ar & exists;
263 if (exists) {
264 uniqueidT id;
265 ar & id;
266 World* world = World::world_from_id(id.get_world_id());
267 MADNESS_ASSERT(world);
268 auto ptr_opt = (world->ptr_from_id< ScalarResultImpl<T> >(id));
269 if (!ptr_opt)
270 MADNESS_EXCEPTION("ScalarResultImpl: remote operation attempting to use a locally uninitialized object",0);
271 ptr.reset(ptr_opt.value(), [] (ScalarResultImpl<T> *p_) -> void {}); // disable destruction
272 if (!ptr)
273 MADNESS_EXCEPTION("ScalarResultImpl<T> operation attempting to use an unregistered object",0);
274 } else {
275 ptr=nullptr;
276 }
277 }
278};
279
282 StoreFunction, ///< store a madness function in the cloud -- can have a large memory impact
283 StorePointerToFunction, ///< store the pointer to the function in the cloud, the actual function lives in the universe and
284 ///< its coefficients can be copied to the subworlds (e.g. by macrotaskq) when needed.
285 ///< The task itself is responsible for handling data movement
286 StoreFunctionViaPointer ///< store a pointer to the function in the cloud, but macrotaskq will move the
287 ///< coefficients to the subworlds when the task is started. This is the default policy.
288 };
289
290 friend std::ostream& operator<<(std::ostream& os, const StoragePolicy sp) {
291 if (sp==StoreFunction) os << "StoreFunction";
292 if (sp==StorePointerToFunction) os << "StorePointerToFunction";
293 if (sp==StoreFunctionViaPointer) os << "StoreFunctionViaPointer";
294 return os;
295 }
296
297 /// given the MacroTask's storage policy return the corresponding Cloud storage policy
306
307 /// return some preset policies
308 /// - "default": StoreFunctionViaPointer, cloud rank-replicated, initial functions node-replicated
309 /// - "small_memory": StoreFunctionViaPointer, cloud rank-replicated, initial functions distributed
310 /// - "large_memory": StoreFunction, cloud rank-replicated, initial functions distributed
311 /// the user can also set the policies manually
312 /// note: the policies are checked for consistency when the MacroTaskQ is created
313 static MacroTaskInfo preset(const std::string name) {
314 MacroTaskInfo info;
315 if (name=="default") {
319 } else if (name=="node_replicated_target") {
323 } else if (name=="small_memory") {
327 } else if (name=="small_memory_owner") {
328 // for tasks that fetch their own operands: the cloud holds pointers and the
329 // queue does not copy coefficients into the subworld, see
330 // handles_own_data_movement
334 } else if (name=="large_memory") {
338 } else {
339 std::string msg="MacroTaskQFactory::preset: unknown preset "+name;
340 MADNESS_EXCEPTION(msg.c_str(),0);
341 }
342 return info;
343 }
344
345 static std::vector<std::string> get_all_preset_names() {
346 return {"default","node_replicated_target","small_memory","small_memory_owner","large_memory"};
347 }
348
349 /// helper function to return all presets
350 static std::vector<MacroTaskInfo> get_all_presets() {
351 std::vector<MacroTaskInfo> result;
352 for (const auto& name : get_all_preset_names()) {
353 result.push_back(preset(name));
354 }
355 return result;
356 }
357
358 /// make sure the policies are consistent
359 bool check_consistency() const {
360 bool store_pointer_in_cloud = (storage_policy==MacroTaskInfo::StorePointerToFunction
362 bool good=true;
363
365 // if functions are stored in the cloud, the initial functions should be distributed
367
368 } else if (store_pointer_in_cloud) {
369 // if pointers are stored in the cloud, the initial functions can be distributed or replicated,
370 // the cloud should be rank-replicated
372
373 }
374 if (not good) std::cout << *this ;
375
376 return good;
377 }
378
379 /// set policy from a vector of strings, assuming the order is storage policy, cloud distribution policy, ptr target distribution policy
380 void from_vector_of_strings(const std::vector<std::string>& vec) {
381 if (vec.size()!=3) {
382 std::string msg="expected 3 policies, got "+std::to_string(vec.size());
383 MADNESS_EXCEPTION(msg.c_str(),0);
384 }
385 auto remove_quotes = [](const std::string& s) {
386 std::string result=s;
387 if (s.size()>=2 and s.front()=='"' and s.back()=='"') {
388 result=s.substr(1,s.size()-2);
389 }
390 return result;
391 };
392
393 std::string sstorage=remove_quotes(vec[0]);
394 if (sstorage=="storefunction") storage_policy=MacroTaskInfo::StoreFunction;
395 else if (sstorage=="storepointertofunction") storage_policy=MacroTaskInfo::StorePointerToFunction;
396 else if (sstorage=="storefunctionviapointer") storage_policy=MacroTaskInfo::StoreFunctionViaPointer;
397 else {
398 std::string msg="unknown storage policy: "+sstorage;
399 print("msg",msg);
400 MADNESS_CHECK_THROW(0, "Oh no1");
401 }
402
403 std::string scloud=remove_quotes(vec[1]);
405 else if (scloud=="nodereplicated") cloud_distribution_policy=DistributionType::NodeReplicated;
406 else if (scloud=="distributed") cloud_distribution_policy=DistributionType::Distributed;
407 else {
408 std::string msg="unknown cloud distribution policy: "+scloud;
409 print("msg",msg);
410 MADNESS_CHECK_THROW(0, "Oh no2");
411 }
412
413 std::string sptrtarget=remove_quotes(vec[2]);
415 else if (sptrtarget=="nodereplicated") ptr_target_distribution_policy=DistributionType::NodeReplicated;
416 else if (sptrtarget=="distributed") ptr_target_distribution_policy=DistributionType::Distributed;
417 else {
418 std::string msg="unknown ptr target distribution policy: "+sptrtarget;
419 print("msg",msg);
420 MADNESS_CHECK_THROW(0, "Oh no3");
421 }
422 // NB: parsing does not log — callers print the resolved policy
423 // gated on verbosity (see ExchangeImpl::set_macro_task_info).
424 }
425
429
430 friend std::ostream& operator<<(std::ostream& os, const MacroTaskInfo policy) {
431 os << "StoragePolicy: " << policy.storage_policy << std::endl;
432 os << "cloud_storage_policy: " << to_cloud_storage_policy(policy.storage_policy) << std::endl;
433 os << "cloud_distribution_policy: " << policy.cloud_distribution_policy << std::endl;
434 os << "ptr_target_distribution_policy: " << policy.ptr_target_distribution_policy << std::endl;
435 return os;
436 }
437
438 friend std::string to_string(const MacroTaskInfo::StoragePolicy sp) {
439 std::ostringstream os;
440 os << sp;
441 return os.str();
442 }
443
444 static StoragePolicy policy_to_string(const std::string policy) {
445 std::string policy_lc=commandlineparser::tolower(policy);
446 if (policy=="function") return MacroTaskInfo::StoreFunction;
447 if (policy=="pointer") return MacroTaskInfo::StorePointerToFunction;
448 if (policy=="functionviapointer") return MacroTaskInfo::StoreFunctionViaPointer;
449 std::string msg="unknown policy: "+policy;
450 MADNESS_EXCEPTION(msg.c_str(),1);
452 }
453
454 nlohmann::json to_json() const {
455 nlohmann::json j;
456 j["storage_policy"]=to_string(storage_policy);
457 j["cloud_distribution_policy"]=to_string(cloud_distribution_policy);
458 j["ptr_target_distribution_policy"]=to_string(ptr_target_distribution_policy);
459 return j;
460 }
461
462
463};
464
465template<typename T=double>
466std::ostream& operator<<(std::ostream& os, const typename MacroTaskInfo::StoragePolicy sp) {
467 if (sp==MacroTaskInfo::StoreFunction) os << "Function";
468 if (sp==MacroTaskInfo::StorePointerToFunction) os << "PointerToFunction";
469 if (sp==MacroTaskInfo::StoreFunctionViaPointer) os << "FunctionViaPointer";
470 return os;
471}
472
473/// base class
475public:
476
477 typedef std::vector<std::shared_ptr<MacroTaskBase> > taskqT;
478
480 virtual ~MacroTaskBase() {};
481
482 double priority=1.0;
483 long owner_slot=-1; ///< the subworld that should run this task; -1 means any
485
489
490 bool is_complete() const {return stat==Complete;}
491 bool is_running() const {return stat==Running;}
492 bool is_waiting() const {return stat==Waiting;}
493
494 virtual void run(World& world, Cloud& cloud, taskqT& taskq, const long element,
495 const bool debug, const MacroTaskInfo policy) = 0;
496 virtual void cleanup() = 0; // clear static data (presumably persistent input data)
497
498 /// Finalize, for tasks that accumulate their own output.
499
500 /// run_all holds only base pointers, so these have to be virtual. Stage 1 reduces the
501 /// per-subworld buffers within a node, stage 2 reduces across nodes into the universe
502 /// result; a nodeworld of nullptr means one node, so stage 1 has nothing to do.
503 virtual bool wants_node_local_reduction() const { return false; }
504 virtual void finalize_stage1(World& /*subworld*/, World* /*nodeworld*/, Cloud& /*cloud*/) {}
505 virtual void finalize_stage2(World& /*subworld*/, World* /*nodeworld*/, Cloud& /*cloud*/) {}
506
507 virtual void print_me(std::string s="") const {
508 printf("this is task with priority %4.1f\n",priority);
509 }
510 virtual void print_me_as_table(std::string s="") const {
511 print("nothing to print");
512 }
514 std::stringstream ss;
515 ss << std::setw(5) << this->get_priority() << " " <<this->stat;
516 return ss.str();
517 }
518
519 double get_priority() const {return priority;}
520 void set_priority(const double p) {priority=p;}
521 long get_owner_slot() const {return owner_slot;}
522 void set_owner_slot(const long owner) {owner_slot=owner;}
523 /// an unowned task may be run by any subworld, an owned one only by its own
524 bool is_owned_by(const long slot) const {return (owner_slot<0) or (owner_slot==slot);}
525
526 friend std::ostream& operator<<(std::ostream& os, const MacroTaskBase::Status s) {
527 if (s==MacroTaskBase::Status::Running) os << "Running";
528 if (s==MacroTaskBase::Status::Waiting) os << "Waiting";
529 if (s==MacroTaskBase::Status::Complete) os << "Complete";
530 if (s==MacroTaskBase::Status::Unknown) os << "Unknown";
531 return os;
532 }
533};
534
535
536template<typename macrotaskT>
538
539public:
540
542
544
545 void cleanup() {};
546};
547
548
549 /// Factory for the MacroTaskQ
551 public:
554 long nworld=1;
555
557
558 MacroTaskQFactory(World& universe) : world(universe), nworld(universe.size()) {}
559
561 nworld = n;
562 return *this;
563 }
564
566 printlevel = p;
567 return *this;
568 }
569
570 MacroTaskQFactory& preset(const std::string name) {
571 return *this;
572 }
573
575 policy = p;
576 return *this;
577 }
578
583
588
593
594 };
595
596
597
598class MacroTaskQ : public WorldObject< MacroTaskQ> {
599
601 std::shared_ptr<World> subworld_ptr;
602 /// node-scoped World for the two-stage finalize; created on first use, reused after
603 std::shared_ptr<World> nodeworld_ptr;
605 std::mutex taskq_mutex;
607 long nsubworld=1;
608 nlohmann::json cloud_statistics; ///< save cloud statistics after run_all()
609 nlohmann::json taskq_statistics; ///< save taskq statistics after run_all()
610
611 const MacroTaskInfo policy; ///< storage and distribution policy
612
613 /// set the process map for the subworld
614 std::shared_ptr< WorldDCPmapInterface< Key<1> > > pmap1;
615 std::shared_ptr< WorldDCPmapInterface< Key<2> > > pmap2;
616 std::shared_ptr< WorldDCPmapInterface< Key<3> > > pmap3;
617 std::shared_ptr< WorldDCPmapInterface< Key<4> > > pmap4;
618 std::shared_ptr< WorldDCPmapInterface< Key<5> > > pmap5;
619 std::shared_ptr< WorldDCPmapInterface< Key<6> > > pmap6;
620
621 bool printdebug() const {return printlevel>=10;}
622 bool printprogress() const {return (printlevel>=4) and (not (printdebug()));}
623 bool printtimings() const {return universe.rank()==0 and printlevel>=3;}
624 bool printtimings_detail() const {return universe.rank()==0 and printlevel>=5;}
625
626public:
627
630 long get_nsubworld() const {return nsubworld;}
631 void set_printlevel(const long p) {printlevel=p;}
632
634 return policy;
635 }
636
637 nlohmann::json get_cloud_statistics() const {
638 return cloud_statistics;
639 }
640
641 nlohmann::json get_taskq_statistics() const {
642 return taskq_statistics;
643 }
644
645 /// create an empty taskq and initialize the subworlds
646 explicit MacroTaskQ(const MacroTaskQFactory factory)
647 : WorldObject<MacroTaskQ>(factory.world)
648 , universe(factory.world)
649 , taskq()
650 , printlevel(factory.printlevel)
651 , nsubworld(factory.nworld)
652 , policy(factory.policy)
653 , cloud(factory.world)
654 {
656 MADNESS_CHECK_THROW(policy.check_consistency(),"MacroTaskQ: inconsistent storage policy");
659
660 if (printdebug()) print(policy);
661 this->process_pending();
662 }
663
665
666 /// for each process create a world using a communicator shared with other processes by round-robin
667 /// copy-paste from test_world.cc
668 static std::shared_ptr<World> create_worlds(World& universe, const std::size_t nsubworld) {
669
670 int color = universe.rank() % nsubworld;
672
673 std::shared_ptr<World> all_worlds;
674 all_worlds.reset(new World(comm));
675
677 return all_worlds;
678 }
679
680 /// a World spanning the ranks that share memory with this one
681
682 /// Collective, and worth creating only once: the two-stage finalize reduces within a
683 /// node first, so it needs a World with node scope.
684 static std::shared_ptr<World> create_node_world(World& universe) {
687 std::shared_ptr<World> node_world(new World(comm));
689 return node_world;
690 }
691
692 /// run all tasks
693
694 /// Put the cloud where the tasks will read it from.
695
696 /// Rank- or node-replication of the cloud, and of the targets that stored pointers refer to.
697 /// Both are no-ops under a distributed policy.
699 auto replication_policy = cloud.get_replication_policy();
700 if (replication_policy!=DistributionType::Distributed) {
701 double cpu0=cpu_time();
702 if (replication_policy==DistributionType::RankReplicated) cloud.replicate();
703 if (replication_policy==DistributionType::NodeReplicated) cloud.replicate_per_node(); // replicate to all hosts
705 double cpu1=cpu_time();
706 if (printtimings_detail()) print("cloud replication wall time",cpu1-cpu0);
707 }
708
709 // replicate the targets (not the cloud) if needed
710 {
711 double cpu0=cpu_time();
712 const bool need_replication_of_target=(policy.ptr_target_distribution_policy!=DistributionType::Distributed)
715
716
717 if (need_replication_of_target) {
719 for (auto wo : cloud.world_object_base_list) {
720 loop_types<Cloud::DistributeFunctor, double, float, double_complex, float_complex>(std::tuple<DistributionType>(dt),wo);
721 }
722 }
723
724 // if (need_replication_of_target) cloud.distribute_targets(policy.ptr_target_distribution_policy);
725 double cpu1=cpu_time();
726 if (printtimings_detail()) print("target replication wall time to ",policy.ptr_target_distribution_policy,cpu1-cpu0);
727 }
728 }
729
730 /// Run this rank's share of the queue.
731
732 /// Two ways in: if every task names an owner and there is one subworld per rank, each rank walks
733 /// the queue and runs what it owns, since the assignment is already decided and the pull
734 /// scheduler's round trip per task would buy nothing. Otherwise task numbers are handed out
735 /// dynamically. Both converge on the same accounting.
736 /// \return the cpu time this rank spent inside task bodies
737 double execute_tasks() {
738 World& subworld=get_subworld();
739 double tasktime=0.0;
740
741 // If every task names an owner and there is one subworld per rank, each rank can walk
742 // the queue and run its own tasks: the assignment is already decided, so the pull
743 // scheduler's round trip per task buys nothing. Falls through to the dynamic loop
744 // whenever any task is unowned, which is the case for every task that does not use
745 // the owner_hint hook.
746 const long requester_slot = universe.rank() % std::max<long>(1, nsubworld);
747 const bool all_tasks_owned = (not taskq.empty()) and std::all_of(taskq.begin(), taskq.end(),
748 [](const std::shared_ptr<MacroTaskBase>& t) { return t->get_owner_slot() >= 0; });
749
750 if (all_tasks_owned and (nsubworld==universe.size())) {
751 for (long element=0; element<long(taskq.size()); ++element) {
752 std::shared_ptr<MacroTaskBase> task=taskq[element];
753 if (not task->is_owned_by(requester_slot)) continue;
754 double cpu0=cpu_time();
755 if (printdebug()) print("starting task no",element, "in subworld",subworld.id(),"at time",wall_time());
756 task->run(subworld,cloud, taskq, element, printdebug(), policy);
757 double cpu1=cpu_time();
758 tasktime+=(cpu1-cpu0);
759 if (printdebug()) printf("completed task %3ld after %6.1fs at time %6.1fs\n",element,cpu1-cpu0,wall_time());
760 }
761 if (printdebug() and subworld.rank()==0) {
762 printf("rank %3d (subworld %3lu) finished its task queue at time %6.1fs (own tasktime %4.1fs)\n",
763 universe.rank(), static_cast<unsigned long>(subworld.id()), wall_time(), tasktime);
764 }
765 } else {
766
767 if (printprogress() and universe.rank()==0) std::cout << "progress in percent: " << std::flush;
768 while (true) {
769 long element=get_scheduled_task_number(subworld);
770 double cpu0=cpu_time();
771 if (element<0) break;
772 std::shared_ptr<MacroTaskBase> task=taskq[element];
773 if (printdebug()) print("starting task no",element, "in subworld",subworld.id(),"at time",wall_time());
774
775 task->run(subworld,cloud, taskq, element, printdebug(), policy);
776
777 double cpu1=cpu_time();
778 set_complete(element);
779 tasktime+=(cpu1-cpu0);
780 if (printdebug()) printf("completed task %3ld after %6.1fs at time %6.1fs\n",element,cpu1-cpu0,wall_time());
781
782 // print progress
783 const std::size_t ntask=taskq.size();
784 // return percentile of ntask for element
785 auto in_percentile = [&ntask](const long element) {
786 return std::floor(element/(0.1*(ntask+1)));
787 };
788 auto is_first_in_percentile = [&](const long element) {
789 return (in_percentile(element)!=in_percentile(element-1));
790 };
791 if (printprogress() and is_first_in_percentile(element)) {
792 std::cout << int(in_percentile(element)*10) << " " << std::flush;
793 }
794 }
795 } // both the owned-walk and the pull loop converge here
796 return tasktime;
797 }
798
799 /// Move the results of tasks that accumulated their own output into the universe result.
800
801 /// The ordering here is load-bearing and the comments inside say why. Two stages, so a node-local
802 /// reduction can sit between them and only one rank per node scatters between nodes.
804 World& subworld=get_subworld();
806 // A task that accumulates its own output does so without fencing, once per batch, so
807 // its buffer still has operations in flight here. The drains below read that buffer,
808 // so the subworld has to be quiesced first -- once, not per batch, which is the whole
809 // point of accumulating locally. Without this the drain reads a partly-written buffer
810 // and the result is wrong in a way that varies from run to run.
811 subworld.gop.fence();
812
813 // Drain the buffers of tasks that accumulated their own output. Two stages: reduce
814 // within a node, then across nodes into the universe result.
815 //
816 // The accumulator buffers MUST outlive the fences below, so a hook must not release
817 // or clear them: a remote gaxpy that arrives after the buffer is gone finds an impl
818 // that was released but is still registered, and throws bad_weak_ptr intermittently.
819 // Release them in cleanup(), after the universe fence. If this ever fences per
820 // subworld instead, re-audit every task that accumulates its own output.
821 const bool want_node_reduction = (not taskq.empty())
822 and taskq.front()->wants_node_local_reduction();
823 if (want_node_reduction and not nodeworld_ptr)
825 World* nodeworld = want_node_reduction ? nodeworld_ptr.get() : nullptr;
826 // one node means the node World is the universe, so stage 1 would only add a fence
827 if (nodeworld and nodeworld->size() == universe.size()) nodeworld = nullptr;
828
829 for (auto& t : taskq) t->finalize_stage1(subworld, nodeworld, cloud);
830 if (nodeworld) nodeworld->gop.fence();
831 for (auto& t : taskq) t->finalize_stage2(subworld, nodeworld, cloud);
833 }
834 void run_all() {
835
836 if (printdebug()) print_taskq();
837 if (printtimings_detail()) {
838 if (universe.rank()==0) {
839 print("number of tasks in taskq",taskq.size());
840 print("redirecting output to files task.#####");
841 }
842 }
843 taskq_statistics["number_tasks"]=taskq.size();
844
845 // replicate the cloud (not necessarily the target if pointers are stored)
847
849 cloud_statistics=cloud.get_statistics(universe); // get stats before clearing the cloud
851 universe.gop.set_forbid_fence(true); // make sure there are no hidden universe fences
859
860 double cpu00=cpu_time();
861
862 World& subworld=get_subworld();
863// if (printdebug()) print("I am subworld",subworld.id());
864 double tasktime=execute_tasks(); // not const: gop.sum reduces it in place below
867
868 universe.gop.sum(tasktime);
869 if (printprogress() and universe.rank()==0) std::cout << std::endl;
870 cloud_statistics.update(cloud.gather_timings(universe)); // get stats before clearing the cloud
871 double cpu11=cpu_time();
872 if (printlevel>=4) {
873 if (universe.rank()==0) {
876 print("all tasks complete");
877 }
879 }
880 if (printtimings_detail()) {
881 printf("completed taskqueue after %4.1fs at time %4.1fs\n", cpu11 - cpu00, wall_time());
882 printf(" total cpu time / per world %4.1fs %4.1fs\n", tasktime, tasktime / universe.size());
883 }
884 taskq_statistics["elapsed_time"]=cpu11-cpu00;
885 taskq_statistics["cpu_time_per_world"]=tasktime/universe.size();
886 taskq_statistics["total_cpu_time"]=tasktime;
887
888 // cleanup task-persistent input data
889 for (auto& task : taskq) task->cleanup();
890 cloud.clear_cache(subworld);
891 subworld.gop.fence();
892 subworld.gop.fence();
901 // restore targets to their original state
904 subworld.gop.fence();
905 cloud.clear();
907 subworld.gop.fence();
909 }
910
912 for (const auto& t : vtask) {
913 if (universe.rank()==0) t->set_waiting();
915 }
916 }
917
918 void print_taskq() const {
920 if (universe.rank()==0) {
921 print("\ntaskq on universe rank",universe.rank());
922 print("total number of tasks: ",taskq.size());
923 print(" task batch priority status");
924 for (const auto& t : taskq) t->print_me_as_table();
925 }
927 }
928
929private:
930 void add_replicated_task(const std::shared_ptr<MacroTaskBase>& task) {
931 taskq.push_back(task);
932 }
933
934 /// scheduler is located on universe.rank==0
936 long number=0;
937 if (subworld.rank()==0) {
939 number=r.get();
940 }
941 subworld.gop.broadcast_serializable(number, 0);
942 subworld.gop.fence();
943 return number;
944
945 }
946
949 std::lock_guard<std::mutex> lock(taskq_mutex);
950
951 auto is_Waiting = [](const std::shared_ptr<MacroTaskBase>& mtb_ptr) {return mtb_ptr->is_waiting();};
952 auto it=std::find_if(taskq.begin(),taskq.end(),is_Waiting);
953 if (it!=taskq.end()) {
954 it->get()->set_running();
955 long element=it-taskq.begin();
956 return element;
957 }
958// print("could not find task to schedule");
959 return -1;
960 }
961
962 /// scheduler is located on rank==0
963 void set_complete(const long task_number) const {
964 this->task(ProcessID(0), &MacroTaskQ::set_complete_local, task_number);
965 }
966
967 /// scheduler is located on rank==0
968 void set_complete_local(const long task_number) const {
970 taskq[task_number]->set_complete();
971 }
972
973public:
982private:
983 std::size_t size() const {
984 return taskq.size();
985 }
986
987};
988
989
990template<typename taskT>
993
994 template<typename Q>
995 struct is_vector : std::false_type {
996 };
997 template<typename Q>
998 struct is_vector<std::vector<Q>> : std::true_type {
999 };
1000
1001 // ---- optional task hooks ----
1002 //
1003 // A task opts into a piece of framework machinery by declaring the corresponding member. Each
1004 // hook is a named detection trait plus an `if constexpr`, the same shape madness::archive uses
1005 // to ask whether a type can serialize itself (see has_member_serialize_v in type_traits.h): the
1006 // trait names the expression that has to compile, and the dispatch reads as an ordinary branch.
1007 // A task that declares nothing takes the empty branch and behaves exactly as before.
1008 //
1009 // Only the hooks whose signature depends on the task's own argument tuple or result type live
1010 // here; the ones with a fixed signature are plain virtuals on MacroTaskOperationBase
1011 // (owner_hint, accumulates_own_output, handles_own_data_movement, cleanup) or on MacroTaskBase
1012 // (wants_node_local_reduction, finalize_stage1, finalize_stage2), because run_all dispatches
1013 // those through a base pointer.
1014
1015 /// true if: task.prepare_owner_assignment(partition, nsubworld)
1016 template<typename Q>
1018 decltype(std::declval<Q&>().prepare_owner_assignment(
1019 std::declval<const MacroTaskPartitioner::partitionT&>(), 0L));
1020 template<typename Q>
1021 static constexpr bool has_prepare_owner_assignment_v =
1022 madness::meta::is_detected_v<has_prepare_owner_assignment_t, Q>;
1023
1024 /// true if: task.store_batches(world, subworld, cloud, argtuple, nsubworld)
1025 template<typename Q, typename ArgTuple>
1027 decltype(std::declval<Q&>().store_batches(
1028 std::declval<World&>(), std::declval<World&>(), std::declval<Cloud&>(),
1029 std::declval<const ArgTuple&>(), 0L));
1030 template<typename Q, typename ArgTuple>
1031 static constexpr bool has_store_batches_v =
1032 madness::meta::is_detected_v<has_store_batches_t, Q, ArgTuple>;
1033
1034 /// true if: task.sym_pipeline_advance(subworld, ket, next_col, next_row, has_next)
1035 template<typename Q, typename VecT>
1037 decltype(std::declval<Q&>().sym_pipeline_advance(
1038 std::declval<World&>(), std::declval<const VecT&>(),
1039 std::declval<const Batch_1D&>(), std::declval<const Batch_1D&>(), true));
1040 template<typename Q, typename VecT>
1041 static constexpr bool has_sym_pipeline_advance_v =
1042 madness::meta::is_detected_v<has_sym_pipeline_advance_t, Q, VecT>;
1043
1044 /// true if: task.accumulate_locally(subworld, result_subworld)
1045 template<typename Q, typename ResT>
1047 decltype(std::declval<Q&>().accumulate_locally(
1048 std::declval<World&>(), std::declval<const ResT&>()));
1049 template<typename Q, typename ResT>
1050 static constexpr bool has_accumulate_locally_v =
1051 madness::meta::is_detected_v<has_accumulate_locally_t, Q, ResT>;
1052
1053 /// true if: task.wants_node_local_reduction()
1054 template<typename Q>
1056 decltype(std::declval<const Q&>().wants_node_local_reduction());
1057 template<typename Q>
1058 static constexpr bool has_wants_node_local_reduction_v =
1059 madness::meta::is_detected_v<has_wants_node_local_reduction_t, Q>;
1060
1061 /// true if: task.finalize_stage1(subworld, nodeworld)
1062 template<typename Q>
1064 decltype(std::declval<Q&>().finalize_stage1(std::declval<World&>(),
1065 std::declval<World*>()));
1066 template<typename Q>
1067 static constexpr bool has_finalize_stage1_v =
1068 madness::meta::is_detected_v<has_finalize_stage1_t, Q>;
1069
1070 /// true if: task.finalize_stage2(subworld, nodeworld, universe_result)
1071 template<typename Q, typename ResT>
1073 decltype(std::declval<Q&>().finalize_stage2(std::declval<World&>(),
1074 std::declval<World*>(),
1075 std::declval<ResT&>()));
1076 template<typename Q, typename ResT>
1077 static constexpr bool has_finalize_stage2_v =
1078 madness::meta::is_detected_v<has_finalize_stage2_t, Q, ResT>;
1079
1080 typedef typename taskT::resultT resultT;
1081 typedef typename taskT::argtupleT argtupleT;
1083
1084 taskT task;
1085 bool debug=false;
1088 std::shared_ptr<MacroTaskQ> taskq_ptr;
1089
1090public:
1091
1092 /// constructor takes the task, but no arguments to the task
1093 explicit MacroTask(World &world, taskT &task)
1096 }
1097
1098 /// constructor takes task and a taskq factory for customization, immediate execution
1099 explicit MacroTask(World &world, taskT& task, const MacroTaskQFactory factory)
1100 : MacroTask(world,task, std::shared_ptr<MacroTaskQ>(new MacroTaskQ(factory))) {
1102 }
1103
1104 /// constructor takes the task, and a taskq, execution is not immediate
1105 explicit MacroTask(World &world, taskT &task, std::shared_ptr<MacroTaskQ> taskq_ptr)
1107
1108 // someone might pass in a taskq nullptr
1109 immediate_execution=false; // will be reset by the forwarding constructors
1110 if (this->taskq_ptr==0) {
1111 this->taskq_ptr=std::make_shared<MacroTaskQ>(MacroTaskQFactory(world));
1113 }
1114
1115 if (debug) this->taskq_ptr->set_printlevel(20);
1116 // set the cloud policies
1117 auto cloud_storage_policy = MacroTaskInfo::to_cloud_storage_policy(this->taskq_ptr->get_policy().storage_policy);
1118 this->taskq_ptr->cloud.set_storing_policy(cloud_storage_policy);
1119 this->taskq_ptr->cloud.set_replication_policy(this->taskq_ptr->get_policy().cloud_distribution_policy);
1120
1121 }
1122
1123 MacroTask& set_debug(const bool value) {
1124 debug=value;
1125 return *this;
1126 }
1127
1128 std::shared_ptr<MacroTaskQ> get_taskq() const {
1129 return taskq_ptr;
1130 }
1131
1132
1133 /// this mimicks the original call to the task functor, called from the universe
1134
1135 /// store all input to the cloud, create output Function<T,NDIM> in the universe,
1136 /// create the batched task and shove it into the taskq. Possibly execute the taskq.
1137 template<typename ... Ts>
1138 resultT operator()(const Ts &... args) {
1139
1140 auto argtuple = std::tie(args...);
1141 static_assert(std::is_same<decltype(argtuple), argtupleT>::value, "type or number of arguments incorrect");
1142
1143 // partition the argument vector into batches
1144 auto partitioner=task.partitioner;
1145 if (not partitioner) partitioner.reset(new MacroTaskPartitioner);
1146 partitioner->set_nsubworld(world.size());
1147 partitionT partition = partitioner->partition_tasks(argtuple);
1148
1149 // let the task assign batches to owners from the whole partition, then reorder it
1150 if constexpr (has_prepare_owner_assignment_v<taskT>)
1151 task.prepare_owner_assignment(partition, taskq_ptr->get_nsubworld());
1152
1153 if (debug and world.rank()==0) print(taskq_ptr->get_policy());
1154
1155 recordlistT inputrecords = taskq_ptr->cloud.store(world, argtuple);
1156 // additionally store the inputs as owner-pinned cloud batches, if the task wants them
1157 if constexpr (has_store_batches_v<taskT, argtupleT>)
1158 task.store_batches(world, taskq_ptr->get_subworld(), taskq_ptr->cloud, argtuple,
1159 taskq_ptr->get_nsubworld());
1160 resultT result = task.allocator(world, argtuple);
1161 auto outputrecords =prepare_output_records(taskq_ptr->cloud, result);
1162
1163 // create tasks and add them to the taskq
1165 for (const auto& batch_prio : partition) {
1166 const long owner_slot = task.owner_hint(batch_prio.first, taskq_ptr->get_nsubworld());
1167 vtask.push_back(
1168 std::shared_ptr<MacroTaskBase>(new MacroTaskInternal(task, batch_prio, inputrecords, outputrecords, owner_slot)));
1169 }
1170 taskq_ptr->add_tasks(vtask);
1171 if (immediate_execution) taskq_ptr->run_all();
1172
1173 return result;
1174 }
1175private:
1176
1177
1178 /// store *pointers* to the result WorldObject in the cloud and return the recordlist
1180 if constexpr (is_tuple<resultT>::value) {
1181 static_assert(check_tuple_is_valid_task_result<resultT,0>(),
1182 "tuple has invalid result type in prepare_output_records");
1183 } else {
1184 static_assert(is_valid_task_result_v<resultT>, "unknown result type in prepare_output_records");
1185 }
1186
1187 if (debug) print("storing pointers to output in cloud");
1188 // store an element of the tuple only
1189 auto store_output_records = [&](const auto& result) {
1190 recordlistT outputrecords;
1191 typedef std::decay_t<decltype(result)> argT;
1192 if constexpr (is_madness_function<argT>::value) {
1193 outputrecords += cloud.store(world, result.get_impl().get()); // store pointer to FunctionImpl
1194 } else if constexpr (is_madness_function_vector<argT>::value) {
1195 outputrecords += cloud.store(world, get_impl(result));
1196 } else if constexpr (is_scalar_result<argT>::value) {
1197 outputrecords += cloud.store(world, result.get_impl()); // store pointer to ScalarResultImpl
1198 } else if constexpr (is_vector<argT>::value) {
1200 // argT = std::vector<ScalarResult<T>>
1201 std::vector<std::shared_ptr<typename argT::value_type::implT>> v;
1202 for (const auto& ptr : result) v.push_back(ptr.get_impl());
1203 outputrecords+=cloud.store(world,v);
1204 } else {
1205 MADNESS_EXCEPTION("\n\n unknown vector result type in prepare_input ", 1);
1206 }
1207 } else {
1208 MADNESS_EXCEPTION("should not be here",1);
1209 }
1210 return outputrecords;
1211 };
1212
1213 recordlistT outputrecords;
1214 if constexpr (is_tuple<resultT>::value) {
1215 // loop over tuple elements -- args is the individual tuple element
1216 std::apply([&](auto &&... args) {
1217 (( outputrecords+=store_output_records(args) ), ...);
1218 }, result);
1219 } else {
1220 outputrecords=store_output_records(result);
1221 }
1222 return outputrecords;
1223 }
1224
1225
1226 class MacroTaskInternal : public MacroTaskIntermediate<MacroTask> {
1227
1228 typedef decay_tuple<typename taskT::argtupleT> argtupleT; // removes const, &, etc
1229 typedef typename taskT::resultT resultT;
1232 public:
1233 taskT task;
1234 std::string get_name() const {
1235 if (task.name=="unknown_task") return typeid(task).name();
1236 return task.name;
1237 }
1238
1239 MacroTaskInternal(const taskT &task, const std::pair<Batch,double> &batch_prio,
1241 const long owner_slot = -1)
1243 if constexpr (is_tuple<resultT>::value) {
1244 static_assert(check_tuple_is_valid_task_result<resultT,0>(),
1245 "tuple has invalid result type in prepare_output_records");
1246 } else {
1247 static_assert(is_valid_task_result_v<resultT>, "unknown result type in prepare_output_records");
1248 }
1249 this->task.batch=batch_prio.first;
1250 this->priority=batch_prio.second;
1252 }
1253
1254
1255 void print_me(std::string s="") const override {
1256 print("this is task",get_name(),"with batch", task.batch,"priority",this->get_priority());
1257 }
1258
1259 void print_me_as_table(std::string s="") const override {
1260 std::stringstream ss;
1261 std::string name=get_name();
1262 std::size_t namesize=std::min(std::size_t(28),name.size());
1263 name += std::string(28-namesize,' ');
1264
1265 std::stringstream ssbatch;
1266 ssbatch << task.batch;
1267 std::string strbatch=ssbatch.str();
1268 int nspaces=std::max(int(0),35-int(ssbatch.str().size()));
1269 strbatch+=std::string(nspaces,' ');
1270
1271 ss << name
1272 << std::setw(10) << strbatch
1274 print(ss.str());
1275 }
1276
1277 /// accumulate the result of the task into the final result living in the universe
1278 template<typename resultT1, std::size_t I=0>
1279 typename std::enable_if<is_tuple<resultT1>::value, void>::type
1280 accumulate_into_final_result(World &subworld, resultT1 &final_result, const resultT1 &tmp_result, const argtupleT& argtuple) {
1281 if constexpr(I < std::tuple_size_v<resultT1>) {
1282 using elementT = typename std::tuple_element<I, resultT>::type;// use decay types for determining a vector
1283 auto element_final=std::get<I>(final_result);
1284 auto element_tmp=std::get<I>(tmp_result);
1285 accumulate_into_final_result<elementT>(subworld, element_final, element_tmp, argtuple);
1286 accumulate_into_final_result<resultT1,I+1>(subworld, final_result, tmp_result, argtuple);
1287 }
1288 }
1289
1290 /// accumulate the result of the task into the final result living in the universe
1291 template<typename resultT1>
1292 typename std::enable_if<not is_tuple<resultT1>::value, void>::type
1293 accumulate_into_final_result(World &subworld, resultT1 &result, const resultT1 &result_tmp, const argtupleT& argtuple) {
1295 // gaxpy can be done in reconstructed or compressed mode
1296 TreeState operating_state=result_tmp.get_impl()->get_tensor_type()==TT_FULL ? compressed : reconstructed;
1297 result_tmp.change_tree_state(operating_state);
1298 gaxpy(1.0,result,1.0, result_tmp);
1299 } else if constexpr(is_madness_function_vector<resultT1>::value) {
1300 TreeState operating_state=result_tmp[0].get_impl()->get_tensor_type()==TT_FULL ? compressed : reconstructed;
1301 change_tree_state(result_tmp,operating_state);
1302 // compress(subworld, result_tmp);
1303 // resultT1 tmp1=task.allocator(subworld,argtuple);
1304 // tmp1=task.batch.template insert_result_batch(tmp1,result_tmp);
1305 gaxpy(1.0,result,1.0,result_tmp,false);
1306 // was using operator+=, but this requires a fence, which is not allowed here..
1307 // result += tmp1;
1308 } else if constexpr (is_scalar_result<resultT1>::value) {
1309 gaxpy(1.0, result, 1.0, result_tmp.get_local(), false);
1310 } else if constexpr (is_scalar_result_vector<resultT1>::value) {
1311 // resultT1 tmp1=task.allocator(subworld,argtuple);
1312 // tmp1=task.batch.template insert_result_batch(tmp1,result_tmp);
1313 std::size_t sz=result.size();
1314 for (size_t i=0; i<sz; ++i) {
1315 gaxpy(1.0, result[i], 1.0, result_tmp[i].get_local(), false);
1316 }
1317 }
1318
1319 }
1320
1321 /// Bridge the framework's virtual finalize to the task's hooks.
1322
1323 /// A task that does not accumulate its own output has nothing to finalize, so the
1324 /// guard keeps every existing task on the queue's own accumulation path.
1325 bool wants_node_local_reduction() const override {
1326 if constexpr (has_wants_node_local_reduction_v<taskT>) return task.wants_node_local_reduction();
1327 else return false;
1328 }
1329
1330 void finalize_stage1(World& subworld, World* nodeworld, Cloud& /*cloud*/) override {
1331 if (not task.accumulates_own_output()) return;
1332 if constexpr (has_finalize_stage1_v<taskT>) task.finalize_stage1(subworld, nodeworld);
1333 }
1334
1335 void finalize_stage2(World& subworld, World* nodeworld, Cloud& cloud) override {
1336 if (not task.accumulates_own_output()) return;
1337 resultT result_universe = get_output(subworld, cloud);
1338 if constexpr (has_finalize_stage2_v<taskT, resultT>)
1339 task.finalize_stage2(subworld, nodeworld, result_universe);
1340 }
1341
1342 /// the next task in taskq that this task's subworld also owns, or {-1, nullptr}
1343
1344 /// A task that prefetches needs to know which batch it will be asked for next. With
1345 /// owner-pinned scheduling that is decidable locally: the queue order is fixed and
1346 /// ownership is already assigned, so the next owned entry is just the next match.
1347 std::pair<long, MacroTaskInternal*> find_next_owned_task(
1348 const MacroTaskBase::taskqT& taskq, const long element) const {
1349 const long owner_slot = this->get_owner_slot();
1350 if (owner_slot < 0) return {-1, nullptr};
1351 for (long next = element + 1; next < long(taskq.size()); ++next) {
1352 if (not taskq[next]->is_owned_by(owner_slot)) continue;
1353 auto next_task = std::dynamic_pointer_cast<MacroTaskInternal>(taskq[next]);
1354 if (next_task) return {next, next_task.get()};
1355 }
1356 return {-1, nullptr};
1357 }
1358
1359 /// called by the MacroTaskQ when the task is scheduled
1360
1361 /// Let this task start the transfer its successor will need.
1362
1363 /// Called before the task body so the request overlaps this task's compute. The successor is
1364 /// the next task in the queue this rank owns; a task that declares no prefetch hook does
1365 /// nothing here.
1366 void prefetch_for_next_task(World& subworld, const argtupleT& argtuple,
1367 MacroTaskBase::taskqT& taskq, const long element) {
1368 // pre-compute: let the task prefetch what the next task it owns will need, so the
1369 // transfer overlaps this task's compute
1370 {
1371 auto [next_elem, next_ptr] = find_next_owned_task(taskq, element);
1372 const bool has_next = (next_ptr != nullptr) and (next_ptr->task.batch.input.size() > 1);
1373 const Batch_1D next_col = has_next ? next_ptr->task.batch.input[0] : Batch_1D();
1374 const Batch_1D next_row = has_next ? next_ptr->task.batch.input[1] : Batch_1D();
1375 // the ket is the third argument; a task with fewer cannot have the hook either
1376 if constexpr (std::tuple_size<argtupleT>::value >= 3) {
1377 using ketT = std::decay_t<std::tuple_element_t<2, argtupleT>>;
1378 if constexpr (has_sym_pipeline_advance_v<taskT, ketT>)
1379 task.sym_pipeline_advance(subworld, std::get<2>(argtuple),
1380 next_col, next_row, has_next);
1381 }
1382 }
1383 }
1384
1385 /// Bring the operand coefficients into the subworld, unless the task does that itself.
1386 void copy_operands_into_subworld(World& subworld, Cloud& cloud, argtupleT& batched_argtuple,
1387 const MacroTaskInfo& policy, const bool debug) {
1388 // maybe move this block to the cloud?
1389 // A task that fetches its own operands must not have them copied in as well --
1390 // that would pay for the coefficients twice, once per task instead of once per
1391 // owned batch, which is the cost the owner-pinned path exists to avoid. Both pointer
1392 // policies store a pointer, so both would otherwise deep-copy here -- and which one is
1393 // in force comes from the queue, so a task sharing a queue with another operator (nemo
1394 // runs Coulomb and exchange through one) inherits that queue's policy, not its own.
1395 const bool need_auto_copy =
1398 and not task.handles_own_data_movement();
1399 if (need_auto_copy) {
1400 double cpu0=wall_time();
1401 Cloud::cloudtimer timer(subworld,cloud.copy_time);
1402 // the functions loaded from the cloud are pointers to the universe functions,
1403 // retrieve the function coefficients from the universe
1404 // aka: turn the current shallow copy into a deep copy
1405 if (debug) print("loading function coefficients from universe for task",get_name());
1406
1407 // loop over the tuple -- copy the functions from the universe to the subworld
1408 auto copi = [&](auto& arg) {
1409 typedef std::decay_t<decltype(arg)> argT;
1410 if constexpr (is_madness_function<argT>::value) {
1411 arg=copy(subworld, arg);
1412 } else if constexpr (is_madness_function_vector<argT>::value) {
1413 for (auto& f : arg) f=copy(subworld,f);
1414 }
1415 };
1416
1417 unary_tuple_loop(batched_argtuple,copi);
1418 double cpu1=wall_time();
1419 if (debug) {
1420 io_redirect_cout io2;
1421 print("copied coefficients for task",get_name(),"in",cpu1-cpu0,"seconds");
1422 }
1423 }
1424 }
1425
1426 void run(World &subworld, Cloud &cloud, MacroTaskBase::taskqT &taskq, const long element, const bool debug,
1427 const MacroTaskInfo policy) override {
1428 // per-task files are a debugging aid and become thousands at fine granularity; a
1429 // failing task still reports on the terminal regardless, see the catch below
1431 io_redirect io(io_mode, element, get_name()+"_output", get_name()+"_task", debug);
1432 // The try covers the whole body, not just the compute: a throw while loading the
1433 // inputs or issuing a prefetch would otherwise escape uninstrumented and be
1434 // reported by the task backend with no task id and no what().
1435 try {
1436 const argtupleT argtuple = cloud.load<argtupleT>(subworld, inputrecords);
1437 argtupleT batched_argtuple = task.batch.copy_input_batch(argtuple);
1438
1439 task.subworld_ptr=&subworld;
1440 // before the prefetch hook, which fetches through it
1441 task.cloud_ptr=&cloud;
1442 prefetch_for_next_task(subworld, argtuple, taskq, element);
1443
1444 copy_operands_into_subworld(subworld, cloud, batched_argtuple, policy, debug);
1445
1446 print("starting task no",element, ", '",get_name(),"', in subworld",subworld.id(),"at time",wall_time());
1447 double cpu0=cpu_time();
1448 resultT result_batch = std::apply(task, batched_argtuple); // lives in the subworld, is a batch of the full vector (if applicable)
1449 double cpu1=cpu_time();
1450 constexpr std::size_t bufsize=256;
1451 char buffer[bufsize];
1452 std::snprintf(buffer,bufsize,"completed task %3ld after %6.1fs at time %6.1fs\n",element,cpu1-cpu0,wall_time());
1453 print(std::string(buffer));
1454
1455 // move the result from the batch to the final result, all still in subworld
1456 auto insert_batch = [&](auto& element1, auto& element2) {
1457 typedef std::decay_t<decltype(element1)> decay_type;;
1458 if constexpr (is_vector<decay_type>::value) {
1459 element1=task.batch.insert_result_batch(element1,element2);
1460 } else {
1461 std::swap(element1,element2);
1462 }
1463 };
1464 resultT result_subworld=task.allocator(subworld,argtuple);
1465 if constexpr (is_tuple<resultT>::value) {
1466 binary_tuple_loop(result_subworld, result_batch, insert_batch);
1467 } else {
1468 insert_batch(result_subworld,result_batch);
1469 }
1470
1471 // Accumulate the batch result. A task that accumulates its own output keeps it in
1472 // a subworld-local buffer and drains it once in finalize_stage2, instead of one
1473 // subworld->universe gaxpy per batch.
1474 if (task.accumulates_own_output()) {
1475 if constexpr (has_accumulate_locally_v<taskT, resultT>)
1476 task.accumulate_locally(subworld, result_subworld);
1477 } else {
1478 resultT result_universe=get_output(subworld, cloud); // lives in the universe
1479 accumulate_into_final_result<resultT>(subworld, result_universe, result_subworld, argtuple);
1480 }
1481
1482 } catch (std::exception& e) {
1483 // RSS at the moment of failure separates a memory fault (near the node ceiling,
1484 // with a bad_alloc what()) from a logic or communication bug (well below it).
1485 const double rss_at_fail = get_rss_usage_in_GB();
1486 print("failing task no",element,"in subworld",subworld.id(),"at time",wall_time());
1487 print(e.what());
1488 print("RSS at failure (current resident, GB):", rss_at_fail);
1490 print("\n\n");
1491 {
1492 // this task's stream may be a file or /dev/null, so repeat it on the terminal
1493 io_redirect_cout io2;
1494 print("failing task no",element,"in subworld",subworld.id(),"at time",wall_time());
1495 print(e.what());
1496 print("RSS at failure (current resident, GB):", rss_at_fail);
1497 }
1498 MADNESS_EXCEPTION("failing task",1);
1499 }
1500
1501 };
1502
1503 // this is called after all tasks have been executed and the taskq has ended
1504 void cleanup() override {
1505 task.cleanup();
1506 }
1507
1508 template<typename T, std::size_t NDIM>
1509 static Function<T,NDIM> pointer2WorldObject(const std::shared_ptr<FunctionImpl<T,NDIM>> impl) {
1510 Function<T,NDIM> result;
1511 result.set_impl(impl);
1512 return result;
1513 }
1514
1515 template<typename T, std::size_t NDIM>
1516 static std::vector<Function<T,NDIM>> pointer2WorldObject(const std::vector<std::shared_ptr<FunctionImpl<T,NDIM>>> v_impl) {
1517 std::vector<Function<T,NDIM>> vresult;
1518 vresult.resize(v_impl.size());
1519 set_impl(vresult,v_impl);
1520 return vresult;
1521 }
1522
1523 template<typename T>
1524 static ScalarResult<T> pointer2WorldObject(const std::shared_ptr<ScalarResultImpl<T>> sr_impl) {
1525 return ScalarResult(sr_impl);
1526 }
1527
1528 template<typename T>
1529 static std::vector<ScalarResult<T>> pointer2WorldObject(const std::vector<std::shared_ptr<ScalarResultImpl<T>>> v_sr_impl) {
1530 std::vector<ScalarResult<T>> vresult(v_sr_impl.size());
1531 for (size_t i=0; i<v_sr_impl.size(); ++i) {
1532 vresult[i].set_impl(v_sr_impl[i]);
1533 }
1534 return vresult;
1535 }
1536
1537 /// return the WorldObjects or the result functions living in the universe
1538
1539 /// read the pointers to the universe WorldObjects from the cloud,
1540 /// convert them to actual WorldObjects and return them
1541 resultT get_output(World &subworld, Cloud &cloud) const {
1542 resultT result;
1543
1544 // save outputrecords, because they will be consumed by the cloud
1545 auto outputrecords1 = this->outputrecords;
1546
1547 // turn an element of the tuple of pointers into an element of the tuple of WorldObjects
1548 auto doit = [&](auto& element) {
1549 typedef std::decay_t<decltype(element)> elementT;
1550
1551 // load the elements from the cloud -- they contain pointers to WorldObjects
1553 typedef typename elementT::value_type::implT implT;
1554 auto ptr_element = cloud.consuming_load<std::vector<std::shared_ptr<implT>>>(
1555 subworld, outputrecords1);
1556 element = pointer2WorldObject(ptr_element);
1557 }
1558 else if constexpr (is_madness_function<elementT>::value) {
1559 typedef typename elementT::implT implT;
1560 auto ptr_element = cloud.consuming_load<std::shared_ptr<implT>>(subworld, outputrecords1);
1561 element = pointer2WorldObject(ptr_element);
1562 }
1563 else if constexpr (is_scalar_result_vector<elementT>::value) { // std::vector<ScalarResult<T>>
1564 typedef typename elementT::value_type ScalarResultT;
1565 typedef typename ScalarResultT::implT implT;
1566 typedef std::vector<std::shared_ptr<implT>> vptrT;
1567 auto ptr_element = cloud.consuming_load<vptrT>(subworld, outputrecords1);
1568 element = pointer2WorldObject(ptr_element);
1569 }
1570 else if constexpr (is_scalar_result<elementT>::value) {
1571 // elementT is a ScalarResultImpl<T>
1572 // in cloud we store a std::shared_ptr<ScalarResultImpl<T>>
1573 auto ptr_element = cloud.consuming_load<std::shared_ptr<typename elementT::implT>>(subworld, outputrecords1);
1574 element = pointer2WorldObject(ptr_element);
1575 }
1576 else {
1577 MADNESS_EXCEPTION("confused about the type of the result", 1);
1578 }
1579 };
1580 if constexpr (is_tuple<resultT>::value) {
1581 static_assert(check_tuple_is_valid_task_result<resultT, 0>(),
1582 "invalid tuple task result -- must be vectors of functions");
1583 static_assert(is_tuple<resultT>::value, "is a tuple");
1584
1585 // loop over all tuple elements
1586 // 1. load the pointers to the WorldObjects living in the universe
1587 // 2. create WorldObjects from the pointers and copy them into the tuple of type resultT
1588
1589 // turn the tuple of pointers into a tuple of WorldObjects
1590 unary_tuple_loop(result,doit);
1591
1592
1593 } else {
1594 doit(result);
1595
1596 }
1597 return result;
1598 }
1599
1600 };
1601
1602};
1603
1605public:
1608 /// set by MacroTaskInternal::run before the task body runs, so a task that moves its
1609 /// own operands (see handles_own_data_movement) can fetch them from the cloud itself
1611 std::string name="unknown_task";
1612 std::shared_ptr<MacroTaskPartitioner> partitioner=0;
1615
1616 /// which subworld should run this batch; -1 leaves the choice to the queue
1617
1618 /// Reads whatever assignment prepare_owner_assignment computed, so the two go together:
1619 /// one decides the mapping for the whole partition, this one applies it per batch.
1620 virtual long owner_hint(const Batch&, const long /*nsubworld*/) const { return -1; }
1621
1622 /// true if the task accumulates its own results and drains them in finalize_stage2,
1623 /// instead of the queue moving every batch result into the universe result itself
1624 virtual bool accumulates_own_output() const { return false; }
1625
1626 /// true if the task moves its operand coefficients into the subworld itself
1627 virtual bool handles_own_data_movement() const { return false; }
1628
1629 /// release whatever the task kept across its batches, called once the queue has run
1630
1631 /// A task class that caches operands between its batches has to keep them in static
1632 /// state, since each batch is a separate task object. This is where that state must be
1633 /// dropped: it still refers to the subworld the batches were built in, and that
1634 /// subworld is destroyed after the queue finishes. Clearing it later means destroying
1635 /// function implementations whose world is already gone.
1636 virtual void cleanup() {}
1637};
1638
1639
1640} /* namespace madness */
1641
1642#endif /* SRC_MADNESS_MRA_MACROTASKQ_H_ */
Wrapper around MPI_Comm. Has a shallow copy constructor; use Create(Get_group()) for deep copy.
Definition safempi.h:497
static const int SHARED_SPLIT_TYPE
Definition safempi.h:657
Intracomm Split_type(int Type, int Key=0) const
Definition safempi.h:674
Intracomm Split(int Color, int Key=0) const
Definition safempi.h:642
Definition macrotaskpartitioner.h:55
a batch consists of a 2D-input batch and a 1D-output batch: K-batch <- (I-batch, J-batch)
Definition macrotaskpartitioner.h:124
cloud class
Definition cloud.h:338
void clear()
Definition cloud.h:675
void replicate_per_node(const std::size_t chunk_size=INT_MAX)
Definition cloud.h:858
nlohmann::json get_statistics(World &world) const
return a json object with the cloud settings and statistics
Definition cloud.h:503
recordlistT store(madness::World &world, const T &source)
Definition cloud.h:818
Recordlist< keyT > recordlistT
Definition cloud.h:352
std::atomic< long > target_replication_time
Definition cloud.h:948
nlohmann::json gather_timings(World &universe) const
Definition cloud.h:572
void replicate(const std::size_t chunk_size=INT_MAX)
Definition cloud.h:878
void print_size(World &universe)
Definition cloud.h:477
T load(madness::World &world, const recordlistT recordlist) const
load a single object from the cloud, recordlist is kept unchanged
Definition cloud.h:738
std::list< WorldObjectBase * > world_object_base_list
Definition cloud.h:396
static void print_memory_statistics(const nlohmann::json stats)
Definition cloud.h:641
DistributionType get_replication_policy() const
is the cloud container replicated: per rank, per node, or distributed
Definition cloud.h:452
void clear_cache(World &subworld)
Definition cloud.h:669
std::atomic< long > copy_time
Definition cloud.h:947
void set_replication_policy(const DistributionType value)
is the cloud container replicated: per rank, per node, or distributed
Definition cloud.h:445
void distribute_targets(const DistributionType dt=Distributed)
distribute/node/rank replicate the targets of all world objects stored in the cloud
Definition cloud.h:721
void print_timings(World &universe) const
backwards compatibility
Definition cloud.h:611
void set_storing_policy(const StoragePolicy value)
storing policy refers to storing functions or pointers to functions
Definition cloud.h:468
StoragePolicy
Definition cloud.h:354
@ StoreFunctionPointer
Definition cloud.h:357
@ StoreFunction
Definition cloud.h:355
T consuming_load(madness::World &world, recordlistT &recordlist) const
similar to load, but will consume the recordlist
Definition cloud.h:751
static void set_default_pmap(World &world)
Definition mraimpl.h:3715
static std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > & get_pmap()
Returns the default process map that was last initialized via set_default_pmap()
Definition funcdefaults.h:399
static void set_pmap(const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &value)
Sets the default process map (does not redistribute existing functions)
Definition funcdefaults.h:430
FunctionImpl holds all Function state to facilitate shallow copy semantics.
Definition funcimpl.h:970
A multiresolution adaptive numerical function.
Definition mra.h:144
void set_impl(const std::shared_ptr< FunctionImpl< T, NDIM > > &impl)
Replace current FunctionImpl with provided new one.
Definition mra.h:731
A future is a possibly yet unevaluated value.
Definition future.h:370
T & get(bool dowork=true) &
Gets the value, waiting if necessary.
Definition future.h:571
base class
Definition macrotaskq.h:474
virtual void finalize_stage1(World &, World *, Cloud &)
Definition macrotaskq.h:504
void set_running()
Definition macrotaskq.h:487
bool is_owned_by(const long slot) const
an unowned task may be run by any subworld, an owned one only by its own
Definition macrotaskq.h:524
virtual ~MacroTaskBase()
Definition macrotaskq.h:480
void set_waiting()
Definition macrotaskq.h:488
MacroTaskBase()
Definition macrotaskq.h:479
virtual void cleanup()=0
virtual void print_me(std::string s="") const
Definition macrotaskq.h:507
std::string print_priority_and_status_to_string() const
Definition macrotaskq.h:513
virtual bool wants_node_local_reduction() const
Finalize, for tasks that accumulate their own output.
Definition macrotaskq.h:503
void set_complete()
Definition macrotaskq.h:486
double priority
Definition macrotaskq.h:482
bool is_complete() const
Definition macrotaskq.h:490
Status
Definition macrotaskq.h:484
@ Complete
Definition macrotaskq.h:484
@ Running
Definition macrotaskq.h:484
@ Unknown
Definition macrotaskq.h:484
@ Waiting
Definition macrotaskq.h:484
void set_owner_slot(const long owner)
Definition macrotaskq.h:522
bool is_running() const
Definition macrotaskq.h:491
long get_owner_slot() const
Definition macrotaskq.h:521
enum madness::MacroTaskBase::Status stat
virtual void finalize_stage2(World &, World *, Cloud &)
Definition macrotaskq.h:505
long owner_slot
the subworld that should run this task; -1 means any
Definition macrotaskq.h:483
double get_priority() const
Definition macrotaskq.h:519
void set_priority(const double p)
Definition macrotaskq.h:520
virtual void print_me_as_table(std::string s="") const
Definition macrotaskq.h:510
virtual void run(World &world, Cloud &cloud, taskqT &taskq, const long element, const bool debug, const MacroTaskInfo policy)=0
std::vector< std::shared_ptr< MacroTaskBase > > taskqT
Definition macrotaskq.h:477
friend std::ostream & operator<<(std::ostream &os, const MacroTaskBase::Status s)
Definition macrotaskq.h:526
bool is_waiting() const
Definition macrotaskq.h:492
Definition macrotaskq.h:537
MacroTaskIntermediate()
Definition macrotaskq.h:541
void cleanup()
Definition macrotaskq.h:545
~MacroTaskIntermediate()
Definition macrotaskq.h:543
Definition macrotaskq.h:1604
Cloud * cloud_ptr
Definition macrotaskq.h:1610
MacroTaskOperationBase()
Definition macrotaskq.h:1613
Batch batch
Definition macrotaskq.h:1606
World * subworld_ptr
Definition macrotaskq.h:1607
virtual bool accumulates_own_output() const
Definition macrotaskq.h:1624
virtual long owner_hint(const Batch &, const long) const
which subworld should run this batch; -1 leaves the choice to the queue
Definition macrotaskq.h:1620
virtual ~MacroTaskOperationBase()
Definition macrotaskq.h:1614
std::shared_ptr< MacroTaskPartitioner > partitioner
Definition macrotaskq.h:1612
virtual bool handles_own_data_movement() const
true if the task moves its operand coefficients into the subworld itself
Definition macrotaskq.h:1627
virtual void cleanup()
release whatever the task kept across its batches, called once the queue has run
Definition macrotaskq.h:1636
std::string name
Definition macrotaskq.h:1611
partition one (two) vectors into 1D (2D) batches.
Definition macrotaskpartitioner.h:182
std::list< std::pair< Batch, double > > partitionT
Definition macrotaskpartitioner.h:186
Factory for the MacroTaskQ.
Definition macrotaskq.h:550
MacroTaskQFactory & set_storage_policy(const MacroTaskInfo::StoragePolicy sp)
Definition macrotaskq.h:579
World & world
Definition macrotaskq.h:553
MacroTaskQFactory & set_policy(const MacroTaskInfo p)
Definition macrotaskq.h:574
MacroTaskQFactory & set_nworld(const long n)
Definition macrotaskq.h:560
MacroTaskQFactory & preset(const std::string name)
Definition macrotaskq.h:570
MacroTaskQFactory & set_cloud_distribution_policy(const DistributionType dp)
Definition macrotaskq.h:584
MacroTaskQFactory & set_printlevel(const long p)
Definition macrotaskq.h:565
long printlevel
Definition macrotaskq.h:552
MacroTaskQFactory & set_ptr_target_distribution_policy(const DistributionType dp)
Definition macrotaskq.h:589
long nworld
Definition macrotaskq.h:554
MacroTaskInfo policy
Definition macrotaskq.h:556
MacroTaskQFactory(World &universe)
Definition macrotaskq.h:558
Definition macrotaskq.h:598
long nsubworld
Definition macrotaskq.h:607
std::mutex taskq_mutex
Definition macrotaskq.h:605
bool printdebug() const
Definition macrotaskq.h:621
static void set_pmap(World &world)
Definition macrotaskq.h:974
bool printtimings_detail() const
Definition macrotaskq.h:624
void run_all()
Definition macrotaskq.h:834
void set_complete(const long task_number) const
scheduler is located on rank==0
Definition macrotaskq.h:963
World & universe
Definition macrotaskq.h:600
std::shared_ptr< WorldDCPmapInterface< Key< 1 > > > pmap1
set the process map for the subworld
Definition macrotaskq.h:614
std::shared_ptr< WorldDCPmapInterface< Key< 3 > > > pmap3
Definition macrotaskq.h:616
MacroTaskBase::taskqT taskq
Definition macrotaskq.h:604
MacroTaskInfo get_policy() const
Definition macrotaskq.h:633
nlohmann::json cloud_statistics
save cloud statistics after run_all()
Definition macrotaskq.h:608
void add_tasks(MacroTaskBase::taskqT &vtask)
Definition macrotaskq.h:911
long get_scheduled_task_number(World &subworld)
scheduler is located on universe.rank==0
Definition macrotaskq.h:935
void set_complete_local(const long task_number) const
scheduler is located on rank==0
Definition macrotaskq.h:968
World & get_subworld()
Definition macrotaskq.h:629
MacroTaskQ(const MacroTaskQFactory factory)
create an empty taskq and initialize the subworlds
Definition macrotaskq.h:646
static std::shared_ptr< World > create_node_world(World &universe)
a World spanning the ranks that share memory with this one
Definition macrotaskq.h:684
void drain_own_output_buffers()
Move the results of tasks that accumulated their own output into the universe result.
Definition macrotaskq.h:803
std::shared_ptr< WorldDCPmapInterface< Key< 4 > > > pmap4
Definition macrotaskq.h:617
nlohmann::json get_taskq_statistics() const
Definition macrotaskq.h:641
std::size_t size() const
Definition macrotaskq.h:983
std::shared_ptr< WorldDCPmapInterface< Key< 5 > > > pmap5
Definition macrotaskq.h:618
std::shared_ptr< World > nodeworld_ptr
node-scoped World for the two-stage finalize; created on first use, reused after
Definition macrotaskq.h:603
void print_taskq() const
Definition macrotaskq.h:918
std::shared_ptr< World > subworld_ptr
Definition macrotaskq.h:601
bool printtimings() const
Definition macrotaskq.h:623
std::shared_ptr< WorldDCPmapInterface< Key< 2 > > > pmap2
Definition macrotaskq.h:615
double execute_tasks()
Run this rank's share of the queue.
Definition macrotaskq.h:737
bool printprogress() const
Definition macrotaskq.h:622
void replicate_inputs()
run all tasks
Definition macrotaskq.h:698
std::shared_ptr< WorldDCPmapInterface< Key< 6 > > > pmap6
Definition macrotaskq.h:619
nlohmann::json get_cloud_statistics() const
Definition macrotaskq.h:637
void set_printlevel(const long p)
Definition macrotaskq.h:631
long printlevel
Definition macrotaskq.h:606
madness::Cloud cloud
Definition macrotaskq.h:628
long get_nsubworld() const
Definition macrotaskq.h:630
void add_replicated_task(const std::shared_ptr< MacroTaskBase > &task)
Definition macrotaskq.h:930
~MacroTaskQ()
Definition macrotaskq.h:664
static std::shared_ptr< World > create_worlds(World &universe, const std::size_t nsubworld)
Definition macrotaskq.h:668
nlohmann::json taskq_statistics
save taskq statistics after run_all()
Definition macrotaskq.h:609
long get_scheduled_task_number_local()
Definition macrotaskq.h:947
const MacroTaskInfo policy
storage and distribution policy
Definition macrotaskq.h:611
Definition macrotaskq.h:1226
void run(World &subworld, Cloud &cloud, MacroTaskBase::taskqT &taskq, const long element, const bool debug, const MacroTaskInfo policy) override
Definition macrotaskq.h:1426
void finalize_stage2(World &subworld, World *nodeworld, Cloud &cloud) override
Definition macrotaskq.h:1335
void finalize_stage1(World &subworld, World *nodeworld, Cloud &) override
Definition macrotaskq.h:1330
taskT task
Definition macrotaskq.h:1233
void prefetch_for_next_task(World &subworld, const argtupleT &argtuple, MacroTaskBase::taskqT &taskq, const long element)
called by the MacroTaskQ when the task is scheduled
Definition macrotaskq.h:1366
void copy_operands_into_subworld(World &subworld, Cloud &cloud, argtupleT &batched_argtuple, const MacroTaskInfo &policy, const bool debug)
Bring the operand coefficients into the subworld, unless the task does that itself.
Definition macrotaskq.h:1386
std::enable_if< notis_tuple< resultT1 >::value, void >::type accumulate_into_final_result(World &subworld, resultT1 &result, const resultT1 &result_tmp, const argtupleT &argtuple)
accumulate the result of the task into the final result living in the universe
Definition macrotaskq.h:1293
resultT get_output(World &subworld, Cloud &cloud) const
return the WorldObjects or the result functions living in the universe
Definition macrotaskq.h:1541
static ScalarResult< T > pointer2WorldObject(const std::shared_ptr< ScalarResultImpl< T > > sr_impl)
Definition macrotaskq.h:1524
decay_tuple< typename taskT::argtupleT > argtupleT
Definition macrotaskq.h:1228
static std::vector< Function< T, NDIM > > pointer2WorldObject(const std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > v_impl)
Definition macrotaskq.h:1516
void cleanup() override
Definition macrotaskq.h:1504
taskT::resultT resultT
Definition macrotaskq.h:1229
recordlistT outputrecords
Definition macrotaskq.h:1231
std::string get_name() const
Definition macrotaskq.h:1234
static Function< T, NDIM > pointer2WorldObject(const std::shared_ptr< FunctionImpl< T, NDIM > > impl)
Definition macrotaskq.h:1509
static std::vector< ScalarResult< T > > pointer2WorldObject(const std::vector< std::shared_ptr< ScalarResultImpl< T > > > v_sr_impl)
Definition macrotaskq.h:1529
void print_me_as_table(std::string s="") const override
Definition macrotaskq.h:1259
bool wants_node_local_reduction() const override
Bridge the framework's virtual finalize to the task's hooks.
Definition macrotaskq.h:1325
void print_me(std::string s="") const override
Definition macrotaskq.h:1255
std::enable_if< is_tuple< resultT1 >::value, void >::type accumulate_into_final_result(World &subworld, resultT1 &final_result, const resultT1 &tmp_result, const argtupleT &argtuple)
accumulate the result of the task into the final result living in the universe
Definition macrotaskq.h:1280
MacroTaskInternal(const taskT &task, const std::pair< Batch, double > &batch_prio, const recordlistT &inputrecords, const recordlistT &outputrecords, const long owner_slot=-1)
Definition macrotaskq.h:1239
std::pair< long, MacroTaskInternal * > find_next_owned_task(const MacroTaskBase::taskqT &taskq, const long element) const
the next task in taskq that this task's subworld also owns, or {-1, nullptr}
Definition macrotaskq.h:1347
recordlistT inputrecords
Definition macrotaskq.h:1230
Definition macrotaskq.h:991
decltype(std::declval< Q & >().sym_pipeline_advance(std::declval< World & >(), std::declval< const VecT & >(), std::declval< const Batch_1D & >(), std::declval< const Batch_1D & >(), true)) has_sym_pipeline_advance_t
true if: task.sym_pipeline_advance(subworld, ket, next_col, next_row, has_next)
Definition macrotaskq.h:1039
MacroTask & set_debug(const bool value)
Definition macrotaskq.h:1123
MacroTask(World &world, taskT &task)
constructor takes the task, but no arguments to the task
Definition macrotaskq.h:1093
MacroTask(World &world, taskT &task, const MacroTaskQFactory factory)
constructor takes task and a taskq factory for customization, immediate execution
Definition macrotaskq.h:1099
taskT::resultT resultT
Definition macrotaskq.h:1080
static constexpr bool has_finalize_stage2_v
Definition macrotaskq.h:1077
std::shared_ptr< MacroTaskQ > taskq_ptr
Definition macrotaskq.h:1088
taskT::argtupleT argtupleT
Definition macrotaskq.h:1081
bool immediate_execution
Definition macrotaskq.h:1086
decltype(std::declval< Q & >().finalize_stage1(std::declval< World & >(), std::declval< World * >())) has_finalize_stage1_t
true if: task.finalize_stage1(subworld, nodeworld)
Definition macrotaskq.h:1065
static constexpr bool has_wants_node_local_reduction_v
Definition macrotaskq.h:1058
static constexpr bool has_finalize_stage1_v
Definition macrotaskq.h:1067
World & world
Definition macrotaskq.h:1087
std::shared_ptr< MacroTaskQ > get_taskq() const
Definition macrotaskq.h:1128
resultT operator()(const Ts &... args)
this mimicks the original call to the task functor, called from the universe
Definition macrotaskq.h:1138
static constexpr bool has_accumulate_locally_v
Definition macrotaskq.h:1050
static constexpr bool has_sym_pipeline_advance_v
Definition macrotaskq.h:1041
decltype(std::declval< Q & >().accumulate_locally(std::declval< World & >(), std::declval< const ResT & >())) has_accumulate_locally_t
true if: task.accumulate_locally(subworld, result_subworld)
Definition macrotaskq.h:1048
bool debug
Definition macrotaskq.h:1085
taskT task
Definition macrotaskq.h:1084
MacroTaskPartitioner::partitionT partitionT
Definition macrotaskq.h:992
decltype(std::declval< Q & >().store_batches(std::declval< World & >(), std::declval< World & >(), std::declval< Cloud & >(), std::declval< const ArgTuple & >(), 0L)) has_store_batches_t
true if: task.store_batches(world, subworld, cloud, argtuple, nsubworld)
Definition macrotaskq.h:1029
decltype(std::declval< Q & >().finalize_stage2(std::declval< World & >(), std::declval< World * >(), std::declval< ResT & >())) has_finalize_stage2_t
true if: task.finalize_stage2(subworld, nodeworld, universe_result)
Definition macrotaskq.h:1075
Cloud::recordlistT recordlistT
Definition macrotaskq.h:1082
static constexpr bool has_store_batches_v
Definition macrotaskq.h:1031
static constexpr bool has_prepare_owner_assignment_v
Definition macrotaskq.h:1021
recordlistT prepare_output_records(Cloud &cloud, resultT &result)
store pointers to the result WorldObject in the cloud and return the recordlist
Definition macrotaskq.h:1179
decltype(std::declval< Q & >().prepare_owner_assignment(std::declval< const MacroTaskPartitioner::partitionT & >(), 0L)) has_prepare_owner_assignment_t
true if: task.prepare_owner_assignment(partition, nsubworld)
Definition macrotaskq.h:1019
MacroTask(World &world, taskT &task, std::shared_ptr< MacroTaskQ > taskq_ptr)
constructor takes the task, and a taskq, execution is not immediate
Definition macrotaskq.h:1105
decltype(std::declval< const Q & >().wants_node_local_reduction()) has_wants_node_local_reduction_t
true if: task.wants_node_local_reduction()
Definition macrotaskq.h:1056
static std::map< MemKey, MemInfo > measure_and_print(World &world)
measure the memory usage of all objects of all worlds
Definition memory_measurement.h:24
helper class for returning the result of a task, which is not a madness Function, but a simple scalar
Definition macrotaskq.h:55
void gaxpy(const double a, const T &right, double b, const bool fence=true)
accumulate, optional fence
Definition macrotaskq.h:84
void serialize(Archive &ar)
Definition macrotaskq.h:92
T get()
after completion of the taskq get the final value
Definition macrotaskq.h:97
ScalarResultImpl< T > & operator=(const T &x)
simple assignment of the scalar value
Definition macrotaskq.h:73
ScalarResultImpl(const ScalarResultImpl &other)=delete
Disable the default copy constructor.
ScalarResultImpl< T > & operator=(const ScalarResultImpl< T > &other)=delete
disable assignment operator
ScalarResultImpl< T > & operator+=(const T &x)
Definition macrotaskq.h:78
~ScalarResultImpl()
Definition macrotaskq.h:69
T value
the scalar value
Definition macrotaskq.h:110
T get_local() const
Definition macrotaskq.h:104
T value_type
Definition macrotaskq.h:57
ScalarResultImpl(World &world)
Definition macrotaskq.h:58
Definition macrotaskq.h:114
void serialize(Archive &ar)
Definition macrotaskq.h:145
ScalarResult(const std::shared_ptr< implT > &impl)
Definition macrotaskq.h:121
void gaxpy(const double a, const T &right, double b, const bool fence=true)
accumulate, optional fence
Definition macrotaskq.h:140
ScalarResultImpl< T > implT
Definition macrotaskq.h:116
T get()
after completion of the taskq get the final value
Definition macrotaskq.h:150
ScalarResult(World &world)
Definition macrotaskq.h:120
std::shared_ptr< implT > impl
Definition macrotaskq.h:117
ScalarResult & operator=(const T &x)
Definition macrotaskq.h:122
std::shared_ptr< implT > get_impl() const
Definition macrotaskq.h:127
void set_impl(const std::shared_ptr< implT > &newimpl)
Definition macrotaskq.h:131
T get_local() const
after completion of the taskq get the final value
Definition macrotaskq.h:155
uniqueidT id() const
Definition macrotaskq.h:135
void broadcast_serializable(objT &obj, ProcessID root)
Broadcast a serializable object.
Definition worldgop.h:774
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:176
bool set_forbid_fence(bool value)
Set forbid_fence flag to new value and return old value.
Definition worldgop.h:677
void sum(T *buf, size_t nelem)
Inplace global sum while still processing AM & tasks.
Definition worldgop.h:890
SafeMPI::Intracomm & comm()
Returns the associated SafeMPI communicator.
Definition worldmpi.h:286
Implements most parts of a globally addressable object (via unique ID).
Definition world_object.h:491
void process_pending()
To be called from derived constructor to process pending messages.
Definition world_object.h:787
detail::task_result_type< memfnT >::futureT send(ProcessID dest, memfnT memfn) const
Definition world_object.h:858
detail::task_result_type< memfnT >::futureT task(ProcessID dest, memfnT memfn, const TaskAttributes &attr=TaskAttributes()) const
Sends task to derived class method returnT (this->*memfn)().
Definition world_object.h:1132
A parallel world class.
Definition world.h:134
static World * world_from_id(std::uint64_t id)
Convert a World ID to a World pointer.
Definition world.h:516
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:344
WorldMpiInterface & mpi
MPI interface.
Definition world.h:213
ProcessID size() const
Returns the number of processes in this World (same as MPI_Comm_size()).
Definition world.h:354
unsigned long id() const
Definition world.h:324
WorldGopInterface & gop
Global operations.
Definition world.h:216
std::optional< T * > ptr_from_id(uniqueidT id) const
Look up a local pointer from a world-wide unique ID.
Definition world.h:440
Class for unique global IDs.
Definition uniqueid.h:53
Declares the Cloud class for storing data and transfering them between worlds.
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
const std::size_t bufsize
Definition derivatives.cc:16
static bool debug
Definition dirac-hatom.cc:16
Tensor< typename Tensor< T >::scalar_type > arg(const Tensor< T > &t)
Return a new tensor holding the argument of each element of t (complex types only)
Definition tensor.h:2643
static const double v
Definition hatom_sf_dirac.cc:20
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_ASSERT(condition)
Assert a condition that should be free of side-effects since in release builds this might be a no-op.
Definition madness_exception.h:134
#define MADNESS_CHECK_THROW(condition, msg)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:207
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
std::vector< ScalarResult< T > > scalar_result_vector(World &world, std::size_t n)
helper function to create a vector of ScalarResultImpl, circumventing problems with the constructors
Definition macrotaskq.h:163
std::ostream & operator<<(std::ostream &os, const particle< PDIM > &p)
Definition lowrankfunction.h:401
double get_rss_usage_in_GB()
Definition ranks_and_hosts.cpp:10
static double cpu_time()
Returns the cpu time in seconds relative to an arbitrary origin.
Definition timers.h:128
DistributionType
some introspection of how data is distributed
Definition worlddc.h:81
@ NodeReplicated
even if there are several ranks per node
Definition worlddc.h:84
@ Distributed
no replication of the container, the container is distributed over the world
Definition worlddc.h:82
@ RankReplicated
replicate the container over all world ranks
Definition worlddc.h:83
void set_impl(std::vector< Function< T, NDIM > > &v, const std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > vimpl)
Definition vmra.h:738
std::vector< std::shared_ptr< FunctionImpl< T, NDIM > > > get_impl(const std::vector< Function< T, NDIM > > &v)
Definition vmra.h:731
TreeState
Definition funcdefaults.h:59
@ reconstructed
s coeffs at the leaves only
Definition funcdefaults.h:60
@ compressed
d coeffs in internal nodes, s and d coeffs at the root, empty leaves may be present
Definition funcdefaults.h:61
constexpr bool check_tuple_is_valid_task_result()
given a tuple check recursively if all elements are valid task results
Definition macrotaskq.h:222
static const Slice _(0,-1, 1)
static void binary_tuple_loop(tupleT &tuple1, tupleR &tuple2, opT &op)
loop over the tuple elements of both tuples and execute the operation op on each element pair
Definition type_traits.h:742
void print(const T &t, const Ts &... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:227
static void unary_tuple_loop(tupleT &tuple, opT &op)
loop over a tuple and apply unary operator op to each element
Definition type_traits.h:732
@ TT_FULL
Definition gentensor.h:120
NDIM & f
Definition mra.h:2622
constexpr bool is_valid_task_result_v
check if type is a valid task result: it must be a WorldObject and must implement gaxpy
Definition macrotaskq.h:208
const Function< T, NDIM > & change_tree_state(const Function< T, NDIM > &f, const TreeState finalstate, bool fence=true)
change tree state of a function
Definition mra.h:2948
decltype(decay_types(std::declval< T >())) decay_tuple
Definition macrotaskpartitioner.h:22
double wall_time()
Returns the wall time in seconds relative to an arbitrary origin.
Definition timers.cc:48
std::string type(const PairType &n)
Definition PNOParameters.h:18
constexpr Vector< T, sizeof...(Ts)+1 > vec(T t, Ts... ts)
Factory function for creating a madness::Vector.
Definition vector.h:750
std::string name(const FuncType &type, const int ex=-1)
Definition ccpairfunction.h:28
Function< T, NDIM > copy(const Function< T, NDIM > &f, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool fence=true)
Create a new copy of the function with different distribution and optional fence.
Definition mra.h:2187
void gaxpy(const double a, ScalarResult< T > &left, const double b, const T &right, const bool fence=true)
the result type of a macrotask must implement gaxpy
Definition macrotaskq.h:244
Definition mraimpl.h:51
static const double b
Definition nonlinschro.cc:119
static const double a
Definition nonlinschro.cc:118
static const double L
Definition rk.cc:46
Definition cloud.h:967
Definition macrotaskq.h:280
friend std::string to_string(const MacroTaskInfo::StoragePolicy sp)
Definition macrotaskq.h:438
StoragePolicy
Definition macrotaskq.h:281
@ StoreFunction
store a madness function in the cloud – can have a large memory impact
Definition macrotaskq.h:282
@ StorePointerToFunction
Definition macrotaskq.h:283
@ StoreFunctionViaPointer
coefficients to the subworlds when the task is started. This is the default policy.
Definition macrotaskq.h:286
static std::vector< MacroTaskInfo > get_all_presets()
helper function to return all presets
Definition macrotaskq.h:350
StoragePolicy storage_policy
Definition macrotaskq.h:426
DistributionType ptr_target_distribution_policy
Definition macrotaskq.h:428
static MacroTaskInfo preset(const std::string name)
Definition macrotaskq.h:313
static std::vector< std::string > get_all_preset_names()
Definition macrotaskq.h:345
static Cloud::StoragePolicy to_cloud_storage_policy(MacroTaskInfo::StoragePolicy policy)
given the MacroTask's storage policy return the corresponding Cloud storage policy
Definition macrotaskq.h:298
DistributionType cloud_distribution_policy
Definition macrotaskq.h:427
friend std::ostream & operator<<(std::ostream &os, const MacroTaskInfo policy)
Definition macrotaskq.h:430
bool check_consistency() const
make sure the policies are consistent
Definition macrotaskq.h:359
friend std::ostream & operator<<(std::ostream &os, const StoragePolicy sp)
Definition macrotaskq.h:290
static StoragePolicy policy_to_string(const std::string policy)
Definition macrotaskq.h:444
nlohmann::json to_json() const
Definition macrotaskq.h:454
void from_vector_of_strings(const std::vector< std::string > &vec)
set policy from a vector of strings, assuming the order is storage policy, cloud distribution policy,...
Definition macrotaskq.h:380
Definition macrotaskq.h:995
World & world
Memoized reference to the world to which this object belongs.
Definition world_object.h:348
World & get_world() const
Definition world_object.h:446
Default load of an object via serialize(ar, t).
Definition archive.h:667
Default store of an object via serialize(ar, t).
Definition archive.h:612
static std::string tolower(std::string s)
make lower case
Definition commandlineparser.h:128
class to temporarily redirect output to cout
Definition print.h:300
RAII class to redirect cout to a file or to /dev/null.
Definition print.h:252
Definition type_traits.h:756
Definition mra.h:2996
Definition macrotaskq.h:193
Definition macrotaskq.h:178
Definition macrotaskq.h:172
Definition macrotaskq.h:199
Definition macrotaskq.h:187
Definition macrotaskq.h:217
static void load(const Archive &ar, std::shared_ptr< ScalarResultImpl< T > > &ptr)
Definition macrotaskq.h:260
static void store(const Archive &ar, const std::shared_ptr< ScalarResultImpl< T > > &ptr)
Definition macrotaskq.h:250
Definition timing_utilities.h:9
static const double_complex I
Definition tdse1d.cc:164
void doit(World &world)
Definition tdse.cc:921
void e()
Definition test_sig.cc:75
Declares the World class for the parallel runtime environment.
int ProcessID
Used to clearly identify process number/rank.
Definition worldtypes.h:43