49 const Vector<T>& a_boxLoads,
50 const Vector<Box>& a_boxes)
52 CH_TIME(
"LoadBalancing::makeBalance");
55 Vector<Real> boxLoads;
56 for (
int i = 0; i < a_boxLoads.size(); i++) {
57 boxLoads.push_back(1.0 * a_boxLoads[i]);
62 const int numBoxes =
static_cast<int>(a_boxes.size());
63 const int numRanks =
static_cast<int>(numProc());
64 const int numSubsets = std::min(numBoxes, numRanks);
66 a_ranks.resize(numBoxes);
72 for (
int ibox = 0; ibox < numBoxes; ibox++) {
73 totalLoad += boxLoads[ibox];
76 Real staticTargetLoad = totalLoad / numSubsets;
82 using Span = std::pair<int, int>;
83 using Subset = std::pair<Span, Real>;
85 std::vector<Subset> subsets(numSubsets);
87 int firstSubsetBox = 0;
89 Real remainingLoad = totalLoad;
91 for (
int curSubset = 0; curSubset < numSubsets; curSubset++) {
94 Real subsetLoad = boxLoads[firstSubsetBox];
96 int lastSubsetBox = firstSubsetBox;
98 const int subsetsLeft = numSubsets - (curSubset + 1);
99 const int boxesLeft = numBoxes - (firstSubsetBox + 1);
101 if (boxesLeft > subsetsLeft) {
102 for (
int ibox = firstSubsetBox + 1; ibox < numBoxes; ibox++) {
106 if (numBoxes - lastSubsetBox - 1 <= subsetsLeft) {
114 const Real load1 = subsetLoad;
115 const Real load2 = subsetLoad + boxLoads[ibox];
118 bool addBoxToSubset =
false;
120 if (boxLoads[ibox] <= std::numeric_limits<Real>::epsilon()) {
121 addBoxToSubset =
true;
123 else if (load1 > staticTargetLoad) {
124 addBoxToSubset =
false;
126 else if (load2 <= staticTargetLoad) {
127 addBoxToSubset =
true;
129 else if (load1 <= staticTargetLoad && load2 > staticTargetLoad) {
132 const Real loadErrWithoutBox = std::abs(load1 - staticTargetLoad);
133 const Real loadErrWithBox = std::abs(load2 - staticTargetLoad);
135 if (loadErrWithBox <= loadErrWithoutBox) {
136 addBoxToSubset =
true;
141 if (addBoxToSubset) {
142 subsetLoad = subsetLoad + boxLoads[ibox];
143 lastSubsetBox = ibox;
148 lastSubsetBox = ibox - 1;
156 subsets[curSubset] = std::make_pair(std::make_pair(firstSubsetBox, lastSubsetBox), subsetLoad);
159 remainingLoad = remainingLoad - subsetLoad;
160 staticTargetLoad = remainingLoad / subsetsLeft;
163 firstSubsetBox = lastSubsetBox + 1;
167 std::sort(subsets.begin(), subsets.end(), [](
const Subset& A,
const Subset& B) ->
bool {
168 return A.second > B.second;
172 const std::vector<std::pair<int, Real>> sortedRankLoads = a_rankLoads.
getSortedLoads();
175 for (
int i = 0; i < subsets.size(); i++) {
176 const int startIndex = subsets[i].first.first;
177 const int endIndex = subsets[i].first.second;
178 const Real subsetLoad = subsets[i].second;
179 const int rank = sortedRankLoads[i].first;
182 for (
int ibox = startIndex; ibox <= endIndex; ibox++) {
183 a_ranks[ibox] = rank;
240 CH_TIME(
"LoadBalancing::sort");
243 case BoxSorting::None: {
246 case BoxSorting::Std: {
251 case BoxSorting::Shuffle: {
256 case BoxSorting::Morton: {
257 LoadBalancing::sortSFC<T, SpaceDim>(a_boxes, a_loads, LoadBalancing::mortonIndex<SpaceDim>);
261 case BoxSorting::Hilbert: {
262 LoadBalancing::sortSFC<T, SpaceDim>(a_boxes, a_loads, LoadBalancing::hilbertIndex<SpaceDim>);
267 MayDay::Abort(
"LoadBalancing::sort_boxes - unknown algorithm requested");
294 CH_TIME(
"LoadBalancing::shuffleSort");
299 auto seed =
static_cast<int>(std::chrono::system_clock::now().time_since_epoch().count());
301 MPI_Bcast(&seed, 1, MPI_INT, 0, Chombo_MPI::comm);
305 std::default_random_engine e(seed);
306 std::shuffle(vec.begin(), vec.end(), e);
316 const std::function<uint64_t(
const std::array<uint32_t, DIM>)>& a_sfcEncoder)
noexcept
319 CH_TIME(
"LoadBalancing::sortSFC");
321 using Bundle = std::tuple<Box, T, uint64_t>;
323 const int n =
static_cast<int>(a_boxes.size());
324 if (n == 0 || n != a_loads.size()) {
329 std::vector<Bundle> buf;
330 for (std::size_t i = 0; i < n; ++i) {
331 buf.emplace_back(std::move(a_boxes[i]), std::move(a_loads[i]), uint64_t{0});
335 for (std::size_t i = 0; i < n; ++i) {
336 const auto& b = std::get<0>(buf[i]);
337 const auto iv = b.smallEnd();
339 std::array<uint32_t, SpaceDim> c;
341 for (
int d = 0; d < SpaceDim; ++d) {
342 c[d] =
static_cast<uint32_t
>(iv[d]);
345 std::get<2>(buf[i]) = a_sfcEncoder(c);
349 std::sort(buf.begin(), buf.end(), [](
const Bundle& a,
const Bundle& b)
noexcept {
350 return std::get<2>(a) < std::get<2>(b);
354 for (std::size_t i = 0; i < n; ++i) {
355 a_boxes[i] = std::move(std::get<0>(buf[i]));
356 a_loads[i] = std::move(std::get<1>(buf[i]));
368 const int maxDim = 1 << 21;
370 for (
int dir = 0; dir < SpaceDim; dir++) {
371 if (a_coords[dir] > maxDim) {
372 MayDay::Abort(
"LoadBalancing::mortonIndex - logic bust");
376 for (
int bit = 20; bit >= 0; --bit) {
377 for (
int dir = CH_SPACEDIM - 1; dir >= 0; --dir) {
378 const uint64_t b = (
static_cast<uint64_t
>(
static_cast<uint32_t
>(a_coords[dir])) >> bit) & 1ULL;
380 code = (code << 1) | b;
391 CH_TIME(
"LoadBalancing::hilbertIndex");
393 constexpr int nbits = 21;
397 for (
int i = 0; i < DIM; ++i) {
401 const uint32_t M = 1U << (nbits - 1);
403 for (uint32_t Q = M; Q > 1; Q >>= 1) {
404 const uint32_t P = Q - 1;
406 for (
int i = 0; i < DIM; ++i) {
411 const uint32_t t = (x[0] ^ x[i]) & P;
419 for (
int i = 1; i < DIM; ++i) {
424 for (uint32_t Q = M; Q > 1; Q >>= 1) {
425 if (x[DIM - 1] & Q) {
430 for (
int i = 0; i < DIM; ++i) {
436 for (
int b = nbits - 1; b >= 0; --b) {
437 for (
int i = 0; i < DIM; ++i) {
438 idx = (idx << 1) | ((x[i] >> b) & 1U);
static uint64_t mortonIndex(const std::array< uint32_t, DIM > &a_coords) noexcept
Compute a Morton (Z-order) code from integer coordinates, using 21 bits per direction.
Definition CD_LoadBalancingImplem.H:362
static void makeBalance(Vector< int > &a_ranks, const Vector< T > &a_loads, const Vector< Box > &a_boxes)
Load balancing, assigning ranks to boxes.
Definition CD_LoadBalancingImplem.H:36
static uint64_t hilbertIndex(const std::array< uint32_t, DIM > &a_coords)
Compute a Hilbert-curve index from integer coordinates, using 21 bits per direction.
Definition CD_LoadBalancingImplem.H:389
static void standardSort(Vector< Box > &a_boxes, Vector< T > &a_loads)
Standard box sorting, calls C++ std::sort.
Definition CD_LoadBalancingImplem.H:276
static void sort(Vector< Vector< Box > > &a_boxes, Vector< Vector< T > > &a_loads, const BoxSorting a_whichSorting)
Sorts boxes and loads over a hierarchy according to some sorting criterion.
Definition CD_LoadBalancingImplem.H:227
static void unpackPairs(Vector< Box > &a_boxes, Vector< T > &a_loads, const std::vector< std::pair< Box, T > > &a_pairs)
Splits vector pair into separate boxes and loads.
Definition CD_LoadBalancingImplem.H:211
static void sortSFC(Vector< Box > &a_boxes, Vector< T > &a_loads, const std::function< uint64_t(const std::array< uint32_t, DIM >)> &a_sfcEncoder) noexcept
Generic SFC sorting function.
Definition CD_LoadBalancingImplem.H:314
static std::vector< std::pair< Box, T > > packPairs(const Vector< Box > &a_boxes, const Vector< T > &a_loads)
Utility function which packs boxes and loads into a vector of pairs.
Definition CD_LoadBalancingImplem.H:197