Comment most of Slice section
This commit is contained in:
parent
06c9f31bcd
commit
51c3203fda
409
atrip.org
409
atrip.org
@ -8,7 +8,9 @@ The algorithm uses two main data types, the =Slice= and the
|
||||
|
||||
** The slice
|
||||
|
||||
The following section introduces the idea of a slice.
|
||||
|
||||
*** Prolog :noexport:
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
@ -26,6 +28,7 @@ struct Slice {
|
||||
|
||||
using F = double;
|
||||
#+end_src
|
||||
*** Introduction
|
||||
|
||||
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:
|
||||
@ -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 \)
|
||||
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)
|
||||
// ASSOCIATED TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
struct Location { size_t rank; size_t source; };
|
||||
|
||||
enum Type
|
||||
{ A = 10
|
||||
, B
|
||||
@ -57,30 +110,65 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
// The non-typed slice
|
||||
, 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 {
|
||||
// Fetch represents the state where a slice is to be fetched
|
||||
// and has a valid data pointer that can be written to
|
||||
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,
|
||||
// Ready means that the data pointer can be read from
|
||||
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,
|
||||
// Recycled means that this slice gets its data pointer from another
|
||||
// slice, so it should not be written to
|
||||
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
|
||||
};
|
||||
#+end_src
|
||||
|
||||
struct Info {
|
||||
*** 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 {
|
||||
// which part of a,b,c the slice holds
|
||||
PartialTuple tuple;
|
||||
// The type of slice for the user to retrieve the correct one
|
||||
@ -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
|
||||
State state;
|
||||
// Where the slice is to be retrieved
|
||||
// NOTE: this can actually be computed from tuple
|
||||
Location from;
|
||||
// If the data are actually to be found in this other slice
|
||||
Type recycling;
|
||||
@ -99,11 +186,26 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
, from{0,0}
|
||||
, recycling{Blank}
|
||||
{}
|
||||
};
|
||||
};
|
||||
|
||||
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
|
||||
{ TA = 100
|
||||
, VIJKA = 101
|
||||
@ -111,21 +213,32 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
, TABIJ = 201
|
||||
, 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 {
|
||||
Slice::Name name;
|
||||
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 Database = LocalDatabase;
|
||||
#+end_src
|
||||
|
||||
|
||||
// STATIC METHODS ===========================================================
|
||||
//
|
||||
// They are useful to organize the structure of slices
|
||||
|
||||
struct mpi {
|
||||
*** MPI Types
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
struct mpi {
|
||||
|
||||
static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
|
||||
MPI_Datatype dt;
|
||||
@ -211,10 +324,30 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
return dt;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
#+end_src
|
||||
|
||||
static
|
||||
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
|
||||
*** 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
|
||||
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
|
||||
switch (sliceType) {
|
||||
case AB: return {abc[0], abc[1]};
|
||||
case BC: return {abc[1], abc[2]};
|
||||
@ -227,32 +360,19 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
case C: return {abc[2], 0};
|
||||
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.
|
||||
|
||||
/**
|
||||
,* 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
|
||||
This function should therefore return a vector of pointers
|
||||
of slices referencing to the given slice's info, when
|
||||
the length of the vector is zero, then there are no dangling
|
||||
links.
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
static std::vector<Slice*> hasRecycledReferencingToIt
|
||||
( std::vector<Slice> &slices
|
||||
, Info const& info
|
||||
) {
|
||||
@ -265,10 +385,38 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
) result.push_back(&s);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#+end_src
|
||||
|
||||
static Slice&
|
||||
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
|
||||
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&
|
||||
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
|
||||
const auto sliceIt
|
||||
= std::find_if(slices.begin(), slices.end(),
|
||||
[&info](Slice const& s) {
|
||||
@ -289,9 +437,11 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
);
|
||||
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
|
||||
return *sliceIt;
|
||||
}
|
||||
}
|
||||
#+end_src
|
||||
|
||||
static Slice& findByTypeAbc
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
static Slice& findByTypeAbc
|
||||
( std::vector<Slice> &slices
|
||||
, Slice::Type type
|
||||
, ABCTuple const& abc
|
||||
@ -317,9 +467,11 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
+ pretty_print(Atrip::rank)
|
||||
);
|
||||
return *sliceIt;
|
||||
}
|
||||
}
|
||||
#+end_src
|
||||
|
||||
static Slice& findByInfo(std::vector<Slice> &slices,
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
static Slice& findByInfo(std::vector<Slice> &slices,
|
||||
Slice::Info const& info) {
|
||||
const auto sliceIt
|
||||
= std::find_if(slices.begin(), slices.end(),
|
||||
@ -338,30 +490,75 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
throw std::domain_error( "Slice by info not found: "
|
||||
+ pretty_print(info));
|
||||
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;
|
||||
F *data;
|
||||
MPI_Request request;
|
||||
const size_t size;
|
||||
#+end_src
|
||||
|
||||
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 {
|
||||
info.state = Ready;
|
||||
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 {
|
||||
return info.state == Ready
|
||||
|| 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 {
|
||||
return isUnwrapped()
|
||||
|| info.state == Recycled
|
||||
@ -393,19 +590,20 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
;
|
||||
}
|
||||
|
||||
#+end_src
|
||||
|
||||
/*
|
||||
,* 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.
|
||||
,*/
|
||||
The function =isRecylable= 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.
|
||||
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
inline bool isRecyclable() const noexcept {
|
||||
return ( info.state == Dispatched
|
||||
|| info.state == Ready
|
||||
@ -414,21 +612,38 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
&& hasValidDataPointer()
|
||||
;
|
||||
}
|
||||
#+end_src
|
||||
|
||||
/*
|
||||
,* 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.
|
||||
,*/
|
||||
|
||||
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=
|
||||
or =Blank=, if this is the case then it is a bug.
|
||||
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
inline bool hasValidDataPointer() const noexcept {
|
||||
return data != nullptr
|
||||
&& info.state != Acceptor
|
||||
&& 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() {
|
||||
if (info.state == Ready) return;
|
||||
if (info.state != Dispatched)
|
||||
@ -458,7 +673,10 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
<< "\n";
|
||||
#endif
|
||||
}
|
||||
#+end_src
|
||||
|
||||
*** Epilog :noexport:
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
Slice(size_t size_)
|
||||
: info({})
|
||||
, data(nullptr)
|
||||
@ -468,7 +686,11 @@ As an example, for the doubles amplitudes \( T^{ab}_{ij} \), one need two kinds
|
||||
|
||||
}; // struct Slice
|
||||
|
||||
#+end_src
|
||||
|
||||
*** Debug :noexport:
|
||||
|
||||
#+begin_src c++ :tangle (atrip-slice-h)
|
||||
std::ostream& operator<<(std::ostream& out, Slice::Location const& v) {
|
||||
// TODO: remove me
|
||||
out << "{.r(" << v.rank << "), .s(" << v.source << ")};";
|
||||
@ -528,6 +750,11 @@ namespace atrip {
|
||||
#+end_src
|
||||
|
||||
** 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)
|
||||
#pragma once
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// [[file:../../atrip.org::*The slice][The slice:1]]
|
||||
// [[file:../../atrip.org::*Prolog][Prolog:1]]
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
@ -14,14 +14,14 @@ namespace atrip {
|
||||
struct Slice {
|
||||
|
||||
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; };
|
||||
// Location:1 ends here
|
||||
|
||||
struct Location { size_t rank; size_t source; };
|
||||
|
||||
enum Type
|
||||
// [[file:../../atrip.org::*Type][Type:1]]
|
||||
enum Type
|
||||
{ A = 10
|
||||
, B
|
||||
, C
|
||||
@ -36,30 +36,21 @@ struct Slice {
|
||||
// The non-typed slice
|
||||
, Blank = 404
|
||||
};
|
||||
// Type:1 ends here
|
||||
|
||||
enum State {
|
||||
// Fetch represents the state where a slice is to be fetched
|
||||
// and has a valid data pointer that can be written to
|
||||
// [[file:../../atrip.org::*State][State:1]]
|
||||
enum State {
|
||||
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,
|
||||
// Ready means that the data pointer can be read from
|
||||
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,
|
||||
// Recycled means that this slice gets its data pointer from another
|
||||
// slice, so it should not be written to
|
||||
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
|
||||
};
|
||||
};
|
||||
// State:1 ends here
|
||||
|
||||
struct Info {
|
||||
// [[file:../../atrip.org::*The Info structure][The Info structure:1]]
|
||||
struct Info {
|
||||
// which part of a,b,c the slice holds
|
||||
PartialTuple tuple;
|
||||
// The type of slice for the user to retrieve the correct one
|
||||
@ -67,7 +58,6 @@ struct Slice {
|
||||
// What is the state of the slice
|
||||
State state;
|
||||
// Where the slice is to be retrieved
|
||||
// NOTE: this can actually be computed from tuple
|
||||
Location from;
|
||||
// If the data are actually to be found in this other slice
|
||||
Type recycling;
|
||||
@ -78,33 +68,35 @@ struct Slice {
|
||||
, from{0,0}
|
||||
, recycling{Blank}
|
||||
{}
|
||||
};
|
||||
};
|
||||
|
||||
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)
|
||||
enum Name
|
||||
// [[file:../../atrip.org::*Name][Name:1]]
|
||||
enum Name
|
||||
{ TA = 100
|
||||
, VIJKA = 101
|
||||
, VABCI = 200
|
||||
, TABIJ = 201
|
||||
, VABIJ = 202
|
||||
};
|
||||
// Name:1 ends here
|
||||
|
||||
// DATABASE ==========================================================={{{1
|
||||
struct LocalDatabaseElement {
|
||||
// [[file:../../atrip.org::*Database][Database:1]]
|
||||
struct LocalDatabaseElement {
|
||||
Slice::Name name;
|
||||
Slice::Info info;
|
||||
};
|
||||
using LocalDatabase = std::vector<LocalDatabaseElement>;
|
||||
using Database = LocalDatabase;
|
||||
};
|
||||
// Database:1 ends here
|
||||
|
||||
// [[file:../../atrip.org::*Database][Database:2]]
|
||||
using LocalDatabase = std::vector<LocalDatabaseElement>;
|
||||
using Database = LocalDatabase;
|
||||
// Database:2 ends here
|
||||
|
||||
// STATIC METHODS ===========================================================
|
||||
//
|
||||
// They are useful to organize the structure of slices
|
||||
|
||||
struct mpi {
|
||||
// [[file:../../atrip.org::*MPI Types][MPI Types:1]]
|
||||
struct mpi {
|
||||
|
||||
static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
|
||||
MPI_Datatype dt;
|
||||
@ -190,10 +182,12 @@ struct Slice {
|
||||
return dt;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
// MPI Types:1 ends here
|
||||
|
||||
static
|
||||
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
|
||||
// [[file:../../atrip.org::*Static utilities][Static utilities:1]]
|
||||
static
|
||||
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
|
||||
switch (sliceType) {
|
||||
case AB: return {abc[0], abc[1]};
|
||||
case BC: return {abc[1], abc[2]};
|
||||
@ -206,32 +200,11 @@ struct Slice {
|
||||
case C: return {abc[2], 0};
|
||||
default: throw "Switch statement not exhaustive!";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Static utilities:1 ends here
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
// [[file:../../atrip.org::*Static utilities][Static utilities:2]]
|
||||
static std::vector<Slice*> hasRecycledReferencingToIt
|
||||
( std::vector<Slice> &slices
|
||||
, Info const& info
|
||||
) {
|
||||
@ -244,10 +217,28 @@ struct Slice {
|
||||
) result.push_back(&s);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Static utilities:2 ends here
|
||||
|
||||
static Slice&
|
||||
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
|
||||
// [[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&
|
||||
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
|
||||
const auto sliceIt
|
||||
= std::find_if(slices.begin(), slices.end(),
|
||||
[&info](Slice const& s) {
|
||||
@ -268,9 +259,11 @@ struct Slice {
|
||||
);
|
||||
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
|
||||
return *sliceIt;
|
||||
}
|
||||
}
|
||||
// Static utilities:4 ends here
|
||||
|
||||
static Slice& findByTypeAbc
|
||||
// [[file:../../atrip.org::*Static utilities][Static utilities:5]]
|
||||
static Slice& findByTypeAbc
|
||||
( std::vector<Slice> &slices
|
||||
, Slice::Type type
|
||||
, ABCTuple const& abc
|
||||
@ -296,9 +289,11 @@ struct Slice {
|
||||
+ pretty_print(Atrip::rank)
|
||||
);
|
||||
return *sliceIt;
|
||||
}
|
||||
}
|
||||
// Static utilities:5 ends here
|
||||
|
||||
static Slice& findByInfo(std::vector<Slice> &slices,
|
||||
// [[file:../../atrip.org::*Static utilities][Static utilities:6]]
|
||||
static Slice& findByInfo(std::vector<Slice> &slices,
|
||||
Slice::Info const& info) {
|
||||
const auto sliceIt
|
||||
= std::find_if(slices.begin(), slices.end(),
|
||||
@ -317,51 +312,62 @@ struct Slice {
|
||||
throw std::domain_error( "Slice by info not found: "
|
||||
+ pretty_print(info));
|
||||
return *sliceIt;
|
||||
}
|
||||
}
|
||||
// Static utilities:6 ends here
|
||||
|
||||
// SLICE DEFINITION =================================================={{{1
|
||||
// [[file:../../atrip.org::*Attributes][Attributes:1]]
|
||||
Info info;
|
||||
// Attributes:1 ends here
|
||||
|
||||
// ATTRIBUTES ============================================================
|
||||
Info info;
|
||||
F *data;
|
||||
MPI_Request request;
|
||||
const size_t size;
|
||||
// [[file:../../atrip.org::*Attributes][Attributes:2]]
|
||||
F *data;
|
||||
// Attributes:2 ends here
|
||||
|
||||
void markReady() noexcept {
|
||||
// [[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 {
|
||||
info.state = Ready;
|
||||
info.recycling = Blank;
|
||||
}
|
||||
}
|
||||
// Member functions:1 ends here
|
||||
|
||||
/*
|
||||
* This means that the data is there
|
||||
*/
|
||||
bool isUnwrapped() const noexcept {
|
||||
// [[file:../../atrip.org::*Member functions][Member functions:2]]
|
||||
bool isUnwrapped() const noexcept {
|
||||
return info.state == Ready
|
||||
|| info.state == SelfSufficient
|
||||
;
|
||||
}
|
||||
}
|
||||
// Member functions:2 ends here
|
||||
|
||||
bool isUnwrappable() const noexcept {
|
||||
// [[file:../../atrip.org::*Member functions][Member functions:3]]
|
||||
bool isUnwrappable() const noexcept {
|
||||
return isUnwrapped()
|
||||
|| info.state == Recycled
|
||||
|| info.state == Dispatched
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isDirectlyFetchable() const noexcept {
|
||||
inline bool isDirectlyFetchable() const noexcept {
|
||||
return info.state == Ready || info.state == Dispatched;
|
||||
}
|
||||
}
|
||||
|
||||
void free() noexcept {
|
||||
void free() noexcept {
|
||||
info.tuple = {0, 0};
|
||||
info.type = Blank;
|
||||
info.state = Acceptor;
|
||||
info.from = {0, 0};
|
||||
info.recycling = Blank;
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isFree() const noexcept {
|
||||
inline bool isFree() const noexcept {
|
||||
return info.tuple == PartialTuple{0, 0}
|
||||
&& info.type == Blank
|
||||
&& info.state == Acceptor
|
||||
@ -370,45 +376,31 @@ struct Slice {
|
||||
&& info.recycling == Blank
|
||||
&& data == nullptr
|
||||
;
|
||||
}
|
||||
}
|
||||
// Member functions:3 ends here
|
||||
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
// [[file:../../atrip.org::*Member functions][Member functions:4]]
|
||||
inline bool isRecyclable() const noexcept {
|
||||
return ( info.state == Dispatched
|
||||
|| info.state == Ready
|
||||
|| info.state == Fetch
|
||||
)
|
||||
&& hasValidDataPointer()
|
||||
;
|
||||
}
|
||||
}
|
||||
// Member functions:4 ends here
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
// [[file:../../atrip.org::*Member functions][Member functions:5]]
|
||||
inline bool hasValidDataPointer() const noexcept {
|
||||
return data != nullptr
|
||||
&& info.state != Acceptor
|
||||
&& info.type != Blank
|
||||
;
|
||||
}
|
||||
}
|
||||
// Member functions:5 ends here
|
||||
|
||||
void unwrapAndMarkReady() {
|
||||
// [[file:../../atrip.org::*Member functions][Member functions:6]]
|
||||
void unwrapAndMarkReady() {
|
||||
if (info.state == Ready) return;
|
||||
if (info.state != Dispatched)
|
||||
throw
|
||||
@ -437,17 +429,20 @@ struct Slice {
|
||||
<< "\n";
|
||||
#endif
|
||||
}
|
||||
// Member functions:6 ends here
|
||||
|
||||
Slice(size_t size_)
|
||||
// [[file:../../atrip.org::*Epilog][Epilog:1]]
|
||||
Slice(size_t size_)
|
||||
: info({})
|
||||
, data(nullptr)
|
||||
, size(size_)
|
||||
{}
|
||||
|
||||
|
||||
}; // struct Slice
|
||||
|
||||
}; // struct Slice
|
||||
// Epilog:1 ends here
|
||||
|
||||
// [[file:../../atrip.org::*Debug][Debug:1]]
|
||||
std::ostream& operator<<(std::ostream& out, Slice::Location const& v) {
|
||||
// TODO: remove me
|
||||
out << "{.r(" << v.rank << "), .s(" << v.source << ")};";
|
||||
@ -464,4 +459,4 @@ std::ostream& operator<<(std::ostream& out, Slice::Info const& i) {
|
||||
}
|
||||
|
||||
} // namespace atrip
|
||||
// The slice:2 ends here
|
||||
// Debug:1 ends here
|
||||
|
||||
Loading…
Reference in New Issue
Block a user