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

775
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,53 +110,102 @@ 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
struct Info { *** The Info structure
// which part of a,b,c the slice holds
PartialTuple tuple;
// The type of slice for the user to retrieve the correct one
Type type;
// 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;
Info() : tuple{0,0} Every slice has an information structure associated with it
, type{Blank} that keeps track of the **variable** type, state and so on.
, state{Acceptor}
, from{0,0}
, recycling{Blank}
{}
};
using Ty_x_Tu = std::pair< Type, PartialTuple >; #+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
Type type;
// What is the state of the slice
State state;
// Where the slice is to be retrieved
Location from;
// If the data are actually to be found in this other slice
Type recycling;
// Names of the integrals that are considered in CCSD(T) Info() : tuple{0,0}
, type{Blank}
, state{Acceptor}
, from{0,0}
, recycling{Blank}
{}
};
using Ty_x_Tu = std::pair< Type, PartialTuple >;
#+end_src
*** 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,257 +213,352 @@ 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
#+begin_src c++ :tangle (atrip-slice-h)
struct mpi {
// STATIC METHODS =========================================================== static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
// MPI_Datatype dt;
// They are useful to organize the structure of slices MPI_Type_vector(n, 1, 1, DT, &dt);
MPI_Type_commit(&dt);
struct mpi { return dt;
static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
MPI_Datatype dt;
MPI_Type_vector(n, 1, 1, DT, &dt);
MPI_Type_commit(&dt);
return dt;
}
static MPI_Datatype sliceLocation () {
constexpr int n = 2;
// create a sliceLocation to measure in the current architecture
// the packing of the struct
Slice::Location measure;
MPI_Datatype dt;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n] = {usizeDt(), usizeDt()};
// measure the displacements in the struct
size_t j = 0;
MPI_Aint displacements[n];
MPI_Get_address(&measure.rank, &displacements[j++]);
MPI_Get_address(&measure.source, &displacements[j++]);
for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
displacements[0] = 0;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
static MPI_Datatype enumDt() { return MPI_INT; }
static MPI_Datatype usizeDt() { return MPI_UINT64_T; }
static MPI_Datatype sliceInfo () {
constexpr int n = 5;
MPI_Datatype dt;
Slice::Info measure;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n]
= { vector(2, usizeDt())
, enumDt()
, enumDt()
, sliceLocation()
, enumDt()
};
// create the displacements from the info measurement struct
size_t j = 0;
MPI_Aint displacements[n];
MPI_Get_address(measure.tuple.data(), &displacements[j++]);
MPI_Get_address(&measure.type, &displacements[j++]);
MPI_Get_address(&measure.state, &displacements[j++]);
MPI_Get_address(&measure.from, &displacements[j++]);
MPI_Get_address(&measure.recycling, &displacements[j++]);
for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
displacements[0] = 0;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
static MPI_Datatype localDatabaseElement () {
constexpr int n = 2;
MPI_Datatype dt;
LocalDatabaseElement measure;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n]
= { enumDt()
, sliceInfo()
};
// measure the displacements in the struct
size_t j = 0;
MPI_Aint displacements[n];
MPI_Get_address(&measure.name, &displacements[j++]);
MPI_Get_address(&measure.info, &displacements[j++]);
for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
displacements[0] = 0;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
};
static
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
switch (sliceType) {
case AB: return {abc[0], abc[1]};
case BC: return {abc[1], abc[2]};
case AC: return {abc[0], abc[2]};
case CB: return {abc[2], abc[1]};
case BA: return {abc[1], abc[0]};
case CA: return {abc[2], abc[0]};
case A: return {abc[0], 0};
case B: return {abc[1], 0};
case C: return {abc[2], 0};
default: throw "Switch statement not exhaustive!";
}
} }
static MPI_Datatype sliceLocation () {
constexpr int n = 2;
// create a sliceLocation to measure in the current architecture
// the packing of the struct
Slice::Location measure;
MPI_Datatype dt;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n] = {usizeDt(), usizeDt()};
/** // measure the displacements in the struct
,* It is important here to return a reference to a Slice size_t j = 0;
,* not to accidentally copy the associated buffer of the slice. MPI_Aint displacements[n];
,*/ MPI_Get_address(&measure.rank, &displacements[j++]);
static Slice& findOneByType(std::vector<Slice> &slices, Slice::Type type) { MPI_Get_address(&measure.source, &displacements[j++]);
const auto sliceIt for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
= std::find_if(slices.begin(), slices.end(), displacements[0] = 0;
[&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;
}
/* MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
,* Check if an info has MPI_Type_commit(&dt);
,* return dt;
,*/ }
static std::vector<Slice*> hasRecycledReferencingToIt
( std::vector<Slice> &slices
, Info const& info
) {
std::vector<Slice*> result;
for (auto& s: slices) static MPI_Datatype enumDt() { return MPI_INT; }
if ( s.info.recycling == info.type static MPI_Datatype usizeDt() { return MPI_UINT64_T; }
&& s.info.tuple == info.tuple
&& s.info.state == Recycled
) result.push_back(&s);
return result; static MPI_Datatype sliceInfo () {
} constexpr int n = 5;
MPI_Datatype dt;
Slice::Info measure;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n]
= { vector(2, usizeDt())
, enumDt()
, enumDt()
, sliceLocation()
, enumDt()
};
static Slice& // create the displacements from the info measurement struct
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) { size_t j = 0;
const auto sliceIt MPI_Aint displacements[n];
= std::find_if(slices.begin(), slices.end(), MPI_Get_address(measure.tuple.data(), &displacements[j++]);
[&info](Slice const& s) { MPI_Get_address(&measure.type, &displacements[j++]);
return info.recycling == s.info.type MPI_Get_address(&measure.state, &displacements[j++]);
&& info.tuple == s.info.tuple MPI_Get_address(&measure.from, &displacements[j++]);
&& State::Recycled != s.info.state MPI_Get_address(&measure.recycling, &displacements[j++]);
; for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
}); displacements[0] = 0;
WITH_CRAZY_DEBUG MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
WITH_RANK << "__slice__:find: recycling source of " MPI_Type_commit(&dt);
<< pretty_print(info) << "\n"; return dt;
if (sliceIt == slices.end()) }
throw std::domain_error( "Slice not found: "
+ pretty_print(info)
+ " rank: "
+ pretty_print(Atrip::rank)
);
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
return *sliceIt;
}
static Slice& findByTypeAbc static MPI_Datatype localDatabaseElement () {
( std::vector<Slice> &slices constexpr int n = 2;
, Slice::Type type MPI_Datatype dt;
, ABCTuple const& abc LocalDatabaseElement measure;
) { const std::vector<int> lengths(n, 1);
const auto tuple = Slice::subtupleBySlice(abc, type); const MPI_Datatype types[n]
const auto sliceIt = { enumDt()
= std::find_if(slices.begin(), slices.end(), , sliceInfo()
[&type, &tuple](Slice const& s) { };
return type == s.info.type
&& tuple == s.info.tuple
;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find:" << type << " and tuple "
<< pretty_print(tuple)
<< "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice not found: "
+ pretty_print(tuple)
+ ", "
+ pretty_print(type)
+ " rank: "
+ pretty_print(Atrip::rank)
);
return *sliceIt;
}
static Slice& findByInfo(std::vector<Slice> &slices, // measure the displacements in the struct
Slice::Info const& info) { size_t j = 0;
const auto sliceIt MPI_Aint displacements[n];
= std::find_if(slices.begin(), slices.end(), MPI_Get_address(&measure.name, &displacements[j++]);
[&info](Slice const& s) { MPI_Get_address(&measure.info, &displacements[j++]);
// TODO: maybe implement comparison in Info struct for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
return info.type == s.info.type displacements[0] = 0;
&& info.state == s.info.state
&& info.tuple == s.info.tuple
&& info.from.rank == s.info.from.rank
&& info.from.source == s.info.from.source
;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find:looking for " << pretty_print(info) << "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice by info not found: "
+ pretty_print(info));
return *sliceIt;
}
// SLICE DEFINITION =================================================={{{1 MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
// ATTRIBUTES ============================================================ };
#+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
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
switch (sliceType) {
case AB: return {abc[0], abc[1]};
case BC: return {abc[1], abc[2]};
case AC: return {abc[0], abc[2]};
case CB: return {abc[2], abc[1]};
case BA: return {abc[1], abc[0]};
case CA: return {abc[2], abc[0]};
case A: return {abc[0], 0};
case B: return {abc[1], 0};
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.
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
) {
std::vector<Slice*> result;
for (auto& s: slices)
if ( s.info.recycling == info.type
&& s.info.tuple == info.tuple
&& s.info.state == Recycled
) result.push_back(&s);
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&
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
const auto sliceIt
= std::find_if(slices.begin(), slices.end(),
[&info](Slice const& s) {
return info.recycling == s.info.type
&& info.tuple == s.info.tuple
&& State::Recycled != s.info.state
;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find: recycling source of "
<< pretty_print(info) << "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice not found: "
+ pretty_print(info)
+ " rank: "
+ pretty_print(Atrip::rank)
);
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
return *sliceIt;
}
#+end_src
#+begin_src c++ :tangle (atrip-slice-h)
static Slice& findByTypeAbc
( std::vector<Slice> &slices
, Slice::Type type
, ABCTuple const& abc
) {
const auto tuple = Slice::subtupleBySlice(abc, type);
const auto sliceIt
= std::find_if(slices.begin(), slices.end(),
[&type, &tuple](Slice const& s) {
return type == s.info.type
&& tuple == s.info.tuple
;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find:" << type << " and tuple "
<< pretty_print(tuple)
<< "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice not found: "
+ pretty_print(tuple)
+ ", "
+ pretty_print(type)
+ " rank: "
+ pretty_print(Atrip::rank)
);
return *sliceIt;
}
#+end_src
#+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(),
[&info](Slice const& s) {
// TODO: maybe implement comparison in Info struct
return info.type == s.info.type
&& info.state == s.info.state
&& info.tuple == s.info.tuple
&& info.from.rank == s.info.from.rank
&& info.from.source == s.info.from.source
;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find:looking for " << pretty_print(info) << "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice by info not found: "
+ pretty_print(info));
return *sliceIt;
}
#+end_src
*** 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,401 +14,393 @@ 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]] // [[file:../../atrip.org::*Location][Location:1]]
// ASSOCIATED TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% struct Location { size_t rank; size_t source; };
// Location:1 ends here
struct Location { size_t rank; size_t source; }; // [[file:../../atrip.org::*Type][Type:1]]
enum Type
enum Type { A = 10
{ A = 10 , B
, B , C
, C // Two-parameter slices
// Two-parameter slices , AB = 20
, AB = 20 , BC
, BC , AC
, AC // for abci and the doubles
// for abci and the doubles , CB
, CB , BA
, BA , CA
, CA // The non-typed slice
// The non-typed slice , Blank = 404
, Blank = 404
};
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
}; };
// Type:1 ends here
struct Info { // [[file:../../atrip.org::*State][State:1]]
// which part of a,b,c the slice holds enum State {
PartialTuple tuple; Fetch = 0,
// The type of slice for the user to retrieve the correct one Dispatched = 2,
Type type; Ready = 1,
// What is the state of the slice SelfSufficient = 911,
State state; Recycled = 123,
// Where the slice is to be retrieved Acceptor = 405
// NOTE: this can actually be computed from tuple };
Location from; // State:1 ends here
// If the data are actually to be found in this other slice
Type recycling;
Info() : tuple{0,0} // [[file:../../atrip.org::*The Info structure][The Info structure:1]]
, type{Blank} struct Info {
, state{Acceptor} // which part of a,b,c the slice holds
, from{0,0} PartialTuple tuple;
, recycling{Blank} // The type of slice for the user to retrieve the correct one
{} Type type;
// What is the state of the slice
State state;
// Where the slice is to be retrieved
Location from;
// If the data are actually to be found in this other slice
Type recycling;
Info() : tuple{0,0}
, type{Blank}
, state{Acceptor}
, from{0,0}
, recycling{Blank}
{}
};
using Ty_x_Tu = std::pair< Type, PartialTuple >;
// The Info structure:1 ends here
// [[file:../../atrip.org::*Name][Name:1]]
enum Name
{ TA = 100
, VIJKA = 101
, VABCI = 200
, TABIJ = 201
, VABIJ = 202
}; };
// Name:1 ends here
using Ty_x_Tu = std::pair< Type, PartialTuple >; // [[file:../../atrip.org::*Database][Database:1]]
struct LocalDatabaseElement {
Slice::Name name;
Slice::Info info;
};
// Database:1 ends here
// Names of the integrals that are considered in CCSD(T) // [[file:../../atrip.org::*Database][Database:2]]
enum Name using LocalDatabase = std::vector<LocalDatabaseElement>;
{ TA = 100 using Database = LocalDatabase;
, VIJKA = 101 // Database:2 ends here
, VABCI = 200
, TABIJ = 201
, VABIJ = 202
};
// DATABASE ==========================================================={{{1 // [[file:../../atrip.org::*MPI Types][MPI Types:1]]
struct LocalDatabaseElement { struct mpi {
Slice::Name name;
Slice::Info info;
};
using LocalDatabase = std::vector<LocalDatabaseElement>;
using Database = LocalDatabase;
static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
// STATIC METHODS =========================================================== MPI_Datatype dt;
// MPI_Type_vector(n, 1, 1, DT, &dt);
// They are useful to organize the structure of slices MPI_Type_commit(&dt);
return dt;
struct mpi {
static MPI_Datatype vector(size_t n, MPI_Datatype const& DT) {
MPI_Datatype dt;
MPI_Type_vector(n, 1, 1, DT, &dt);
MPI_Type_commit(&dt);
return dt;
}
static MPI_Datatype sliceLocation () {
constexpr int n = 2;
// create a sliceLocation to measure in the current architecture
// the packing of the struct
Slice::Location measure;
MPI_Datatype dt;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n] = {usizeDt(), usizeDt()};
// measure the displacements in the struct
size_t j = 0;
MPI_Aint displacements[n];
MPI_Get_address(&measure.rank, &displacements[j++]);
MPI_Get_address(&measure.source, &displacements[j++]);
for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
displacements[0] = 0;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
static MPI_Datatype enumDt() { return MPI_INT; }
static MPI_Datatype usizeDt() { return MPI_UINT64_T; }
static MPI_Datatype sliceInfo () {
constexpr int n = 5;
MPI_Datatype dt;
Slice::Info measure;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n]
= { vector(2, usizeDt())
, enumDt()
, enumDt()
, sliceLocation()
, enumDt()
};
// create the displacements from the info measurement struct
size_t j = 0;
MPI_Aint displacements[n];
MPI_Get_address(measure.tuple.data(), &displacements[j++]);
MPI_Get_address(&measure.type, &displacements[j++]);
MPI_Get_address(&measure.state, &displacements[j++]);
MPI_Get_address(&measure.from, &displacements[j++]);
MPI_Get_address(&measure.recycling, &displacements[j++]);
for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
displacements[0] = 0;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
static MPI_Datatype localDatabaseElement () {
constexpr int n = 2;
MPI_Datatype dt;
LocalDatabaseElement measure;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n]
= { enumDt()
, sliceInfo()
};
// measure the displacements in the struct
size_t j = 0;
MPI_Aint displacements[n];
MPI_Get_address(&measure.name, &displacements[j++]);
MPI_Get_address(&measure.info, &displacements[j++]);
for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
displacements[0] = 0;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
};
static
PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
switch (sliceType) {
case AB: return {abc[0], abc[1]};
case BC: return {abc[1], abc[2]};
case AC: return {abc[0], abc[2]};
case CB: return {abc[2], abc[1]};
case BA: return {abc[1], abc[0]};
case CA: return {abc[2], abc[0]};
case A: return {abc[0], 0};
case B: return {abc[1], 0};
case C: return {abc[2], 0};
default: throw "Switch statement not exhaustive!";
}
} }
static MPI_Datatype sliceLocation () {
constexpr int n = 2;
// create a sliceLocation to measure in the current architecture
// the packing of the struct
Slice::Location measure;
MPI_Datatype dt;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n] = {usizeDt(), usizeDt()};
/** // measure the displacements in the struct
* It is important here to return a reference to a Slice size_t j = 0;
* not to accidentally copy the associated buffer of the slice. MPI_Aint displacements[n];
*/ MPI_Get_address(&measure.rank, &displacements[j++]);
static Slice& findOneByType(std::vector<Slice> &slices, Slice::Type type) { MPI_Get_address(&measure.source, &displacements[j++]);
const auto sliceIt for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
= std::find_if(slices.begin(), slices.end(), displacements[0] = 0;
[&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;
}
/* MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
* Check if an info has MPI_Type_commit(&dt);
* return dt;
*/ }
static std::vector<Slice*> hasRecycledReferencingToIt
( std::vector<Slice> &slices
, Info const& info
) {
std::vector<Slice*> result;
for (auto& s: slices) static MPI_Datatype enumDt() { return MPI_INT; }
if ( s.info.recycling == info.type static MPI_Datatype usizeDt() { return MPI_UINT64_T; }
&& s.info.tuple == info.tuple
&& s.info.state == Recycled
) result.push_back(&s);
return result; static MPI_Datatype sliceInfo () {
} constexpr int n = 5;
MPI_Datatype dt;
Slice::Info measure;
const std::vector<int> lengths(n, 1);
const MPI_Datatype types[n]
= { vector(2, usizeDt())
, enumDt()
, enumDt()
, sliceLocation()
, enumDt()
};
static Slice& // create the displacements from the info measurement struct
findRecycledSource (std::vector<Slice> &slices, Slice::Info info) { size_t j = 0;
const auto sliceIt MPI_Aint displacements[n];
= std::find_if(slices.begin(), slices.end(), MPI_Get_address(measure.tuple.data(), &displacements[j++]);
[&info](Slice const& s) { MPI_Get_address(&measure.type, &displacements[j++]);
return info.recycling == s.info.type MPI_Get_address(&measure.state, &displacements[j++]);
&& info.tuple == s.info.tuple MPI_Get_address(&measure.from, &displacements[j++]);
&& State::Recycled != s.info.state MPI_Get_address(&measure.recycling, &displacements[j++]);
; for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
}); displacements[0] = 0;
WITH_CRAZY_DEBUG MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
WITH_RANK << "__slice__:find: recycling source of " MPI_Type_commit(&dt);
<< pretty_print(info) << "\n"; return dt;
if (sliceIt == slices.end()) }
throw std::domain_error( "Slice not found: "
+ pretty_print(info)
+ " rank: "
+ pretty_print(Atrip::rank)
);
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
return *sliceIt;
}
static Slice& findByTypeAbc static MPI_Datatype localDatabaseElement () {
( std::vector<Slice> &slices constexpr int n = 2;
, Slice::Type type MPI_Datatype dt;
, ABCTuple const& abc LocalDatabaseElement measure;
) { const std::vector<int> lengths(n, 1);
const auto tuple = Slice::subtupleBySlice(abc, type); const MPI_Datatype types[n]
const auto sliceIt = { enumDt()
= std::find_if(slices.begin(), slices.end(), , sliceInfo()
[&type, &tuple](Slice const& s) { };
return type == s.info.type
&& tuple == s.info.tuple
;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find:" << type << " and tuple "
<< pretty_print(tuple)
<< "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice not found: "
+ pretty_print(tuple)
+ ", "
+ pretty_print(type)
+ " rank: "
+ pretty_print(Atrip::rank)
);
return *sliceIt;
}
static Slice& findByInfo(std::vector<Slice> &slices, // measure the displacements in the struct
Slice::Info const& info) { size_t j = 0;
const auto sliceIt MPI_Aint displacements[n];
= std::find_if(slices.begin(), slices.end(), MPI_Get_address(&measure.name, &displacements[j++]);
[&info](Slice const& s) { MPI_Get_address(&measure.info, &displacements[j++]);
// TODO: maybe implement comparison in Info struct for (size_t i = 1; i < n; i++) displacements[i] -= displacements[0];
return info.type == s.info.type displacements[0] = 0;
&& info.state == s.info.state
&& info.tuple == s.info.tuple
&& info.from.rank == s.info.from.rank
&& info.from.source == s.info.from.source
;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find:looking for " << pretty_print(info) << "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice by info not found: "
+ pretty_print(info));
return *sliceIt;
}
// SLICE DEFINITION =================================================={{{1 MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
// ATTRIBUTES ============================================================ };
Info info; // MPI Types:1 ends here
F *data;
MPI_Request request;
const size_t size;
void markReady() noexcept { // [[file:../../atrip.org::*Static utilities][Static utilities:1]]
info.state = Ready; static
info.recycling = Blank; PartialTuple subtupleBySlice(ABCTuple abc, Type sliceType) {
} switch (sliceType) {
case AB: return {abc[0], abc[1]};
case BC: return {abc[1], abc[2]};
case AC: return {abc[0], abc[2]};
case CB: return {abc[2], abc[1]};
case BA: return {abc[1], abc[0]};
case CA: return {abc[2], abc[0]};
case A: return {abc[0], 0};
case B: return {abc[1], 0};
case C: return {abc[2], 0};
default: throw "Switch statement not exhaustive!";
}
}
// Static utilities:1 ends here
/* // [[file:../../atrip.org::*Static utilities][Static utilities:2]]
* This means that the data is there static std::vector<Slice*> hasRecycledReferencingToIt
*/ ( std::vector<Slice> &slices
bool isUnwrapped() const noexcept { , Info const& info
return info.state == Ready ) {
|| info.state == SelfSufficient std::vector<Slice*> result;
;
}
bool isUnwrappable() const noexcept { for (auto& s: slices)
return isUnwrapped() if ( s.info.recycling == info.type
|| info.state == Recycled && s.info.tuple == info.tuple
|| info.state == Dispatched && s.info.state == Recycled
; ) result.push_back(&s);
}
inline bool isDirectlyFetchable() const noexcept { return result;
return info.state == Ready || info.state == Dispatched; }
} // Static utilities:2 ends here
void free() noexcept { // [[file:../../atrip.org::*Static utilities][Static utilities:3]]
info.tuple = {0, 0}; static Slice& findOneByType(std::vector<Slice> &slices, Slice::Type type) {
info.type = Blank; const auto sliceIt
info.state = Acceptor; = std::find_if(slices.begin(), slices.end(),
info.from = {0, 0}; [&type](Slice const& s) {
info.recycling = Blank; return type == s.info.type;
data = nullptr; });
} 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
inline bool isFree() const noexcept { // [[file:../../atrip.org::*Static utilities][Static utilities:4]]
return info.tuple == PartialTuple{0, 0} static Slice&
&& info.type == Blank findRecycledSource (std::vector<Slice> &slices, Slice::Info info) {
&& info.state == Acceptor const auto sliceIt
&& info.from.rank == 0 = std::find_if(slices.begin(), slices.end(),
&& info.from.source == 0 [&info](Slice const& s) {
&& info.recycling == Blank return info.recycling == s.info.type
&& data == nullptr && info.tuple == s.info.tuple
; && State::Recycled != s.info.state
} ;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find: recycling source of "
<< pretty_print(info) << "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice not found: "
+ pretty_print(info)
+ " rank: "
+ pretty_print(Atrip::rank)
);
WITH_RANK << "__slice__:find: " << pretty_print(sliceIt->info) << "\n";
return *sliceIt;
}
// Static utilities:4 ends here
/* // [[file:../../atrip.org::*Static utilities][Static utilities:5]]
* This function answers the question, which slices can be recycled. static Slice& findByTypeAbc
* ( std::vector<Slice> &slices
* A slice can only be recycled if it is Fetch or Ready and has , Slice::Type type
* a valid datapointer. , ABCTuple const& abc
* ) {
* In particular, SelfSufficient are not recyclable, since it is easier const auto tuple = Slice::subtupleBySlice(abc, type);
* just to create a SelfSufficient slice than deal with data dependencies. const auto sliceIt
* = std::find_if(slices.begin(), slices.end(),
* Furthermore, a recycled slice is not recyclable, if this is the case [&type, &tuple](Slice const& s) {
* then it is either bad design or a bug. return type == s.info.type
*/ && tuple == s.info.tuple
inline bool isRecyclable() const noexcept { ;
return ( info.state == Dispatched });
|| info.state == Ready WITH_CRAZY_DEBUG
|| info.state == Fetch WITH_RANK << "__slice__:find:" << type << " and tuple "
) << pretty_print(tuple)
&& hasValidDataPointer() << "\n";
; if (sliceIt == slices.end())
} throw std::domain_error( "Slice not found: "
+ pretty_print(tuple)
+ ", "
+ pretty_print(type)
+ " rank: "
+ pretty_print(Atrip::rank)
);
return *sliceIt;
}
// Static utilities:5 ends here
/* // [[file:../../atrip.org::*Static utilities][Static utilities:6]]
* This function describes if a slice has a valid data pointer. static Slice& findByInfo(std::vector<Slice> &slices,
* Slice::Info const& info) {
* This is important to know if the slice has some data to it, also const auto sliceIt
* some structural checks are done, so that it should not be Acceptor = std::find_if(slices.begin(), slices.end(),
* or Blank, if this is the case then it is a bug. [&info](Slice const& s) {
*/ // TODO: maybe implement comparison in Info struct
inline bool hasValidDataPointer() const noexcept { return info.type == s.info.type
return data != nullptr && info.state == s.info.state
&& info.state != Acceptor && info.tuple == s.info.tuple
&& info.type != Blank && info.from.rank == s.info.from.rank
; && info.from.source == s.info.from.source
} ;
});
WITH_CRAZY_DEBUG
WITH_RANK << "__slice__:find:looking for " << pretty_print(info) << "\n";
if (sliceIt == slices.end())
throw std::domain_error( "Slice by info not found: "
+ pretty_print(info));
return *sliceIt;
}
// Static utilities:6 ends here
void unwrapAndMarkReady() { // [[file:../../atrip.org::*Attributes][Attributes:1]]
Info info;
// Attributes:1 ends here
// [[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 {
info.state = Ready;
info.recycling = Blank;
}
// Member functions:1 ends here
// [[file:../../atrip.org::*Member functions][Member functions:2]]
bool isUnwrapped() const noexcept {
return info.state == Ready
|| info.state == SelfSufficient
;
}
// Member functions:2 ends here
// [[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 {
return info.state == Ready || info.state == Dispatched;
}
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 {
return info.tuple == PartialTuple{0, 0}
&& info.type == Blank
&& info.state == Acceptor
&& info.from.rank == 0
&& info.from.source == 0
&& info.recycling == Blank
&& data == nullptr
;
}
// Member functions:3 ends here
// [[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
// [[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
// [[file:../../atrip.org::*Member functions][Member functions:6]]
void unwrapAndMarkReady() {
if (info.state == Ready) return; if (info.state == Ready) return;
if (info.state != Dispatched) if (info.state != Dispatched)
throw throw
@ -437,17 +429,20 @@ struct Slice {
<< "\n"; << "\n";
#endif #endif
} }
// Member functions:6 ends here
Slice(size_t size_) // [[file:../../atrip.org::*Epilog][Epilog:1]]
: info({}) Slice(size_t size_)
, data(nullptr) : info({})
, size(size_) , 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) { 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