Comment most of Slice section
This commit is contained in:
parent
06c9f31bcd
commit
51c3203fda
375
atrip.org
375
atrip.org
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user