Comment most of Slice section

This commit is contained in:
Alejandro Gallo 2021-10-13 17:47:58 +02:00
parent 06c9f31bcd
commit 51c3203fda
2 changed files with 868 additions and 646 deletions

375
atrip.org
View File

@ -8,7 +8,9 @@ The algorithm uses two main data types, the =Slice= and the
** The slice ** The slice
The following section introduces the idea of a slice.
*** Prolog :noexport:
#+begin_src c++ :tangle (atrip-slice-h) #+begin_src c++ :tangle (atrip-slice-h)
#pragma once #pragma once
#include <iostream> #include <iostream>
@ -26,6 +28,7 @@ struct Slice {
using F = double; using F = double;
#+end_src #+end_src
*** Introduction
A slice is the concept of a subset of values of a given tensor. A slice is the concept of a subset of values of a given tensor.
As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds of objects: As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds of objects:
@ -35,13 +38,63 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
- the object \( \mathsf{T}(a,b)_{ij} \) which for every pair of \( a, b \) - the object \( \mathsf{T}(a,b)_{ij} \) which for every pair of \( a, b \)
corresponds the \( N_\mathrm{o}^2 \)-sized tensor \( T^{ab}_{ij} \). corresponds the \( N_\mathrm{o}^2 \)-sized tensor \( T^{ab}_{ij} \).
*** Location
Every slice set, for instance,
\( S_k = \left\{
a \mapsto \mathsf{T}(a)^{b}_{ij}
\mid
a \in A_k
\right\} \)
where \( A_k \) is some subset of
\( \mathsf{N}_\mathrm{v} \),
gets stored in some rank \( k \).
In general however, the number of elements in \( A_k \) can be bigger
than the number of processes \( n_p \). Therefore in order to uniquely
indentify a given slice in \( S_k \) we need two identifiers,
the rank \( k \), which tells us in which core's memory the slice is
allocated, and an additional tag which we will call =source=.
The datatype that simply models this state of affairs
is therefore a simple structure:
#+begin_src c++ :tangle (atrip-slice-h)
struct Location { size_t rank; size_t source; };
#+end_src
*** Type
Due to the permutation operators in the equations
it is noticeable that for every one dimensional
slice and triple \( (a,b,c) \)
\begin{equation*}
a \mapsto \mathsf{t}(a)
\end{equation*}
one needs at the same time
\( \mathsf{t}(a) \),
\( \mathsf{t}(b) \) and
\( \mathsf{t}(c) \).
For two dimensional slices, i.e., slices of the form
\begin{equation*}
(a,b) \mapsto \mathsf{t}(a,b)
\end{equation*}
one needs in the equations the slices
\( \mathsf{t}(a,b) \),
\( \mathsf{t}(b,c) \) and
\( \mathsf{t}(a,c) \).
In addition, in the case of diagrams where
the integral \( V^{ab}_{ci} \) appears,
we additionaly need the permuted slices
from before, i.e.
\( \mathsf{t}(b,a) \),
\( \mathsf{t}(c,b) \) and
\( \mathsf{t}(c,a) \).
This means, every slice has associated with it
a type which denotes which permutation it is.
#+begin_src c++ :tangle (atrip-slice-h) #+begin_src c++ :tangle (atrip-slice-h)
// ASSOCIATED TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
struct Location { size_t rank; size_t source; };
enum Type enum Type
{ A = 10 { A = 10
, B , B
@ -57,29 +110,64 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
// The non-typed slice // The non-typed slice
, Blank = 404 , Blank = 404
}; };
#+end_src
*** State
Every slice can be in different states and every state
denotes which function the slice is going to provide
and which relations they have between themselves.
- Fetch ::
A slice is in state =Fetch= when it
has a valid data pointer that **must** be written to.
A =Fetch= slice should not live very long, this means
that after the database send and receive phase,
=Fetch= slices should be changed into =Dispatched=
in order to start the process of writing to the
data pointer from some other rank.
- Dispatched ::
A =Dispatched= slice indicates that at some point
send and receive MPI calls have been dispatched
in order to get the data.
However, the calls have just been dispatched and there
is no warranty for the data to be there, for that,
the slice must be unwrapped.
- Ready ::
=Ready= means that the data pointer can be read from
directly.
- SelfSufficient ::
A slice is =SelfSufficient= when its contents are located
in the same rank that it lives, so that it does not have to
fetch from no other rank.
This is important in order to handle the data pointers correctly
and in order to save calls to MPI receive and send functions.
- Recycled ::
=Recycled= means that this slice gets its data pointer from another
slice, so it should not be written to
- Acceptor ::
=Acceptor= means that the slice can accept a new slice, it is
the counterpart of the =Blank= type, but for states
Again the implementation is a simple enum type.
#+begin_src c++ :tangle (atrip-slice-h)
enum State { enum State {
// Fetch represents the state where a slice is to be fetched
// and has a valid data pointer that can be written to
Fetch = 0, Fetch = 0,
// Dispatches represents the state that an MPI call has been
// dispatched in order to get the data, but the data has not been
// yet unwrapped, the data might be there or we might have to wait.
Dispatched = 2, Dispatched = 2,
// Ready means that the data pointer can be read from
Ready = 1, Ready = 1,
// Self sufficient is a slice when its contents are located
// in the same rank that it lives, so that it does not have to
// fetch from no one else.
SelfSufficient = 911, SelfSufficient = 911,
// Recycled means that this slice gets its data pointer from another
// slice, so it should not be written to
Recycled = 123, Recycled = 123,
// Acceptor means that the Slice can accept a new Slice, it is
// the counterpart of the Blank type, but for states
Acceptor = 405 Acceptor = 405
}; };
#+end_src
*** The Info structure
Every slice has an information structure associated with it
that keeps track of the **variable** type, state and so on.
#+begin_src c++ :tangle (atrip-slice-h)
struct Info { struct Info {
// which part of a,b,c the slice holds // which part of a,b,c the slice holds
PartialTuple tuple; PartialTuple tuple;
@ -88,7 +176,6 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
// What is the state of the slice // What is the state of the slice
State state; State state;
// Where the slice is to be retrieved // Where the slice is to be retrieved
// NOTE: this can actually be computed from tuple
Location from; Location from;
// If the data are actually to be found in this other slice // If the data are actually to be found in this other slice
Type recycling; Type recycling;
@ -102,8 +189,23 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
}; };
using Ty_x_Tu = std::pair< Type, PartialTuple >; using Ty_x_Tu = std::pair< Type, PartialTuple >;
#+end_src
// Names of the integrals that are considered in CCSD(T) *** Name
CCSD(T) needs in this algorithm 5 types of tensor slices,
namely
\( V^{ij}_{ka} \), \( V^{ab}_{ci} \),
\( V^{ab}_{ij} \)
and two times \( T^{ab}_{ij} \).
The reason why we need two times the doubles
amplitudes is because in the doubles contribution
to the energy, the \( T \) amplidutes will be sliced
through one parameter for the particle contribution
and through two parameters for the hole contribution.
#+begin_src c++ :tangle (atrip-slice-h)
enum Name enum Name
{ TA = 100 { TA = 100
, VIJKA = 101 , VIJKA = 101
@ -111,20 +213,31 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
, TABIJ = 201 , TABIJ = 201
, VABIJ = 202 , VABIJ = 202
}; };
#+end_src
// DATABASE ==========================================================={{{1 *** Database
The database is a simple representation of the slices of a slice union.
Every element of the database is given by the name of the tensor it
represents and the internal information structure.
#+begin_src c++ :tangle (atrip-slice-h)
struct LocalDatabaseElement { struct LocalDatabaseElement {
Slice::Name name; Slice::Name name;
Slice::Info info; Slice::Info info;
}; };
#+end_src
A local database (of a given rank) and the global database is thus simply
a vector of these elements.
#+begin_src c++ :tangle (atrip-slice-h)
using LocalDatabase = std::vector<LocalDatabaseElement>; using LocalDatabase = std::vector<LocalDatabaseElement>;
using Database = LocalDatabase; using Database = LocalDatabase;
#+end_src
*** MPI Types
// STATIC METHODS =========================================================== #+begin_src c++ :tangle (atrip-slice-h)
//
// They are useful to organize the structure of slices
struct mpi { struct mpi {
static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) { static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
@ -212,7 +325,27 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
} }
}; };
#+end_src
*** Static utilities
This section presents some functions which are useful to work with
slices and are inside the namespace created by the slice struct.
The function =subtupleBySlice= gives to every =Slice::Type=
its meaning in terms of the triples \( (a,b,c) \).
Notice that since in general the relation
\( a < b < c \) holds (in our implementation), the case
of one-dimensional parametrizations =A=, =B= and =C= is well
defined.
The function should only throw if there is an implementation
error where the =Slice::Type= enum has been expanded and this
function has not been updated accordingly.
#+begin_src c++ :tangle (atrip-slice-h)
static static
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) { PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
switch (sliceType) { switch (sliceType) {
@ -228,30 +361,17 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
default: throw "Switch statement not exhaustive!"; default: throw "Switch statement not exhaustive!";
} }
} }
#+end_src
In the context of cleaning up slices during the main loop,
it is important to check if a given slice has some slices
referencing to it in quality of recycled slices.
/** This function should therefore return a vector of pointers
,* It is important here to return a reference to a Slice of slices referencing to the given slice's info, when
,* not to accidentally copy the associated buffer of the slice. the length of the vector is zero, then there are no dangling
,*/ links.
static Slice& findOneByType(std::vector<Slice> &slices, Slice::Type type) { #+begin_src c++ :tangle (atrip-slice-h)
const auto sliceIt
= std::find_if(slices.begin(), slices.end(),
[&type](Slice const& s) {
return type == s.info.type;
});
WITH_CRAZY_DEBUG
WITH_RANK
<< "\t__ looking for " << type << "\n";
if (sliceIt == slices.end())
throw std::domain_error("Slice by type not found!");
return *sliceIt;
}
/*
,* Check if an info has
,*
,*/
static std::vector<Slice*> hasRecycledReferencingToIt static std::vector<Slice*> hasRecycledReferencingToIt
( std::vector<Slice> &slices ( std::vector<Slice> &slices
, Info const& info , Info const& info
@ -266,7 +386,35 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
return result; return result;
} }
#+end_src
The rest of the coming functions are utilities in order to find in a vector
of slices a given slice by reference. Mostly they are merely convenience
wrappers to the standard library function =std::find_if=.
They are named as =find<...>=, where =<...>= represents some condition
and must always return a reference to the found slice, i.e., =Slice&=.
=Atrip= relies on these functions to find the sought for slices,
therefore these functions will throw a =std::domain_error= if the
given slice could not be found.
#+begin_src c++ :tangle (atrip-slice-h)
static Slice& findOneByType(std::vector<Slice> &slices, Slice::Type type) {
const auto sliceIt
= std::find_if(slices.begin(), slices.end(),
[&type](Slice const& s) {
return type == s.info.type;
});
WITH_CRAZY_DEBUG
WITH_RANK
<< "\t__ looking for " << type << "\n";
if (sliceIt == slices.end())
throw std::domain_error("Slice by type not found!");
return *sliceIt;
}
#+end_src
#+begin_src c++ :tangle (atrip-slice-h)
static Slice& static Slice&
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) { findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
const auto sliceIt const auto sliceIt
@ -290,7 +438,9 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n"; WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
return *sliceIt; return *sliceIt;
} }
#+end_src
#+begin_src c++ :tangle (atrip-slice-h)
static Slice& findByTypeAbc static Slice& findByTypeAbc
( std::vector<Slice> &slices ( std::vector<Slice> &slices
, Slice::Type type , Slice::Type type
@ -318,7 +468,9 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
); );
return *sliceIt; return *sliceIt;
} }
#+end_src
#+begin_src c++ :tangle (atrip-slice-h)
static Slice& findByInfo(std::vector<Slice> &slices, static Slice& findByInfo(std::vector<Slice> &slices,
Slice::Info const& info) { Slice::Info const& info) {
const auto sliceIt const auto sliceIt
@ -339,29 +491,74 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
+ pretty_print(info)); + pretty_print(info));
return *sliceIt; return *sliceIt;
} }
#+end_src
// SLICE DEFINITION =================================================={{{1 *** Attributes
// ATTRIBUTES ============================================================ A slice object does not own data, it is just a container
or a pointer to data together with additional bookkeeping facilities.
It includes an info structure with the information about the slice,
=Type=, =State= etc, which will be later communicated to other ranks.
#+begin_src c++ :tangle (atrip-slice-h)
Info info; Info info;
F *data; #+end_src
MPI_Request request;
const size_t size;
A pointer to data is also necessary for the =Slice= but not necessary
to be communicated to other ranks. The =Slice= should never allocate
or deallocate itself the pointer.
#+begin_src c++ :tangle (atrip-slice-h)
F *data;
#+end_src
An =MPI_Request= handle is also included so that the slices that are
to receive data through MPI can know which request they belong to.
#+begin_src c++ :tangle (atrip-slice-h)
MPI_Request request;
#+end_src
For practical purposes in MPI calls, the number of elements in =data= is also included.
#+begin_src c++ :tangle (atrip-slice-h)
const size_t size;
#+end_src
*** Member functions
It is important to note that a ready slice should not be recycled from
any other slice, so that it can have access by itself to the data.
#+begin_src c++ :tangle (atrip-slice-h)
void markReady() noexcept { void markReady() noexcept {
info.state = Ready; info.state = Ready;
info.recycling = Blank; info.recycling = Blank;
} }
#+end_src
/*
,* This means that the data is there The following function asks wether or not
,*/ the slice has effectively been unwrapped or not,
i.e., wether or not the data are accessible and already
there. This can only happen in two ways, either
is the slice =Ready= or it is =SelfSufficient=,
i.e., the data pointed to was pre-distributed to the current node.
#+begin_src c++ :tangle (atrip-slice-h)
bool isUnwrapped() const noexcept { bool isUnwrapped() const noexcept {
return info.state == Ready return info.state == Ready
|| info.state == SelfSufficient || info.state == SelfSufficient
; ;
} }
#+end_src
The function =isUnwrappable= answers which slices can be unwrapped
potentially. Unwrapped slices can be unwrapped again idempotentially.
Also =Recycled= slices can be unwrapped, i.e. the slices pointed to by them
will be unwrapped.
The only other possibility is that the slice has been dispatched
in the past and can be unwrapped. The case where the state
is =Dispatched= is the canonical intuitive case where a real process
of unwrapping, i.e. waiting for the data to get through the network,
is done.
#+begin_src c++ :tangle (atrip-slice-h)
bool isUnwrappable() const noexcept { bool isUnwrappable() const noexcept {
return isUnwrapped() return isUnwrapped()
|| info.state == Recycled || info.state == Recycled
@ -393,19 +590,20 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
; ;
} }
#+end_src
/* The function =isRecylable= answers the question, which slices can be recycled.
,* This function answers the question, which slices can be recycled.
,* A slice can only be recycled if it is Fetch or Ready and has
,* A slice can only be recycled if it is Fetch or Ready and has a valid datapointer.
,* a valid datapointer.
,* In particular, SelfSufficient are not recyclable, since it is easier
,* In particular, SelfSufficient are not recyclable, since it is easier just to create a SelfSufficient slice than deal with data dependencies.
,* just to create a SelfSufficient slice than deal with data dependencies.
,* Furthermore, a recycled slice is not recyclable, if this is the case
,* Furthermore, a recycled slice is not recyclable, if this is the case then it is either bad design or a bug.
,* then it is either bad design or a bug.
,*/ #+begin_src c++ :tangle (atrip-slice-h)
inline bool isRecyclable() const noexcept { inline bool isRecyclable() const noexcept {
return ( info.state == Dispatched return ( info.state == Dispatched
|| info.state == Ready || info.state == Ready
@ -414,21 +612,38 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
&& hasValidDataPointer() && hasValidDataPointer()
; ;
} }
#+end_src
/*
,* This function describes if a slice has a valid data pointer. The function =hasValidDataPointer= describes if a slice has a valid
,* data pointer.
,* This is important to know if the slice has some data to it, also
,* some structural checks are done, so that it should not be Acceptor This is important to know if the slice has some data to it, also
,* or Blank, if this is the case then it is a bug. some structural checks are done, so that it should not be =Acceptor=
,*/ or =Blank=, if this is the case then it is a bug.
#+begin_src c++ :tangle (atrip-slice-h)
inline bool hasValidDataPointer() const noexcept { inline bool hasValidDataPointer() const noexcept {
return data != nullptr return data != nullptr
&& info.state != Acceptor && info.state != Acceptor
&& info.type != Blank && info.type != Blank
; ;
} }
#+end_src
The function
=unwrapAndMarkReady=
calls the low-level MPI functions
in order to wait whenever the state of the slice is correct.
The main behaviour of the function should
- return if state is =Ready=, since then there is nothing to be done.
- throw if the state is not =Dispatched=, only a dispatched slice
can be unwrapped through MPI.
- throw if an MPI error happens.
#+begin_src c++ :tangle (atrip-slice-h)
void unwrapAndMarkReady() { void unwrapAndMarkReady() {
if (info.state == Ready) return; if (info.state == Ready) return;
if (info.state != Dispatched) if (info.state != Dispatched)
@ -458,7 +673,10 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
<< "\n"; << "\n";
#endif #endif
} }
#+end_src
*** Epilog :noexport:
#+begin_src c++ :tangle (atrip-slice-h)
Slice(size_t size_) Slice(size_t size_)
: info({}) : info({})
, data(nullptr) , data(nullptr)
@ -468,7 +686,11 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
}; // struct Slice }; // struct Slice
#+end_src
*** Debug :noexport:
#+begin_src c++ :tangle (atrip-slice-h)
std::ostream& operator<<(std::ostream& out, Slice::Location const& v) { std::ostream& operator<<(std::ostream& out, Slice::Location const& v) {
// TODO: remove me // TODO: remove me
out << "{.r(" << v.rank << "), .s(" << v.source << ")};"; out << "{.r(" << v.rank << "), .s(" << v.source << ")};";
@ -528,6 +750,11 @@ namespace atrip {
#+end_src #+end_src
** The rank mapping ** The rank mapping
This section introduces the concept of rank mapping,
which defines how slices will be allocated to every
rank.
#+begin_src c++ :tangle (atrip-rankmap-h) #+begin_src c++ :tangle (atrip-rankmap-h)
#pragma once #pragma once

View File

@ -1,4 +1,4 @@
// [[file:../../atrip.org::*The slice][The slice:1]] // [[file:../../atrip.org::*Prolog][Prolog:1]]
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
@ -14,13 +14,13 @@ namespace atrip {
struct Slice { struct Slice {
using F = double; using F = double;
// The slice:1 ends here // Prolog:1 ends here
// [[file:../../atrip.org::*The slice][The slice:2]]
// ASSOCIATED TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// [[file:../../atrip.org::*Location][Location:1]]
struct Location { size_t rank; size_t source; }; struct Location { size_t rank; size_t source; };
// Location:1 ends here
// [[file:../../atrip.org::*Type][Type:1]]
enum Type enum Type
{ A = 10 { A = 10
, B , B
@ -36,29 +36,20 @@ struct Slice {
// The non-typed slice // The non-typed slice
, Blank = 404 , Blank = 404
}; };
// Type:1 ends here
// [[file:../../atrip.org::*State][State:1]]
enum State { enum State {
// Fetch represents the state where a slice is to be fetched
// and has a valid data pointer that can be written to
Fetch = 0, Fetch = 0,
// Dispatches represents the state that an MPI call has been
// dispatched in order to get the data, but the data has not been
// yet unwrapped, the data might be there or we might have to wait.
Dispatched = 2, Dispatched = 2,
// Ready means that the data pointer can be read from
Ready = 1, Ready = 1,
// Self sufficient is a slice when its contents are located
// in the same rank that it lives, so that it does not have to
// fetch from no one else.
SelfSufficient = 911, SelfSufficient = 911,
// Recycled means that this slice gets its data pointer from another
// slice, so it should not be written to
Recycled = 123, Recycled = 123,
// Acceptor means that the Slice can accept a new Slice, it is
// the counterpart of the Blank type, but for states
Acceptor = 405 Acceptor = 405
}; };
// State:1 ends here
// [[file:../../atrip.org::*The Info structure][The Info structure:1]]
struct Info { struct Info {
// which part of a,b,c the slice holds // which part of a,b,c the slice holds
PartialTuple tuple; PartialTuple tuple;
@ -67,7 +58,6 @@ struct Slice {
// What is the state of the slice // What is the state of the slice
State state; State state;
// Where the slice is to be retrieved // Where the slice is to be retrieved
// NOTE: this can actually be computed from tuple
Location from; Location from;
// If the data are actually to be found in this other slice // If the data are actually to be found in this other slice
Type recycling; Type recycling;
@ -81,8 +71,9 @@ struct Slice {
}; };
using Ty_x_Tu = std::pair< Type, PartialTuple >; using Ty_x_Tu = std::pair< Type, PartialTuple >;
// The Info structure:1 ends here
// Names of the integrals that are considered in CCSD(T) // [[file:../../atrip.org::*Name][Name:1]]
enum Name enum Name
{ TA = 100 { TA = 100
, VIJKA = 101 , VIJKA = 101
@ -90,20 +81,21 @@ struct Slice {
, TABIJ = 201 , TABIJ = 201
, VABIJ = 202 , VABIJ = 202
}; };
// Name:1 ends here
// DATABASE ==========================================================={{{1 // [[file:../../atrip.org::*Database][Database:1]]
struct LocalDatabaseElement { struct LocalDatabaseElement {
Slice::Name name; Slice::Name name;
Slice::Info info; Slice::Info info;
}; };
// Database:1 ends here
// [[file:../../atrip.org::*Database][Database:2]]
using LocalDatabase = std::vector<LocalDatabaseElement>; using LocalDatabase = std::vector<LocalDatabaseElement>;
using Database = LocalDatabase; using Database = LocalDatabase;
// Database:2 ends here
// [[file:../../atrip.org::*MPI Types][MPI Types:1]]
// STATIC METHODS ===========================================================
//
// They are useful to organize the structure of slices
struct mpi { struct mpi {
static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) { static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
@ -191,7 +183,9 @@ struct Slice {
} }
}; };
// MPI Types:1 ends here
// [[file:../../atrip.org::*Static utilities][Static utilities:1]]
static static
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) { PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
switch (sliceType) { switch (sliceType) {
@ -207,30 +201,9 @@ struct Slice {
default: throw "Switch statement not exhaustive!"; default: throw "Switch statement not exhaustive!";
} }
} }
// Static utilities:1 ends here
// [[file:../../atrip.org::*Static utilities][Static utilities:2]]
/**
* It is important here to return a reference to a Slice
* not to accidentally copy the associated buffer of the slice.
*/
static Slice& findOneByType(std::vector<Slice> &slices, Slice::Type type) {
const auto sliceIt
= std::find_if(slices.begin(), slices.end(),
[&type](Slice const& s) {
return type == s.info.type;
});
WITH_CRAZY_DEBUG
WITH_RANK
<< "\t__ looking for " << type << "\n";
if (sliceIt == slices.end())
throw std::domain_error("Slice by type not found!");
return *sliceIt;
}
/*
* Check if an info has
*
*/
static std::vector<Slice*> hasRecycledReferencingToIt static std::vector<Slice*> hasRecycledReferencingToIt
( std::vector<Slice> &slices ( std::vector<Slice> &slices
, Info const& info , Info const& info
@ -245,7 +218,25 @@ struct Slice {
return result; return result;
} }
// Static utilities:2 ends here
// [[file:../../atrip.org::*Static utilities][Static utilities:3]]
static Slice& findOneByType(std::vector<Slice> &slices, Slice::Type type) {
const auto sliceIt
= std::find_if(slices.begin(), slices.end(),
[&type](Slice const& s) {
return type == s.info.type;
});
WITH_CRAZY_DEBUG
WITH_RANK
<< "\t__ looking for " << type << "\n";
if (sliceIt == slices.end())
throw std::domain_error("Slice by type not found!");
return *sliceIt;
}
// Static utilities:3 ends here
// [[file:../../atrip.org::*Static utilities][Static utilities:4]]
static Slice& static Slice&
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) { findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
const auto sliceIt const auto sliceIt
@ -269,7 +260,9 @@ struct Slice {
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n"; WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
return *sliceIt; return *sliceIt;
} }
// Static utilities:4 ends here
// [[file:../../atrip.org::*Static utilities][Static utilities:5]]
static Slice& findByTypeAbc static Slice& findByTypeAbc
( std::vector<Slice> &slices ( std::vector<Slice> &slices
, Slice::Type type , Slice::Type type
@ -297,7 +290,9 @@ struct Slice {
); );
return *sliceIt; return *sliceIt;
} }
// Static utilities:5 ends here
// [[file:../../atrip.org::*Static utilities][Static utilities:6]]
static Slice& findByInfo(std::vector<Slice> &slices, static Slice& findByInfo(std::vector<Slice> &slices,
Slice::Info const& info) { Slice::Info const& info) {
const auto sliceIt const auto sliceIt
@ -318,29 +313,40 @@ struct Slice {
+ pretty_print(info)); + pretty_print(info));
return *sliceIt; return *sliceIt;
} }
// Static utilities:6 ends here
// SLICE DEFINITION =================================================={{{1 // [[file:../../atrip.org::*Attributes][Attributes:1]]
// ATTRIBUTES ============================================================
Info info; Info info;
F *data; // Attributes:1 ends here
MPI_Request request;
const size_t size;
// [[file:../../atrip.org::*Attributes][Attributes:2]]
F *data;
// Attributes:2 ends here
// [[file:../../atrip.org::*Attributes][Attributes:3]]
MPI_Request request;
// Attributes:3 ends here
// [[file:../../atrip.org::*Attributes][Attributes:4]]
const size_t size;
// Attributes:4 ends here
// [[file:../../atrip.org::*Member functions][Member functions:1]]
void markReady() noexcept { void markReady() noexcept {
info.state = Ready; info.state = Ready;
info.recycling = Blank; info.recycling = Blank;
} }
// Member functions:1 ends here
/* // [[file:../../atrip.org::*Member functions][Member functions:2]]
* This means that the data is there
*/
bool isUnwrapped() const noexcept { bool isUnwrapped() const noexcept {
return info.state == Ready return info.state == Ready
|| info.state == SelfSufficient || info.state == SelfSufficient
; ;
} }
// Member functions:2 ends here
// [[file:../../atrip.org::*Member functions][Member functions:3]]
bool isUnwrappable() const noexcept { bool isUnwrappable() const noexcept {
return isUnwrapped() return isUnwrapped()
|| info.state == Recycled || info.state == Recycled
@ -371,20 +377,9 @@ struct Slice {
&& data == nullptr && data == nullptr
; ;
} }
// Member functions:3 ends here
// [[file:../../atrip.org::*Member functions][Member functions:4]]
/*
* This function answers the question, which slices can be recycled.
*
* A slice can only be recycled if it is Fetch or Ready and has
* a valid datapointer.
*
* In particular, SelfSufficient are not recyclable, since it is easier
* just to create a SelfSufficient slice than deal with data dependencies.
*
* Furthermore, a recycled slice is not recyclable, if this is the case
* then it is either bad design or a bug.
*/
inline bool isRecyclable() const noexcept { inline bool isRecyclable() const noexcept {
return ( info.state == Dispatched return ( info.state == Dispatched
|| info.state == Ready || info.state == Ready
@ -393,21 +388,18 @@ struct Slice {
&& hasValidDataPointer() && hasValidDataPointer()
; ;
} }
// Member functions:4 ends here
/* // [[file:../../atrip.org::*Member functions][Member functions:5]]
* This function describes if a slice has a valid data pointer.
*
* This is important to know if the slice has some data to it, also
* some structural checks are done, so that it should not be Acceptor
* or Blank, if this is the case then it is a bug.
*/
inline bool hasValidDataPointer() const noexcept { inline bool hasValidDataPointer() const noexcept {
return data != nullptr return data != nullptr
&& info.state != Acceptor && info.state != Acceptor
&& info.type != Blank && info.type != Blank
; ;
} }
// Member functions:5 ends here
// [[file:../../atrip.org::*Member functions][Member functions:6]]
void unwrapAndMarkReady() { void unwrapAndMarkReady() {
if (info.state == Ready) return; if (info.state == Ready) return;
if (info.state != Dispatched) if (info.state != Dispatched)
@ -437,7 +429,9 @@ struct Slice {
<< "\n"; << "\n";
#endif #endif
} }
// Member functions:6 ends here
// [[file:../../atrip.org::*Epilog][Epilog:1]]
Slice(size_t size_) Slice(size_t size_)
: info({}) : info({})
, data(nullptr) , data(nullptr)
@ -446,8 +440,9 @@ struct Slice {
}; // struct Slice }; // struct Slice
// Epilog:1 ends here
// [[file:../../atrip.org::*Debug][Debug:1]]
std::ostream& operator<<(std::ostream& out, Slice::Location const& v) { std::ostream& operator<<(std::ostream& out, Slice::Location const& v) {
// TODO: remove me // TODO: remove me
out << "{.r(" << v.rank << "), .s(" << v.source << ")};"; out << "{.r(" << v.rank << "), .s(" << v.source << ")};";
@ -464,4 +459,4 @@ std::ostream& operator<<(std::ostream& out, Slice::Info const& i) {
} }
} // namespace atrip } // namespace atrip
// The slice:2 ends here // Debug:1 ends here