src/FieldLayout/MultiBalancer.cpp

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 /***************************************************************************
00003  *
00004  * The IPPL Framework
00005  * 
00006  * This program was prepared by PSI. 
00007  * All rights in the program are reserved by PSI.
00008  * Neither PSI nor the author(s)
00009  * makes any warranty, express or implied, or assumes any liability or
00010  * responsibility for the use of this software
00011  *
00012  * Visit http://www.acl.lanl.gov/POOMS for more details
00013  *
00014  ***************************************************************************/
00015 
00016 // -*- C++ -*-
00017 /***************************************************************************
00018  *
00019  * The IPPL Framework
00020  * 
00021  *
00022  * Visit http://people.web.psi.ch/adelmann/ for more details
00023  *
00024  ***************************************************************************/
00025 
00026 // include files
00027 #include "FieldLayout/MultiBalancer.h"
00028 #include "Utility/PAssert.h"
00029 
00030 #ifdef IPPL_STDSTL
00031 // Standard STL names this header "algorithm"
00032 #include <algorithm>
00033 using namespace std;
00034 #else
00035 #include <algo.h>
00036 #endif // IPPL_STDSTL
00037 
00039 
00040 //
00041 // Construct a MultiBalancer given:
00042 // The number of processors
00043 // The number of vnodes.
00044 //
00045 // We don't actually allocate space in the arrays here.
00046 //
00047 
00048 MultiBalancer::MultiBalancer(int procs, int vnodes)
00049 : 
00050   // Record the number of procs and vnodes.
00051   m_procs(procs) , 
00052   m_vnodes(vnodes) ,
00053   // The number of materials starts out at zero.
00054   m_materials(0) ,
00055   // Set the size of the container of vnode destination procs.
00056   m_vnodeProcs(vnodes),
00057   // Set the size of the container of processor weights.
00058   m_procWeights(procs),
00059   // It starts out with no materials.
00060   m_phase( noMaterials )
00061 {
00062 }
00063 
00065 
00066 //
00067 // The dtor for MultiBalancer
00068 //
00069 
00070 MultiBalancer::~MultiBalancer()
00071 {
00072   // Delete the nested vectors
00073   for (int i=0; i<m_materials; ++i)
00074     {
00075       delete m_inputWeights[i];
00076       delete m_materialProcWeights[i];
00077     }
00078 }
00079   
00081 
00082 //
00083 // Start recording information for a new material.
00084 //
00085 
00086 void MultiBalancer::newMaterial()
00087 {
00088   // Make sure we're not in the wrong mode.
00089   PAssert( m_phase != distributed );
00090   
00091   // Do a sanity check on the previous material,
00092   // provided that this is not the first one.
00093   if ( m_phase == someMaterials )
00094     // Make sure we got the right number of vnodes.
00095     PAssert( m_inputWeights.back()->size() == m_vnodes );
00096 
00097   // Add a new vector of weights to the materials container.
00098   m_inputWeights.push_back( new VnodeWeights_t );
00099 
00100   // Make it can hold the right number of vnodes.
00101   m_inputWeights.back()->reserve( m_vnodes );
00102 
00103   // Increment the number of materials.
00104   ++m_materials;
00105 
00106   // Set the new mode.
00107   m_phase = someMaterials;
00108 }
00109 
00111 
00112 //
00113 // Record weights from the vnodes on the next processor.
00114 //
00115 
00116 void MultiBalancer::appendWeights(double *begin, double *end)
00117 {
00118   // Make sure we have a nonzero number of materials.
00119   PAssert(m_materials>0);
00120 
00121   // Get a reference to the container where we'll be putting these.
00122   VnodeWeights_t& weights = *m_inputWeights.back();
00123 
00124   // Loop over the inputs, appending to the container.
00125   for (; begin!=end; ++begin)
00126     weights.push_back(*begin);
00127 
00128   // Make sure we didn't make it longer than it should be.
00129   PAssert( weights.size() <= m_vnodes );
00130 }
00131 
00133 
00134 //
00135 // Calculate the redistribution.
00136 //
00137 
00138 void MultiBalancer::distribute()
00139 {
00140   // Make sure we're in the right mode.
00141   PAssert( m_phase != noMaterials );
00142 
00143   // If we've already distributed, we don't need to do anything.
00144   if ( m_phase == distributed )
00145     return;
00146 
00147   int m;
00148 
00149   // Build a vector of the total weight per vnode.
00150   VnodeWeights_t vnodeTotalWeights(m_vnodes,0.0);
00151   calcTotalWeights(vnodeTotalWeights);
00152 
00153   // Build an indirection vector so we can treat the vnodes
00154   // in descending order of total weight.
00155   vector<int> vnodeIndirect(m_vnodes);
00156   calcIndirectSort(vnodeIndirect,vnodeTotalWeights);
00157 
00158   // Keep track of the maximum weight for each material as we go along.
00159   m_materialMaxWeights.resize( m_materials );
00160   for (m=0; m<m_materials; ++m)
00161     m_materialMaxWeights[m] = 0.0;
00162 
00163   // Keep track of the total weight of each material on each processor.
00164   m_materialProcWeights.reserve(m_materials);
00165   for (m=0; m<m_materials; ++m)
00166     m_materialProcWeights.push_back(new VnodeWeights_t(m_procs));
00167 
00168   // Loop over the vnodes in decreasing order of weight,
00169   // finding the processor for each one.
00170 
00171   for (int i=m_vnodes-1; i>=0; --i) 
00172     {
00173       // Which vnode is this?
00174       int vnode = vnodeIndirect[i];
00175 
00176       // Find the processor for which this weight
00177       // minimizes the maximum weight across processors.
00178       int proc = findProcForVnode(vnode);
00179 
00180       // Record that proc.
00181       m_vnodeProcs[vnode] = proc;
00182 
00183       // Record the total weight for that processor.
00184       m_procWeights[proc] += vnodeTotalWeights[vnode];
00185 
00186       // Record the weights for each material.
00187       for (int m=0; m<m_materials; ++m)
00188         {
00189           // Accumulate the weight into the weight for this proc.
00190           m_materialProcWeights[m][0][proc] += m_inputWeights[m][0][vnode];
00191 
00192           // See if this proc is now the maximum weight for this material.
00193           if ( m_materialMaxWeights[m] < m_materialProcWeights[m][0][proc] )
00194             m_materialMaxWeights[m] = m_materialProcWeights[m][0][proc];
00195         }
00196     }
00197 
00198   // Set the new mode.
00199   m_phase = distributed;
00200 }
00201 
00203 
00204 //
00205 // Find the processor we should add this vnode to.
00206 // This is the processor that will minimize the increase 
00207 // in the total weight. 
00208 //
00209 
00210 int MultiBalancer::findProcForVnode( int v )
00211 {
00212   // The minimum of the weights on a processer when this vnode is included.
00213   double minWeight = 1e30;
00214 
00215   // Which proc has the minimum weight when this vnode is included.
00216   int minProc = -1;
00217 
00218   // Try putting this vnode on each processor in turn.
00219   for (int p = 0; p<m_procs; ++p) 
00220     {
00221       // Find the estimated total weight if this vnode is put on this proc.
00222       double totalWeight = 0.0;
00223       for (int m=0; m<m_materials; ++m)
00224         {
00225           // Add the weight from this material on this vnode
00226           // to the weight from this material already on this proc.
00227           double w = m_materialProcWeights[m][0][p]+m_inputWeights[m][0][v];
00228 
00229           // If the weight is less than the maximum weight on other
00230           // procs, then the other proc determines the weight.
00231           if ( w < m_materialMaxWeights[m] )
00232             w = m_materialMaxWeights[m];
00233 
00234           // Accumulate the weight into the total.
00235           totalWeight += w;
00236         }
00237 
00238       // If the new weight for this proc is less then the previous
00239       // least weight, then this is the new best candidate.
00240       if ( totalWeight < minWeight )
00241         {
00242           minWeight = totalWeight;
00243           minProc = p;
00244         }
00245     }
00246 
00247   // Return the best candidate processor found.
00248   // We had better have found something.
00249   PAssert(minProc>=0);
00250   return minProc;
00251 }
00252 
00254 
00255 //
00256 // Calculate the total weight per vnode.
00257 //
00258 void 
00259 MultiBalancer::calcTotalWeights(VnodeWeights_t& vnodeTotalWeights)
00260 {
00261   MaterialVnodeWeights_t::iterator matp=m_inputWeights.begin();
00262   for (; matp != m_inputWeights.end(); ++matp)
00263     {
00264       // Do a sanity check.
00265       // Make sure that all of the input materials have the same
00266       // number of vnodes.
00267       PAssert( (*matp)->size() == m_vnodes );
00268 
00269       // Accumulate the weight of each vnode into the 
00270       // array of total weights.
00271       for (int i = 0; i<m_vnodes; ++i)
00272         vnodeTotalWeights[i] += (**matp)[i];
00273     }
00274 }
00275 
00277 
00278 //
00279 // Calculate a list of integers we can use to 
00280 // access the vnodes in sorted order.
00281 //
00282 void
00283 MultiBalancer::calcIndirectSort(vector<int>& vnodeIndirect,
00284                                 const VnodeWeights_t& vnodeTotalWeights)
00285 {
00286   // Initialize the indirection list with increasing integers.
00287   for (int i=0; i<m_vnodes; ++i)
00288     vnodeIndirect[i] = i;
00289 
00290   // Sort the indirection list using a comparator
00291   // which looks up values in the weight array.
00292   sort(
00293     vnodeIndirect.begin(), 
00294     vnodeIndirect.end(), 
00295     IndirectComparator(vnodeTotalWeights.begin()));
00296 }
00297 
00298 
00299 /***************************************************************************
00300  * $RCSfile: MultiBalancer.cpp,v $   $Author: adelmann $
00301  * $Revision: 1.1.1.1 $   $Date: 2003/01/23 07:40:27 $
00302  * IPPL_VERSION_ID: $Id: MultiBalancer.cpp,v 1.1.1.1 2003/01/23 07:40:27 adelmann Exp $ 
00303  ***************************************************************************/

Generated on Mon Jan 16 13:23:47 2006 for IPPL by  doxygen 1.4.6