blob: bd6c6c8e3cb4dbc2b65d99def629170b9f87189a [file] [log] [blame]
//@HEADER
// ************************************************************************
//
// MiniFE: Simple Finite Element Assembly and Solve
// Copyright (2006-2013) Sandia Corporation
//
// Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
// license for use of this work by or on behalf of the U.S. Government.
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
//
// ************************************************************************
//@HEADER
#ifndef _imbalance_hpp_
#define _imbalance_hpp_
#include <cmath>
#ifdef HAVE_MPI
#include <mpi.h>
#endif
#include <box_utils.hpp>
#include <utils.hpp>
#include <YAML_Doc.hpp>
namespace miniFE {
const int X = 0;
const int Y = 1;
const int Z = 2;
const int NONE = 3;
const int LOWER = 0;
const int UPPER = 1;
template<typename GlobalOrdinal>
void
compute_imbalance(const Box& global_box,
const Box& local_box,
float& largest_imbalance,
float& std_dev,
YAML_Doc& doc,
bool record_in_doc)
{
int numprocs = 1, myproc = 0;
#ifdef HAVE_MPI
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myproc);
#endif
GlobalOrdinal local_nrows = get_num_ids<GlobalOrdinal>(local_box);
GlobalOrdinal min_nrows = 0, max_nrows = 0, global_nrows = 0;
int min_proc = myproc, max_proc = myproc;
get_global_min_max(local_nrows, global_nrows, min_nrows, min_proc,
max_nrows, max_proc);
float avg_nrows = global_nrows;
avg_nrows /= numprocs;
//largest_imbalance will be the difference between the min (or max)
//rows-per-processor and avg_nrows, represented as a percentage:
largest_imbalance = percentage_difference<float>(min_nrows, avg_nrows);
float tmp = percentage_difference<float>(max_nrows, avg_nrows);
if (tmp > largest_imbalance) largest_imbalance = tmp;
std_dev = compute_std_dev_as_percentage<float>(local_nrows, avg_nrows);
if (myproc == 0 && record_in_doc) {
doc.add("Rows-per-proc Load Imbalance","");
doc.get("Rows-per-proc Load Imbalance")->add("Largest (from avg, %)",largest_imbalance);
doc.get("Rows-per-proc Load Imbalance")->add("Std Dev (%)",std_dev);
}
}
std::pair<int,int>
decide_how_to_grow(const Box& global_box, const Box& local_box)
{
std::pair<int,int> result(NONE,UPPER);
if (local_box[Z][UPPER] < global_box[Z][UPPER]) {
result.first = Z;
result.second = UPPER;
return result;
}
if (local_box[Z][LOWER] > global_box[Z][LOWER]) {
result.first = Z;
result.second = LOWER;
return result;
}
if (local_box[Y][UPPER] < global_box[Y][UPPER]) {
result.first = Y;
result.second = UPPER;
return result;
}
if (local_box[Y][LOWER] > global_box[Y][LOWER]) {
result.first = Y;
result.second = LOWER;
return result;
}
if (local_box[X][UPPER] < global_box[X][UPPER]) {
result.first = X;
result.second = UPPER;
return result;
}
if (local_box[X][LOWER] > global_box[X][LOWER]) {
result.first = X;
result.second = LOWER;
return result;
}
return result;
}
std::pair<int,int>
decide_how_to_shrink(const Box& global_box, const Box& local_box)
{
std::pair<int,int> result(NONE,UPPER);
if (local_box[Z][UPPER] < global_box[Z][UPPER] && local_box[Z][UPPER]-local_box[Z][LOWER] > 2) {
result.first = Z;
result.second = UPPER;
return result;
}
if (local_box[Z][LOWER] > global_box[Z][LOWER] && local_box[Z][UPPER]-local_box[Z][LOWER] > 2) {
result.first = Z;
result.second = LOWER;
return result;
}
if (local_box[Y][UPPER] < global_box[Y][UPPER] && local_box[Y][UPPER]-local_box[Y][LOWER] > 2) {
result.first = Y;
result.second = UPPER;
return result;
}
if (local_box[Y][LOWER] > global_box[Y][LOWER] && local_box[Y][UPPER]-local_box[Y][LOWER] > 2) {
result.first = Y;
result.second = LOWER;
return result;
}
if (local_box[X][UPPER] < global_box[X][UPPER] && local_box[X][UPPER]-local_box[X][LOWER] > 2) {
result.first = X;
result.second = UPPER;
return result;
}
if (local_box[X][LOWER] > global_box[X][LOWER] && local_box[X][UPPER]-local_box[X][LOWER] > 2) {
result.first = X;
result.second = LOWER;
return result;
}
return result;
}
template<typename GlobalOrdinal>
void
add_imbalance(const Box& global_box,
Box& local_box,
float imbalance,
YAML_Doc& doc)
{
int numprocs = 1, myproc = 0;
#ifdef HAVE_MPI
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myproc);
#endif
if (numprocs == 1) {
return;
}
float cur_imbalance = 0, cur_std_dev = 0;
compute_imbalance<GlobalOrdinal>(global_box, local_box,
cur_imbalance, cur_std_dev, doc, false);
while (cur_imbalance < imbalance) {
GlobalOrdinal local_nrows = get_num_ids<GlobalOrdinal>(local_box);
GlobalOrdinal min_nrows = 0, max_nrows = 0, global_nrows = 0;
int min_proc = myproc, max_proc = myproc;
get_global_min_max(local_nrows, global_nrows, min_nrows, min_proc,
max_nrows, max_proc);
std::pair<int,int> grow(NONE,UPPER);
int grow_axis_val = -1;
std::pair<int,int> shrink(NONE,UPPER);
int shrink_axis_val = -1;
if (myproc == max_proc) {
grow = decide_how_to_grow(global_box, local_box);
if (grow.first != NONE) {
grow_axis_val = local_box[grow.first][grow.second];
}
}
if (myproc == min_proc) {
shrink = decide_how_to_shrink(global_box, local_box);
if (shrink.first != NONE) {
shrink_axis_val = local_box[shrink.first][shrink.second];
}
}
int grow_info[8] = {grow.first, grow.second,
local_box[X][0], local_box[X][1],
local_box[Y][0], local_box[Y][1],
local_box[Z][0], local_box[Z][1]};
int shrink_info[8] = {shrink.first, shrink.second,
local_box[X][0], local_box[X][1],
local_box[Y][0], local_box[Y][1],
local_box[Z][0], local_box[Z][1]};
#ifdef HAVE_MPI
MPI_Bcast(&grow_info[0], 8, MPI_INT, max_proc, MPI_COMM_WORLD);
MPI_Bcast(&shrink_info[0], 8, MPI_INT, min_proc, MPI_COMM_WORLD);
#endif
int grow_axis = grow_info[0];
int grow_end = grow_info[1];
int shrink_axis = shrink_info[0];
int shrink_end = shrink_info[1];
int grow_incr = 1;
if (grow_end == LOWER) grow_incr = -1;
int shrink_incr = -1;
if (shrink_end == LOWER) shrink_incr = 1;
if (grow_axis != NONE) grow_axis_val = grow_info[2+grow_axis*2+grow_end];
if (shrink_axis != NONE) shrink_axis_val = shrink_info[2+shrink_axis*2+shrink_end];
if (grow_axis == NONE && shrink_axis == NONE) break;
bool grow_status = grow_axis==NONE ? false : true;
if (grow_axis != NONE) {
if ((grow_incr == 1 && local_box[grow_axis][0] == grow_axis_val) ||
(grow_incr == -1 && local_box[grow_axis][1] == grow_axis_val)) {
if (local_box[grow_axis][1] - local_box[grow_axis][0] < 2) {
grow_status = false;
}
}
}
bool shrink_status = shrink_axis==NONE ? false : true;
if (shrink_axis != NONE) {
if ((shrink_incr == 1 && local_box[shrink_axis][0] == shrink_axis_val) ||
(shrink_incr == -1 && local_box[shrink_axis][1] == shrink_axis_val)) {
if (local_box[shrink_axis][1] - local_box[shrink_axis][0] < 2) {
shrink_status = false;
}
}
}
#ifdef HAVE_MPI
int statusints[2] = { grow_status ? 0 : 1, shrink_status ? 0 : 1 };
int globalstatus[2] = { 0, 0 };
MPI_Allreduce(&statusints, &globalstatus, 2, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
grow_status = globalstatus[0]>0 ? false : true;
shrink_status = globalstatus[1]>0 ? false : true;
#endif
if (grow_status == false && shrink_status == false) break;
if (grow_status && grow_axis != NONE) {
if (local_box[grow_axis][0] == grow_axis_val) {
local_box[grow_axis][0] += grow_incr;
}
if (local_box[grow_axis][1] == grow_axis_val) {
local_box[grow_axis][1] += grow_incr;
}
}
if (shrink_status && shrink_axis != NONE) {
if (local_box[shrink_axis][0] == shrink_axis_val) {
local_box[shrink_axis][0] += shrink_incr;
}
if (local_box[shrink_axis][1] == shrink_axis_val) {
local_box[shrink_axis][1] += shrink_incr;
}
}
compute_imbalance<GlobalOrdinal>(global_box, local_box,
cur_imbalance, cur_std_dev, doc, false);
}
}
}//namespace miniFE
#endif