-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic variable shaping not working as expected with MPI #3709
Comments
Perhaps a related issue is when I do the same with #include <Kokkos_Core.hpp>
#include <stdio.h>
#include <chrono>
#include <iostream>
#include <numeric>
#ifdef ADIOS2_ENABLED
# include <adios2.h>
# include <adios2/cxx11/KokkosView.h>
#endif
#ifdef MPI_ENABLED
# include <mpi.h>
#endif
namespace math = Kokkos;
auto Initialize(int argc, char* argv[]) -> void;
auto Finalize() -> void;
#ifdef ADIOS2_ENABLED
auto main(int argc, char* argv[]) -> int {
Initialize(argc, argv);
try {
int mpi_rank = 0, mpi_size = 1;
# ifndef MPI_ENABLED
adios2::ADIOS adios;
# else
MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
adios2::ADIOS adios(MPI_COMM_WORLD);
# endif
auto io = adios.DeclareIO("Test-Output");
io.SetEngine("HDF5");
const std::size_t maxptl = 200;
const std::size_t ncells = 100;
const auto nsteps = 10;
auto myarr1 = Kokkos::View<double*>("myarr1", maxptl);
auto myarr2 = Kokkos::View<double*>("myarr2", ncells);
io.DefineVariable<double>("myarr1", {}, {}, { adios2::UnknownDim });
io.DefineVariable<double>(
"myarr2", { ncells * mpi_size }, { ncells * mpi_rank }, { ncells });
auto writer = io.Open("test.h5", adios2::Mode::Write);
for (auto t { 0 }; t < nsteps; ++t) {
if (mpi_rank == 0) {
printf("Timestep #%d\n\n", t);
}
const auto nprt = (std::size_t)((t + 1) * (mpi_rank + 1));
if (nprt > maxptl) {
throw std::runtime_error("Too many particles");
}
adios.EnterComputationBlock();
Kokkos::parallel_for(
"fill_myarr1", nprt, KOKKOS_LAMBDA(const int i) {
myarr1(i) += static_cast<double>(i);
});
Kokkos::parallel_for(
"fill_myarr2", ncells, KOKKOS_LAMBDA(const int i) {
myarr2(i) += static_cast<double>(i) + 0.1 * t;
});
adios.ExitComputationBlock();
writer.BeginStep();
// write myarr1
auto myarr1_slice = Kokkos::View<double*>("myarr1_slice", nprt);
auto slice = std::make_pair((std::size_t)0, (std::size_t)nprt);
Kokkos::deep_copy(myarr1_slice, Kokkos::subview(myarr1, slice));
auto var1 = io.InquireVariable<double>("myarr1");
var1.SetSelection({ {}, { nprt } });
writer.Put<double>(var1, myarr1_slice);
// write myarr2
auto var2 = io.InquireVariable<double>("myarr2");
writer.Put<double>(var2, myarr2);
writer.EndStep();
}
if (mpi_rank == 0) {
std::cout << "Closing file\n";
}
writer.Close();
} catch (std::exception& e) {
std::cerr << "Exception caught: " << e.what() << '\n';
Finalize();
return 1;
}
Finalize();
return 0;
}
#else
auto main() -> int {
std::cout << "ADIOS2 is not enabled.\n";
return 0;
}
#endif
auto Initialize(int argc, char* argv[]) -> void {
Kokkos::initialize(argc, argv);
#ifdef MPI_ENABLED
MPI_Init(&argc, &argv);
#endif
}
auto Finalize() -> void {
#ifdef MPI_ENABLED
MPI_Finalize();
#endif
Kokkos::finalize();
} If I comment out the output of Info:
Just in case, I added both of the examples (with the |
UPD I checked with the default |
UPD I think I figured it out. The key was to:
This is a bit counter-intuitive since the examples referred to above from this repo showed a different use case. Would still be interested to hear from the devs, on what's the correct way of doing this, so leaving this issue open for now. |
I'm puzzled by this example. adios2::UnknownDim is just constexpr size_t 0 and doesn't appear in the actual implementation of ADIOS. (I.E. it's just in ADIOSTypes.h and in the example, nowhere else, so it's not like ADIOS is looking for that value.). I'm not really sure what is going on here, so I'm going to punt this to @pnorbert (who is unfortunately on vacation at the moment, so it might be a few days.) |
@eisenhauer, no that i understand. i think it's fair to say i could have pretty much used any value, instead of |
Indeed, I haven't looked at this case, but just a note, your original
definition is a LocalArray definition (empty Shape and Start), which may
not be updatable (SetShape and SetSelection should fail), and your fix is
using a 1D GlobalArray with proper setting before calling Put. I am not
sure if this is how it is or should be, I am just imagining this is the
case.
…On Tue, Jul 25, 2023 at 1:29 PM Greg Eisenhauer ***@***.***> wrote:
I'm puzzled by this example. adios2::UnknownDim is just constexpr size_t 0
and doesn't appear in the actual implementation of ADIOS. (I.E. it's just
in ADIOSTypes.h and in the example, nowhere else, so it's not like ADIOS is
looking for that value.). I'm not really sure what is going on here, so I'm
going to punt this to @pnorbert <https://github.com/pnorbert> (who is
unfortunately on vacation at the moment, so it might be a few days.)
—
Reply to this email directly, view it on GitHub
<#3709 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAYYYLKBOWK3PW3NJANHPOTXR766XANCNFSM6AAAAAA2U7BJ3M>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
@pnorbert that indeed seems to be the case, as when i attempted to adios2::Variable<double> varV2 =
io.DefineVariable<double>("v2", {}, {}, {adios2::UnknownDim});
// ...
varV2.SetSelection(adios2::Box<adios2::Dims>({}, {Nelems}));
writer.Put<double>(varV2, v2.data()); |
What did bpls -lD tell you about the BP5 datasets? Were they wrong? Your original expectation was certainly wrong. Writing LocalArrays will not result in GlobalArray at reading. since you did not define for v0 and v2 how the blocks should be arranged into a global array. If you are looking for adios to put blocks automatically together for you, that's called adios2::JoinedDim, but it only works for BP4 and BP5. |
Did you always wanted GlobalArray, that is, an array which has a (single global) shape, but assumed you needed LocalArray for writing from multiple processes, or writing multiple blocks from a process? That is certainly not the case. We use LocalArrays only for cases where the blocks cannot logically organized into a global array. And almost always there is a way to put them into a global view, maybe a sparsely filled one. The only reason not to do that is to save on global communication to calculate the start offsets on each processor, and the global shape on all processors, especially for changing arrays. But then, joined array can still help if blocks only change size in one dimension. |
@pnorbert thanks for a detailed response! Yes, i indeed must have misunderstood the concept of the solution i have in mind now basically looks like this (pseudocode): io.DefineVariable<double>(
"particles", { adios2::UnknownDim }, { adios2::UnknownDim }, { adios2::UnknownDim });
// let `npart` be # of particles on a given MPI task
// and `particle_array` is the array of particles (double) of size `npart`
MPI_Allgather(&npart, npart_all);
// `npart_all` is the array of `npart`-s from all MPI ranks
offset = sum(npart_all[ :mpi_rank ]);
npart_global = sum(npart_all[ : ]);
auto var_p = io.InquireVariable<double>("particles");
var_p.SetShape({ npart_global });
var_p.SetSelection({ { offset }, { nprt } });
writer.Put<double>(var_p, particle_array); does this sound reasonable? (we use HDF5 as the output engine most of the time, so |
Sounds good.
You only have 1 double value for the particle? PIC codes usually put
multiple particle attributes in columns and produce a 2D table, one row per
particle.
The JoinedArray concept was created for particles, so you wouldn't need to
do that AllGather() and sum(), just Put() all locally available set of
particles/table and let adios join them on the 1st/slow dimension.
…On Tue, Jul 25, 2023, 7:16 PM hayk ***@***.***> wrote:
@pnorbert <https://github.com/pnorbert> thanks for a detailed response!
Yes, i indeed must have misunderstood the concept of local vs global
arrays. indeed i want a *global* array, imagine particle data spread out
across multiple MPI blocks: they can logically be combined into a single
global array. the use case i have in mind basically assumes that at each
timestep there is an unknown number of particles in each MPI block:
$n_1,~n_2,\dots$, which can nonetheless be logically concatenated into a
single array of length $n_1+n_2+\dots$.
the solution i have in mind now basically looks like this (pseudocode):
io.DefineVariable<double>(
"particles", { adios2::UnknownDim }, { adios2::UnknownDim }, { adios2::UnknownDim });
// let `npart` be # of particles on a given MPI task// and `particle_array` is the array of particles (double) of size `npart`MPI_Allgather(&npart, npart_all);// `npart_all` is the array of `npart`-s from all MPI ranks
offset = sum(npart_all[ :mpi_rank ]);
npart_global = sum(npart_all[ : ]);
auto var_p = io.InquireVariable<double>("particles");
var_p.SetShape({ npart_global });
var_p.SetSelection({ { offset }, { nprt } });
writer.Put<double>(var_p, particle_array);
—
Reply to this email directly, view it on GitHub
<#3709 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAYYYLJKGBX7YCJA3KGQASDXSAEORANCNFSM6AAAAAA2U7BJ3M>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
yes, we have multiple different quantities, like positions, velocities, etc. (in fact, these might have different types too: e.g., velocities are double, while positions are single). so the plan was to simply use the routine i outlined above to write every individual array separately. i'm assuming of course, that the arrays are not reshuffled, right? because the order of indices in the array of particle velocities has to be the same as for the coordinates. |
Yes, that's the very first question everyone asks about joined arrays.
The order is determined by ranks and then the order you call multiple Put()
for one variable on one process (if you have multiple blocks per process
which you don't seem to).
So assuming you write the same number of blocks for each variable and if
across all variables on one process the block sizes are the same, then you
will get the same ordering for each variable.
…On Tue, Jul 25, 2023, 7:40 PM hayk ***@***.***> wrote:
yes, we have multiple different quantities, like positions, velocities,
etc. (in fact, these might have different types too: e.g., velocities are
double, while positions are single). so the plan was to simply use the
routine i outlined above to write every individual array separately. i'm
assuming of course, that the arrays are not reshuffled, right? because the
order of indices in the array of particle velocities has to be the same as
for the coordinates.
—
Reply to this email directly, view it on GitHub
<#3709 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAYYYLLBCO7NTAEK34PDB6LXSAHIBANCNFSM6AAAAAA2U7BJ3M>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Thank you for responding. I think perhaps clarifying this confusion in the documentation might prove very helpful. |
To Reproduce
Here's the minimum reproducible example.
Write three vectors `v0` (implicit fixed size), `v1` (explicit fixed size + offsets) and `v2` (variable size).
Example was adapted from the ADIOS2 examples.
After compilation and running (e.g.,
mpiexec -np 8 <exec>
), it produceslocalArray.h5
, and when I view it withh5dump
, both the sizes ofv0
andv2
datasets are wrong. Their content content seems to be randomly picked from either of the MPI ranks. Forv0
I expect the size to be6 * nproc
, while forv2
, the size of each sub-dataset is chosen randomly at every time between 5 and 10, and I expect the outputted length to be the sum of all sizes (all MPI ranks).example output
Info:
Stellar
cluster in Princeton University,Intel(R) Xeon(R) Gold 6242R CPU
Flags used to compile ADIOS2:
I might be doing something wrong or using the dynamic writing not as intended. Could you please have a look and let me know if there is a bug on my end, or if something is indeed going wrong?
Thanks in advance!
The text was updated successfully, but these errors were encountered: