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 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,53 +110,102 @@ 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 {
// 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;
*** The Info structure
Info() : tuple{0,0}
, type{Blank}
, state{Acceptor}
, from{0,0}
, recycling{Blank}
{}
};
Every slice has an information structure associated with it
that keeps track of the **variable** type, state and so on.
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
{ TA = 100
, VIJKA = 101
@ -111,257 +213,352 @@ 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
*** MPI Types
#+begin_src c++ :tangle (atrip-slice-h)
struct mpi {
// STATIC METHODS ===========================================================
//
// They are useful to organize the structure of slices
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 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()};
/**
,* 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;
}
// 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;
/*
,* Check if an info has
,*
,*/
static std::vector<Slice*> hasRecycledReferencingToIt
( std::vector<Slice> &slices
, Info const& info
) {
std::vector<Slice*> result;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
for (auto& s: slices)
if ( s.info.recycling == info.type
&& s.info.tuple == info.tuple
&& s.info.state == Recycled
) result.push_back(&s);
static MPI_Datatype enumDt() { return MPI_INT; }
static MPI_Datatype usizeDt() { return MPI_UINT64_T; }
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&
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
;
});
// 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;
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;
}
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
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;
}
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()
};
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;
}
// 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;
// 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;
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

View File

@ -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,401 +14,393 @@ 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
{ A = 10
, B
, C
// Two-parameter slices
, AB = 20
, BC
, AC
// for abci and the doubles
, CB
, BA
, CA
// The non-typed slice
, 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
// [[file:../../atrip.org::*Type][Type:1]]
enum Type
{ A = 10
, B
, C
// Two-parameter slices
, AB = 20
, BC
, AC
// for abci and the doubles
, CB
, BA
, CA
// The non-typed slice
, Blank = 404
};
// Type:1 ends here
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
// NOTE: this can actually be computed from tuple
Location from;
// If the data are actually to be found in this other slice
Type recycling;
// [[file:../../atrip.org::*State][State:1]]
enum State {
Fetch = 0,
Dispatched = 2,
Ready = 1,
SelfSufficient = 911,
Recycled = 123,
Acceptor = 405
};
// State:1 ends here
Info() : tuple{0,0}
, type{Blank}
, state{Acceptor}
, from{0,0}
, recycling{Blank}
{}
// [[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
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)
enum Name
{ TA = 100
, VIJKA = 101
, VABCI = 200
, TABIJ = 201
, VABIJ = 202
};
// [[file:../../atrip.org::*Database][Database:2]]
using LocalDatabase = std::vector<LocalDatabaseElement>;
using Database = LocalDatabase;
// Database:2 ends here
// DATABASE ==========================================================={{{1
struct LocalDatabaseElement {
Slice::Name name;
Slice::Info info;
};
using LocalDatabase = std::vector<LocalDatabaseElement>;
using Database = LocalDatabase;
// [[file:../../atrip.org::*MPI Types][MPI Types:1]]
struct mpi {
// STATIC METHODS ===========================================================
//
// They are useful to organize the structure of slices
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 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()};
/**
* 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;
}
// 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;
/*
* Check if an info has
*
*/
static std::vector<Slice*> hasRecycledReferencingToIt
( std::vector<Slice> &slices
, Info const& info
) {
std::vector<Slice*> result;
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
for (auto& s: slices)
if ( s.info.recycling == info.type
&& s.info.tuple == info.tuple
&& s.info.state == Recycled
) result.push_back(&s);
static MPI_Datatype enumDt() { return MPI_INT; }
static MPI_Datatype usizeDt() { return MPI_UINT64_T; }
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&
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
;
});
// 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;
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;
}
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
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;
}
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()
};
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;
}
// 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;
// SLICE DEFINITION =================================================={{{1
MPI_Type_create_struct(n, lengths.data(), displacements, types, &dt);
MPI_Type_commit(&dt);
return dt;
}
// ATTRIBUTES ============================================================
Info info;
F *data;
MPI_Request request;
const size_t size;
};
// MPI Types:1 ends here
void markReady() noexcept {
info.state = Ready;
info.recycling = Blank;
}
// [[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]};
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
/*
* This means that the data is there
*/
bool isUnwrapped() const noexcept {
return info.state == Ready
|| info.state == SelfSufficient
;
}
// [[file:../../atrip.org::*Static utilities][Static utilities:2]]
static std::vector<Slice*> hasRecycledReferencingToIt
( std::vector<Slice> &slices
, Info const& info
) {
std::vector<Slice*> result;
bool isUnwrappable() const noexcept {
return isUnwrapped()
|| info.state == Recycled
|| info.state == Dispatched
;
}
for (auto& s: slices)
if ( s.info.recycling == info.type
&& s.info.tuple == info.tuple
&& s.info.state == Recycled
) result.push_back(&s);
inline bool isDirectlyFetchable() const noexcept {
return info.state == Ready || info.state == Dispatched;
}
return result;
}
// Static utilities:2 ends here
void free() noexcept {
info.tuple = {0, 0};
info.type = Blank;
info.state = Acceptor;
info.from = {0, 0};
info.recycling = Blank;
data = nullptr;
}
// [[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
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
;
}
// [[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) {
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;
}
// Static utilities:4 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 {
return ( info.state == Dispatched
|| info.state == Ready
|| info.state == Fetch
)
&& hasValidDataPointer()
;
}
// [[file:../../atrip.org::*Static utilities][Static utilities:5]]
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;
}
// Static utilities:5 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 {
return data != nullptr
&& info.state != Acceptor
&& info.type != Blank
;
}
// [[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(),
[&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;
}
// 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 != Dispatched)
throw
@ -437,17 +429,20 @@ struct Slice {
<< "\n";
#endif
}
// Member functions:6 ends here
Slice(size_t size_)
: info({})
, data(nullptr)
, size(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