|
| | MacroTaskExchangeSimple (const long nresult, const double lo, const double mul_tol, const bool symmetric, const bool owner_pinned=false, const long granularity_level=1, const long universe_rank=0, const int accumulation_mode=2, const bool cost_aware=true, const long batch_salt=0, const bool bra_shares_ket=true, const bool vf_shares_ket=true) |
| |
| void | accumulate_locally (World &subworld, const vecfuncT &result_subworld) const |
| | sum one tile's result into this subworld's accumulator
|
| |
| bool | accumulates_own_output () const override |
| |
| resultT | allocator (World &world, const argtupleT &argtuple) const |
| |
| int | bra_role () const |
| | the record role carrying the bra, and the one carrying the vf over a shared split
|
| |
| void | cleanup () override |
| | Drop the cached batches while the subworld holding them is still alive.
|
| |
| vecfuncT | compute_batch_in_asymmetric_matrix (World &subworld, const vecfuncT &ket_batch, const vecfuncT &bra_batch, const vecfuncT &vf_batch) const |
| | compute a batch of the exchange matrix, with non-identical ranges
|
| |
| vecfuncT | compute_diagonal_batch_in_symmetric_matrix (World &subworld, const vecfuncT &ket_batch, const vecfuncT &bra_batch, const vecfuncT &vf_batch) const |
| | compute a batch of the exchange matrix, with identical ranges, exploiting the matrix symmetry
|
| |
| std::pair< vecfuncT, vecfuncT > | compute_offdiagonal_batch_in_symmetric_matrix (World &subworld, const vecfuncT &ket_rows, const vecfuncT &ket_columns, const vecfuncT &bra_batch, const vecfuncT &vf_batch) const |
| | compute a batch of the exchange matrix, with non-identical ranges
|
| |
| void | ensure_node_accumulator (World &nodeworld) const |
| | Collectively (re)build the node-shared accumulator in the node world.
|
| |
| void | finalize_stage1 (World &subworld, World *nodeworld) |
| | reduce this subworld's accumulator into the node-shared one
|
| |
| void | finalize_stage2 (World &subworld, World *nodeworld, vecfuncT &universe_result) |
| | drain into the universe result, from the node accumulator if there is one
|
| |
| bool | handles_own_data_movement () const override |
| | the owner-pinned path fetches its operand batches from the cloud itself
|
| |
| std::vector< Function< T, NDIM > > | operator() (const std::vector< Function< T, NDIM > > &vf_batch, const std::vector< Function< T, NDIM > > &bra_batch, const std::vector< Function< T, NDIM > > &vket) |
| |
| long | owner_hint (const Batch &task_batch, const long nsubworld) const override |
| |
| void | prepare_owner_assignment (const MacroTaskPartitioner::partitionT &partition, const long nsubworld) |
| | Assign every task to the rank that will own one of its two batches.
|
| |
| bool | profile_active () const |
| | per-task profiling on for this task?
|
| |
| void | store_batches (World &world, World &subworld, Cloud &cloud, const argtupleT &argtuple, const long nsubworld) |
| | Store the orbitals as owner-pinned batches, one record per batch.
|
| |
| void | sym_pipeline_advance (World &subworld, const vecfuncT &, const Batch_1D &next_col, const Batch_1D &next_row, const bool has_next) const |
| | Request the batch the next task will have to fetch, before computing this one.
|
| |
| std::vector< long > | touched_result_indices () const |
| | The result entries this tile actually wrote.
|
| |
| int | vf_role () const |
| |
| bool | wants_node_local_reduction () const |
| |
| | MacroTaskOperationBase () |
| |
| virtual | ~MacroTaskOperationBase () |
| |
|
| static std::map< long, long > | batch_begin_to_index_ |
| | batch offset -> index
|
| |
| static ExchangeBatchLRU< long, vecfuncT > | batch_cache_ |
| | Batches fetched from the cloud, reused across the tasks that run in one subworld.
|
| |
| static std::atomic< long > | batch_cache_hits_ |
| |
| static std::atomic< long > | batch_cache_misses_ |
| |
| static std::atomic< long > | batch_prefetch_hits_ |
| |
| static long | cache_world_id_ = -1 |
| |
| static std::vector< double > | cost_reference_ |
| | What each task cost last time, to place them better this time.
|
| |
| static std::vector< double > | cost_this_call_ |
| | rank-local, summed after the call
|
| |
| static long | exchange_call_index_ = 0 |
| |
| static bool | finalize_stage1_done_ = false |
| | each drain happens once per rank, not once per task object
|
| |
| static bool | finalize_universe_done_ = false |
| |
| static vecfuncT | Kf_local_ |
| |
| static bool | Kf_local_initialized_ = false |
| |
| static long | Kf_local_world_id_ = -1 |
| |
| static vecfuncT | Kf_node_ |
| |
| static bool | Kf_node_initialized_ = false |
| |
| static long | Kf_node_world_id_ = -1 |
| |
| static std::shared_ptr< FinalizeReducer< T, NDIM > > | node_reducer_ |
| |
| static long | node_reducer_world_id_ = -1 |
| |
| static PrefetchSlot | prefetch_current_ |
| | promoted from the previous task
|
| |
| static PrefetchSlot | prefetch_next_ |
| | requested during this task
|
| |
| static long | prof_task_seq_ = 0 |
| | per-process task counter, for identity only
|
| |
| static std::shared_ptr< FinalizeReducer< T, NDIM > > | universe_reducer_ |
| | Receiving endpoints for the two drains, one per world they transfer within.
|
| |
| static long | universe_reducer_world_id_ = -1 |
| |
compute a batch of the exchange matrix, with identical ranges, exploiting the matrix symmetry
- Parameters
-
| subworld | the world we're computing in |
| cloud | where to store the results |
| bra_batch | the bra batch of orbitals (including the nuclear correlation factor square) |
| ket_batch | the ket batch of orbitals, i.e. the orbitals to premultiply with |
| vf_batch | the argument of the exchange operator Streams the tile one row at a time, so only the intermediates of a single row are live at once where computing the tile in one go holds the whole triangle. Row i builds N_ij = P(bra[i] vf[j]) for j <= i, adds ket[i] N_ij to column j, and adds the mirrored ket[j] N_ij to column i. |
References apply(), madness::ExchTaskProfile::apply_wall, madness::compress(), cpu_time(), madness::gaxpy(), lo, MADNESS_CHECK_THROW, madness::ExchTaskProfile::mul1_wall, madness::ExchTaskProfile::mul2_wall, madness::mul_sparse(), truncate(), madness::ExchTaskProfile::truncate_wall, update(), and madness::wall_time().
compute a batch of the exchange matrix, with non-identical ranges
The caller supplies the ket over each of the tile's two ranges: it is the one that knows the ranges, and where those orbitals come from depends on how the operands were supplied, which is not the kernel's concern.
Streams the tile one bra row at a time, so only one row of intermediates is live at once where building the tile in one go holds all nrow*ncolumn of them. Row irow builds N_ij = P(bra[irow] vf[j]) over the whole column range and contributes to both result ranges: ket[irow] N_ij to column j, and the sum over j of ket[j] N_ij to row irow.
- Parameters
-
| subworld | the world we're computing in |
| ket_rows | the orbitals to premultiply with, over the bra/row range |
| ket_columns | the orbitals to premultiply with, over the vf/column range |
| bra_batch | the bra batch of orbitals (including the nuclear correlation factor square) |
| vf_batch | the argument of the exchange operator |
References madness::apply(), madness::compress(), madness::cpu_time(), madness::dot(), madness::gaxpy(), lo, MADNESS_CHECK_THROW, madness::mul_sparse(), madness::nonlinear_vector_solver(), madness::truncate(), and madness::wall_time().
Collectively (re)build the node-shared accumulator in the node world.
Must be reached by every rank of nodeworld, since constructing a Function is collective; the queue drives this uniformly across the replicated task list, so the initialized flag flips in lockstep. The process map is passed explicitly because the process-wide default is the subworld's during the finalize – inheriting it would map keys to subworld rank indices for functions that live in the node world.
References madness::compressed, madness::WorldGopInterface::fence(), madness::World::gop, and madness::World::id().
Assign every task to the rank that will own one of its two batches.
Called by the macrotask queue after partitioning and before it asks for each task's owner. The batch boundaries come from the same split the partitioner used, so a task's (column, row) batch offsets identify a pair of batch indices, and exchange_sym_round_robin_assign turns that pair into an owner. Every rank runs this over the same partition and gets the same map without communicating.
References c, madness::exchange_row_owner_assign(), madness::exchange_sym_cost_aware_assign(), madness::exchange_sym_owner_split(), madness::exchange_sym_round_robin_assign(), k, MADNESS_CHECK_THROW, and split().
Store the orbitals as owner-pinned batches, one record per batch.
Called by the macrotask queue on the universe right after the argument tuple is stored. The batch boundaries and the record keys are derived exactly as the task side derives them, so no manifest has to be communicated.
Every rank registers the routing for all records, which is local and needs no communication, and then each owner pulls the batches it owns into its own size-1 subworld and serializes them there. That is what spreads the ingest across the owners: serializing centrally instead funnels the whole orbital set through one rank's network interface.
A record is stored per distinct operand vector, not per role: HF exchange passes one vector as all three and still stores a single set, nemo's bra = R^2 * ket makes two, and three only when all three differ. The symmetric grid shares one split; the asymmetric one puts vf on the column boundaries and bra/ket on the row ones, so vf needs its own record there even when it is the ket.
References madness::Batch_1D::begin, copy(), madness::Batch_1D::end, madness::EXCHANGE_BATCH_BRA, madness::EXCHANGE_BATCH_KET, madness::exchange_batch_record_key(), madness::EXCHANGE_BATCH_VF, madness::exchange_row_owner_split(), madness::exchange_sym_owner_split(), madness::WorldGopInterface::fence(), madness::World::gop, k, MADNESS_CHECK_THROW, madness::World::rank(), madness::Cloud::register_batch_owner(), madness::Batch_1D::size(), madness::World::size(), split(), madness::Cloud::store_batch(), and v.
Request the batch the next task will have to fetch, before computing this one.
Called by the queue once per task, before the task body, with the batches of the next task this rank will run. Of that task's two batches one is normally owned here and reads locally, so at most one is worth requesting – and requesting exactly one keeps the in-flight count within the bound PrefetchSlot documents.
This is what makes the owner-pinned transport worth its machinery: without it every task pays the full latency of its remote batch with nothing to overlap it against.
- Parameters
-
| mo_ket | unused: the salt it used to be derived from is carried on the task. It stays in the signature because the queue's detection trait matches on it (has_sym_pipeline_advance_v). |
References madness::Cloud::batch_owner(), madness::ExchangeBatchLRU< keyT, dataT >::contains(), madness::EXCHANGE_BATCH_KET, madness::exchange_batch_record_key(), madness::Exchange< T, NDIM >::ExchangeImpl< T, NDIM >::MacroTaskExchangeSimple::PrefetchSlot::fut, madness::Exchange< T, NDIM >::ExchangeImpl< T, NDIM >::MacroTaskExchangeSimple::PrefetchSlot::key, madness::Cloud::request_batch_bytes_async(), and madness::Exchange< T, NDIM >::ExchangeImpl< T, NDIM >::MacroTaskExchangeSimple::PrefetchSlot::valid.
The result entries this tile actually wrote.
operator() scatters into a full-width Kf and leaves everything else zero, so summing all nresult entries would gaxpy mostly zeros – a per-tile cost proportional to the whole result vector. Every tile writes its column range; a symmetric off-diagonal tile also writes its row range, reusing each intermediate for the transposed element, whereas an asymmetric tile contributes to its column alone. A full-size or absent range falls back to all of them.
The result must be a set: the caller gaxpys one entry per index, so a repeated index is added twice. The two ranges do overlap in the asymmetric case, coming from separate splits of different lengths rather than from one split, where they are always equal or disjoint.
References b, and e().
What each task cost last time, to place them better this time.
Screening makes the tiles strongly uneven for large molecules, and a placement that balances task counts cannot see that. The measured wall time of each tile is recorded here, summed across ranks after the call – each tile ran on exactly one rank, so summing unions the contributions – and used as the reference for the next call. Kept across calls and across protocol changes on purpose: only the relative cost matters, and its structure barely moves between them. from the previous call
Receiving endpoints for the two drains, one per world they transfer within.
These are WorldObjects, so they are bound to a world exactly as a Function is, and the same lifetime rule applies: release them while that world is still alive. The node world is built per application (the queue owns it and a fresh queue is built per call), so a cached node reducer that survives the application is registered in a world that no longer exists. The world id is kept beside the pointer rather than read back out of it, because comparing reducer->get_world().id() is itself a read of the dead world.