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