Nektar++
CollectionOptimisation.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File: CollectionOptimisation.cpp
4 //
5 // For more information, please see: http://www.nektar.info
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description: Collection top class definition
32 //
33 ///////////////////////////////////////////////////////////////////////////////
34 
35 #include <boost/core/ignore_unused.hpp>
36 #include <boost/algorithm/string/predicate.hpp>
37 
41 
42 using namespace std;
43 
44 namespace Nektar
45 {
46 namespace Collections
47 {
48 
49 // static manager for Operator ImplementationMap
50 map<OpImpTimingKey,OperatorImpMap> CollectionOptimisation::m_opImpMap;
51 
52 CollectionOptimisation::CollectionOptimisation(
54  ImplementationType defaultType)
55 {
56  map<ElmtOrder, ImplementationType> defaults, defaultsPhysDeriv;
57  bool verbose = (pSession.get()) &&
58  (pSession->DefinesCmdLineArgument("verbose")) &&
59  (pSession->GetComm()->GetRank() == 0);
60 
61  m_setByXml = false;
62  m_autotune = false;
63  m_maxCollSize = 0;
64  m_defaultType = defaultType == eNoImpType ? eIterPerExp : defaultType;
65 
66  map<string, LibUtilities::ShapeType> elTypes;
67  elTypes["S"] = LibUtilities::eSegment;
68  elTypes["T"] = LibUtilities::eTriangle;
69  elTypes["Q"] = LibUtilities::eQuadrilateral;
70  elTypes["A"] = LibUtilities::eTetrahedron;
71  elTypes["P"] = LibUtilities::ePyramid;
72  elTypes["R"] = LibUtilities::ePrism;
73  elTypes["H"] = LibUtilities::eHexahedron;
74 
75  // Set defaults for all element types.
76  for (auto &it2 : elTypes)
77  {
78  defaults [ElmtOrder(it2.second, -1)] = m_defaultType;
79  defaultsPhysDeriv [ElmtOrder(it2.second, -1)] = m_defaultType;
80  }
81 
82  if (defaultType == eNoImpType)
83  {
84  for (auto &it2 : elTypes)
85  {
86  // For 1<=N<=5 use StdMat otherwise IterPerExp or given default type
87  for (int i = 1; i < 5; ++i)
88  {
89  defaults[ElmtOrder(it2.second, i)] = eStdMat;
90  }
91 
92  // For 1<=N<=3 use SumFac otherwise NoCollection. Note that
93  // default is not currently overwritten by given default
94  // type
95  defaultsPhysDeriv [ElmtOrder(it2.second, -1)] = eNoCollection;
96  for (int i = 1; i < 3; ++i)
97  {
98  defaultsPhysDeriv[ElmtOrder(it2.second, i)] = eSumFac;
99  }
100  }
101  }
102 
103  map<string, OperatorType> opTypes;
104  for (int i = 0; i < SIZE_OperatorType; ++i)
105  {
106  opTypes[OperatorTypeMap[i]] = (OperatorType)i;
107  switch ((OperatorType)i)
108  {
109  case ePhysDeriv:
110  m_global[(OperatorType)i] = defaultsPhysDeriv;
111  break;
112  default:
113  m_global[(OperatorType)i] = defaults;
114  }
115  }
116 
117  map<string, ImplementationType> impTypes;
118  for (int i = 0; i < SIZE_ImplementationType; ++i)
119  {
120  impTypes[ImplementationTypeMap[i]] = (ImplementationType)i;
121  }
122 
123  if(pSession.get()) // turn off file reader if dummy pointer is given
124  {
125  TiXmlDocument &doc = pSession->GetDocument();
126  TiXmlHandle docHandle(&doc);
127  TiXmlElement *master = docHandle.FirstChildElement("NEKTAR").Element();
128  ASSERTL0(master, "Unable to find NEKTAR tag in file.");
129 
130  TiXmlElement *xmlCol = master->FirstChildElement("COLLECTIONS");
131 
132  // Check if user has specified some options
133  if (xmlCol)
134  {
135  // Set the maxsize and default implementation type if provided
136  const char *maxSize = xmlCol->Attribute("MAXSIZE");
137  m_maxCollSize = (maxSize ? atoi(maxSize) : 0);
138 
139  const char *defaultImpl = xmlCol->Attribute("DEFAULT");
140  m_defaultType = defaultType;
141 
142  // If user has specified a default impl type, autotuning
143  // and set this default across all operators.
144  if (defaultType == eNoImpType && defaultImpl)
145  {
146  const std::string collinfo = string(defaultImpl);
147  m_autotune = boost::iequals(collinfo, "auto");
148 
149  if (!m_autotune)
150  {
151  bool collectionFound{false};
152  for(int i = 1; i < Collections::SIZE_ImplementationType; ++i)
153  {
154  if(boost::iequals(collinfo,
156  {
157  m_defaultType = (Collections::ImplementationType) i;
158  collectionFound = true;
159  break;
160  }
161  }
162 
163  ASSERTL0(collectionFound,
164  "Unknown default collection scheme: "+collinfo);
165 
166  defaults.clear();
167  // Override default types
168  for (auto &it2 : elTypes)
169  {
170  defaults[ElmtOrder(it2.second, -1)] = m_defaultType;
171  }
172 
173  for (int i = 0; i < SIZE_OperatorType; ++i)
174  {
175  m_global[(OperatorType)i] = defaults;
176  }
177  }
178  }
179 
180  // Now process operator-specific implementation selections
181  TiXmlElement *elmt = xmlCol->FirstChildElement();
182  while (elmt)
183  {
184  m_setByXml = true;
185 
186  string tagname = elmt->ValueStr();
187 
188  ASSERTL0(boost::iequals(tagname, "OPERATOR"),
189  "Only OPERATOR tags are supported inside the "
190  "COLLECTIONS tag.");
191 
192  const char *attr = elmt->Attribute("TYPE");
193  ASSERTL0(attr, "Missing TYPE in OPERATOR tag.");
194  string opType(attr);
195 
196  ASSERTL0(opTypes.count(opType) > 0,
197  "Unknown OPERATOR type " + opType + ".");
198 
199  OperatorType ot = opTypes[opType];
200 
201  TiXmlElement *elmt2 = elmt->FirstChildElement();
202 
203  while (elmt2)
204  {
205  string tagname = elmt2->ValueStr();
206  ASSERTL0(boost::iequals(tagname, "ELEMENT"),
207  "Only ELEMENT tags are supported inside the "
208  "OPERATOR tag.");
209 
210  const char *attr = elmt2->Attribute("TYPE");
211  ASSERTL0(attr, "Missing TYPE in ELEMENT tag.");
212 
213  string elType(attr);
214  auto it2 = elTypes.find(elType);
215  ASSERTL0(it2 != elTypes.end(),
216  "Unknown element type "+elType+" in ELEMENT "
217  "tag");
218 
219  const char *attr2 = elmt2->Attribute("IMPTYPE");
220  ASSERTL0(attr2, "Missing IMPTYPE in ELEMENT tag.");
221  string impType(attr2);
222  ASSERTL0(impTypes.count(impType) > 0,
223  "Unknown IMPTYPE type " + impType + ".");
224 
225  const char *attr3 = elmt2->Attribute("ORDER");
226  ASSERTL0(attr3, "Missing ORDER in ELEMENT tag.");
227  string order(attr3);
228 
229  if (order == "*")
230  {
231  m_global[ot][ElmtOrder(it2->second, -1)]
232  = impTypes[impType];
233  }
234  else
235  {
236  vector<unsigned int> orders;
237  ParseUtils::GenerateSeqVector(order, orders);
238 
239  for (int i = 0; i < orders.size(); ++i)
240  {
241  m_global[ot][ElmtOrder(it2->second, orders[i])]
242  = impTypes[impType];
243  }
244  }
245 
246  elmt2 = elmt2->NextSiblingElement();
247  }
248 
249  elmt = elmt->NextSiblingElement();
250  }
251 
252  // Print out operator map
253  if (verbose)
254  {
255  if (!m_setByXml && !m_autotune)
256  {
257  cout << "Setting Collection optimisation using: "
258  << Collections::ImplementationTypeMap[m_defaultType]
259  << endl;
260  }
261 
262  if (m_setByXml)
263  {
264  for (auto &mIt : m_global)
265  {
266  cout << "Operator " << OperatorTypeMap[mIt.first]
267  << ":" << endl;
268 
269  for (auto &eIt : mIt.second)
270  {
271  cout << "- "
272  << LibUtilities::ShapeTypeMap[eIt.first.first]
273  << " order " << eIt.first.second << " -> "
274  << ImplementationTypeMap[eIt.second] << endl;
275  }
276  }
277  }
278  }
279  }
280  }
281 }
282 
283 OperatorImpMap CollectionOptimisation::GetOperatorImpMap(
285 {
286  OperatorImpMap ret;
287  ElmtOrder searchKey(pExp->DetShapeType(),
288  pExp->GetBasisNumModes(0));
289  ElmtOrder defSearch(pExp->DetShapeType(), -1);
290 
291  for (auto &it : m_global)
292  {
293  ImplementationType impType;
294 
295  auto it2 = it.second.find(searchKey);
296 
297  if (it2 == it.second.end())
298  {
299  it2 = it.second.find(defSearch);
300  if (it2 == it.second.end())
301  {
302  // Shouldn't be able to reach here.
303  impType = eNoCollection;
304  }
305  else
306  {
307  impType = it2->second;
308  }
309  }
310  else
311  {
312  impType = it2->second;
313  }
314 
315  ret[it.first] = impType;
316  }
317 
318  return ret;
319 }
320 
321 OperatorImpMap CollectionOptimisation::SetWithTimings(
322  vector<StdRegions::StdExpansionSharedPtr> pCollExp,
323  OperatorImpMap &impTypes,
324  bool verbose )
325 {
326  boost::ignore_unused(impTypes);
327 
328  OperatorImpMap ret;
329 
330  StdRegions::StdExpansionSharedPtr pExp = pCollExp[0];
331 
332  // check to see if already defined for this expansion
333  OpImpTimingKey OpKey(pExp,pCollExp.size(),pExp->GetNumBases());
334  if(m_opImpMap.count(OpKey) != 0)
335  {
336  ret = m_opImpMap[OpKey];
337  return ret;
338  }
339 
340  int maxsize = pCollExp.size()*max(pExp->GetNcoeffs(),pExp->GetTotPoints());
341  Array<OneD, NekDouble> inarray(maxsize,1.0);
342  Array<OneD, NekDouble> outarray1(maxsize);
343  Array<OneD, NekDouble> outarray2(maxsize);
344  Array<OneD, NekDouble> outarray3(maxsize);
345 
347 
348  if(verbose)
349  {
350  cout << "Collection Implemenation for "
351  << LibUtilities::ShapeTypeMap[pExp->DetShapeType()] << " ( ";
352  for(int i = 0; i < pExp->GetNumBases(); ++i)
353  {
354  cout << pExp->GetBasis(i)->GetNumModes() <<" ";
355  }
356  cout << ")" << " for ngeoms = " << pCollExp.size() << endl;
357  }
358  // set up an array of collections
359  CollectionVector coll;
360 
361  StdRegions::ConstFactorMap factors; // required for helmholtz operator
362  factors[StdRegions::eFactorLambda] = 1.5;
363 
364  for(int imp = 1; imp < SIZE_ImplementationType; ++imp)
365  {
366  ImplementationType impType = (ImplementationType)imp;
367  OperatorImpMap impTypes;
368  for (int i = 0; i < SIZE_OperatorType; ++i)
369  {
370  OperatorType opType = (OperatorType)i;
371  OperatorKey opKey(pCollExp[0]->DetShapeType(), opType, impType,
372  pCollExp[0]->IsNodalNonTensorialExp());
373 
374  if (GetOperatorFactory().ModuleExists(opKey))
375  {
376  impTypes[opType] = impType;
377  }
378  else
379  {
380  cout << "Note: Implementation does not exist: " << opKey << endl;
381  }
382  }
383 
384  Collection collLoc(pCollExp,impTypes);
385  for (int i = 0; i < SIZE_OperatorType; ++i)
386  {
387  collLoc.Initialise((OperatorType)i, factors);
388  }
389  coll.push_back(collLoc);
390  }
391 
392  // Determine the number of tests to do in one second
394  for(int i = 0; i < SIZE_OperatorType; ++i)
395  {
396  OperatorType OpType = (OperatorType)i;
397 
398  t.Start();
399 
400  coll[0].ApplyOperator(OpType, inarray,
401  outarray1, outarray2,
402  outarray3);
403  t.Stop();
404 
405  NekDouble oneTest = t.TimePerTest(1);
406 
407  Ntest[i] = max((int)(0.25/oneTest),1);
408  }
409 
411  // loop over all operators and determine fastest implementation
412  for(int i = 0; i < SIZE_OperatorType; ++i)
413  {
414  OperatorType OpType = (OperatorType)i;
415 
416  // call collection implementation in thorugh ExpList.
417  for (int imp = 0; imp < coll.size(); ++imp)
418  {
419  if (coll[imp].HasOperator(OpType))
420  {
421  t.Start();
422  for(int n = 0; n < Ntest[i]; ++n)
423  {
424  coll[imp].ApplyOperator(OpType, inarray,
425  outarray1, outarray2,
426  outarray3);
427  }
428  t.Stop();
429  timing[imp] = t.TimePerTest(Ntest[i]);
430  }
431  else
432  {
433  timing[imp] = 1000.0;
434  }
435  }
436  // determine optimal implementation. Note +1 to
437  // remove NoImplementationType flag
438  int minImp = Vmath::Imin(coll.size(),timing,1)+1;
439 
440  if(verbose)
441  {
442  cout << "\t " << OperatorTypeMap[i] << ": "
443  << ImplementationTypeMap[minImp] << "\t (";
444  for(int j = 0; j < coll.size(); ++j)
445  {
446  if (timing[j] > 999.0)
447  {
448  cout << "-";
449  }
450  else
451  {
452  cout << timing[j] ;
453  }
454  if(j != coll.size()-1)
455  {
456  cout <<", ";
457  }
458  }
459  cout << ")" <<endl;
460  }
461  // could reset global map if reusing method?
462  //m_global[OpType][pExp->DetShapeType()] = (ImplementationType)minImp;
463  // set up new map
464  ret[OpType] = (ImplementationType)minImp;
465  }
466 
467  // store map for use by another expansion.
468  m_opImpMap[OpKey] = ret;
469  return ret;
470 }
471 
472 }
473 }
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
COLLECTIONS_EXPORT void Initialise(const OperatorType opType, StdRegions::FactorMap factors=StdRegions::NullFactorMap)
Definition: Collection.cpp:64
std::pair< LibUtilities::ShapeType, int > ElmtOrder
NekDouble TimePerTest(unsigned int n)
Returns amount of seconds per iteration in a test with n iterations.
Definition: Timer.cpp:62
std::map< OperatorType, ImplementationType > OperatorImpMap
Definition: Operator.h:108
const char *const ImplementationTypeMap[]
Definition: Operator.h:96
const char *const OperatorTypeMap[]
Definition: Operator.h:76
std::tuple< LibUtilities::ShapeType, OperatorType, ImplementationType, ExpansionIsNodal > OperatorKey
Key for describing an Operator.
Definition: Operator.h:181
OperatorFactory & GetOperatorFactory()
Returns the singleton Operator factory object.
Definition: Operator.cpp:121
std::vector< Collection > CollectionVector
Definition: Collection.h:114
std::shared_ptr< SessionReader > SessionReaderSharedPtr
const char *const ShapeTypeMap[]
Definition: ShapeType.hpp:67
std::shared_ptr< StdExpansion > StdExpansionSharedPtr
std::map< ConstFactorType, NekDouble > ConstFactorMap
Definition: StdRegions.hpp:314
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1
double NekDouble
int Imin(int n, const T *x, const int incx)
Return the index of the minimum element in x.
Definition: Vmath.cpp:966