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  int i;
57  map<ElmtOrder, ImplementationType> defaults, defaultsPhysDeriv;
58  bool verbose = (pSession.get()) &&
59  (pSession->DefinesCmdLineArgument("verbose")) &&
60  (pSession->GetComm()->GetRank() == 0);
61 
62  m_setByXml = false;
63  m_autotune = false;
64  m_maxCollSize = 0;
65  m_defaultType = defaultType == eNoImpType ? eIterPerExp : defaultType;
66 
67  map<string, LibUtilities::ShapeType> elTypes;
68  elTypes["S"] = LibUtilities::eSegment;
69  elTypes["T"] = LibUtilities::eTriangle;
70  elTypes["Q"] = LibUtilities::eQuadrilateral;
71  elTypes["A"] = LibUtilities::eTetrahedron;
72  elTypes["P"] = LibUtilities::ePyramid;
73  elTypes["R"] = LibUtilities::ePrism;
74  elTypes["H"] = LibUtilities::eHexahedron;
75 
76  // Set defaults for all element types.
77  for (auto &it2 : elTypes)
78  {
79  defaults [ElmtOrder(it2.second, -1)] = m_defaultType;
80  defaultsPhysDeriv [ElmtOrder(it2.second, -1)] = m_defaultType;
81  }
82 
83  if (defaultType == eNoImpType)
84  {
85  for (auto &it2 : elTypes)
86  {
87  // For 1<=N<=5 use StdMat otherwise IterPerExp or given default type
88  for (int i = 1; i < 5; ++i)
89  {
90  defaults[ElmtOrder(it2.second, i)] = eStdMat;
91  }
92 
93  // For 1<=N<=3 use SumFac otherwise NoCollection. Note that
94  // default is not currently overwritten by given default
95  // type
96  defaultsPhysDeriv [ElmtOrder(it2.second, -1)] = eNoCollection;
97  for (int i = 1; i < 3; ++i)
98  {
99  defaultsPhysDeriv[ElmtOrder(it2.second, i)] = eSumFac;
100  }
101  }
102  }
103 
104  map<string, OperatorType> opTypes;
105  for (i = 0; i < SIZE_OperatorType; ++i)
106  {
107  opTypes[OperatorTypeMap[i]] = (OperatorType)i;
108  switch ((OperatorType)i)
109  {
110  case ePhysDeriv:
111  m_global[(OperatorType)i] = defaultsPhysDeriv;
112  break;
113  default:
114  m_global[(OperatorType)i] = defaults;
115  }
116  }
117 
118  map<string, ImplementationType> impTypes;
119  for (i = 0; i < SIZE_ImplementationType; ++i)
120  {
121  impTypes[ImplementationTypeMap[i]] = (ImplementationType)i;
122  }
123 
124  if(pSession.get()) // turn off file reader if dummy pointer is given
125  {
126  TiXmlDocument &doc = pSession->GetDocument();
127  TiXmlHandle docHandle(&doc);
128  TiXmlElement *master = docHandle.FirstChildElement("NEKTAR").Element();
129  ASSERTL0(master, "Unable to find NEKTAR tag in file.");
130 
131  TiXmlElement *xmlCol = master->FirstChildElement("COLLECTIONS");
132 
133  // Check if user has specified some options
134  if (xmlCol)
135  {
136  // Set the maxsize and default implementation type if provided
137  const char *maxSize = xmlCol->Attribute("MAXSIZE");
138  m_maxCollSize = (maxSize ? atoi(maxSize) : 0);
139 
140  const char *defaultImpl = xmlCol->Attribute("DEFAULT");
141  m_defaultType = defaultType;
142 
143  // If user has specified a default impl type, autotuning
144  // and set this default across all operators.
145  if (defaultType == eNoImpType && defaultImpl)
146  {
147  const std::string collinfo = string(defaultImpl);
148  m_autotune = boost::iequals(collinfo, "auto");
149 
150  if (!m_autotune)
151  {
152  for(i = 1; i < Collections::SIZE_ImplementationType; ++i)
153  {
154  if(boost::iequals(collinfo,
156  {
157  m_defaultType = (Collections::ImplementationType) i;
158  break;
159  }
160  }
161 
162  ASSERTL0(i != Collections::SIZE_ImplementationType,
163  "Unknown default collection scheme: "+collinfo);
164 
165  defaults.clear();
166  // Override default types
167  for (auto &it2 : elTypes)
168  {
169  defaults[ElmtOrder(it2.second, -1)] = m_defaultType;
170  }
171 
172  for (i = 0; i < SIZE_OperatorType; ++i)
173  {
174  m_global[(OperatorType)i] = defaults;
175  }
176  }
177  }
178 
179  // Now process operator-specific implementation selections
180  TiXmlElement *elmt = xmlCol->FirstChildElement();
181  while (elmt)
182  {
183  m_setByXml = true;
184 
185  string tagname = elmt->ValueStr();
186 
187  ASSERTL0(boost::iequals(tagname, "OPERATOR"),
188  "Only OPERATOR tags are supported inside the "
189  "COLLECTIONS tag.");
190 
191  const char *attr = elmt->Attribute("TYPE");
192  ASSERTL0(attr, "Missing TYPE in OPERATOR tag.");
193  string opType(attr);
194 
195  ASSERTL0(opTypes.count(opType) > 0,
196  "Unknown OPERATOR type " + opType + ".");
197 
198  OperatorType ot = opTypes[opType];
199 
200  TiXmlElement *elmt2 = elmt->FirstChildElement();
201 
202  while (elmt2)
203  {
204  string tagname = elmt2->ValueStr();
205  ASSERTL0(boost::iequals(tagname, "ELEMENT"),
206  "Only ELEMENT tags are supported inside the "
207  "OPERATOR tag.");
208 
209  const char *attr = elmt2->Attribute("TYPE");
210  ASSERTL0(attr, "Missing TYPE in ELEMENT tag.");
211 
212  string elType(attr);
213  auto it2 = elTypes.find(elType);
214  ASSERTL0(it2 != elTypes.end(),
215  "Unknown element type "+elType+" in ELEMENT "
216  "tag");
217 
218  const char *attr2 = elmt2->Attribute("IMPTYPE");
219  ASSERTL0(attr2, "Missing IMPTYPE in ELEMENT tag.");
220  string impType(attr2);
221  ASSERTL0(impTypes.count(impType) > 0,
222  "Unknown IMPTYPE type " + impType + ".");
223 
224  const char *attr3 = elmt2->Attribute("ORDER");
225  ASSERTL0(attr3, "Missing ORDER in ELEMENT tag.");
226  string order(attr3);
227 
228  if (order == "*")
229  {
230  m_global[ot][ElmtOrder(it2->second, -1)]
231  = impTypes[impType];
232  }
233  else
234  {
235  vector<unsigned int> orders;
236  ParseUtils::GenerateSeqVector(order, orders);
237 
238  for (int i = 0; i < orders.size(); ++i)
239  {
240  m_global[ot][ElmtOrder(it2->second, orders[i])]
241  = impTypes[impType];
242  }
243  }
244 
245  elmt2 = elmt2->NextSiblingElement();
246  }
247 
248  elmt = elmt->NextSiblingElement();
249  }
250 
251  // Print out operator map
252  if (verbose)
253  {
254  if (!m_setByXml && !m_autotune)
255  {
256  cout << "Setting Collection optimisation using: "
257  << Collections::ImplementationTypeMap[m_defaultType]
258  << endl;
259  }
260 
261  if (m_setByXml)
262  {
263  for (auto &mIt : m_global)
264  {
265  cout << "Operator " << OperatorTypeMap[mIt.first]
266  << ":" << endl;
267 
268  for (auto &eIt : mIt.second)
269  {
270  cout << "- "
271  << LibUtilities::ShapeTypeMap[eIt.first.first]
272  << " order " << eIt.first.second << " -> "
273  << ImplementationTypeMap[eIt.second] << endl;
274  }
275  }
276  }
277  }
278  }
279  }
280 }
281 
282 OperatorImpMap CollectionOptimisation::GetOperatorImpMap(
284 {
285  OperatorImpMap ret;
286  ElmtOrder searchKey(pExp->DetShapeType(),
287  pExp->GetBasisNumModes(0));
288  ElmtOrder defSearch(pExp->DetShapeType(), -1);
289 
290  for (auto &it : m_global)
291  {
292  ImplementationType impType;
293 
294  auto it2 = it.second.find(searchKey);
295 
296  if (it2 == it.second.end())
297  {
298  it2 = it.second.find(defSearch);
299  if (it2 == it.second.end())
300  {
301  // Shouldn't be able to reach here.
302  impType = eNoCollection;
303  }
304  else
305  {
306  impType = it2->second;
307  }
308  }
309  else
310  {
311  impType = it2->second;
312  }
313 
314  ret[it.first] = impType;
315  }
316 
317  return ret;
318 }
319 
320 OperatorImpMap CollectionOptimisation::SetWithTimings(
321  vector<StdRegions::StdExpansionSharedPtr> pCollExp,
322  OperatorImpMap &impTypes,
323  bool verbose )
324 {
325  boost::ignore_unused(impTypes);
326 
327  OperatorImpMap ret;
328 
329  StdRegions::StdExpansionSharedPtr pExp = pCollExp[0];
330 
331  // check to see if already defined for this expansion
332  OpImpTimingKey OpKey(pExp,pCollExp.size(),pExp->GetNumBases());
333  if(m_opImpMap.count(OpKey) != 0)
334  {
335  ret = m_opImpMap[OpKey];
336  return ret;
337  }
338 
339  int maxsize = pCollExp.size()*max(pExp->GetNcoeffs(),pExp->GetTotPoints());
340  Array<OneD, NekDouble> inarray(maxsize,1.0);
341  Array<OneD, NekDouble> outarray1(maxsize);
342  Array<OneD, NekDouble> outarray2(maxsize);
343  Array<OneD, NekDouble> outarray3(maxsize);
344 
346 
347  if(verbose)
348  {
349  cout << "Collection Implemenation for "
350  << LibUtilities::ShapeTypeMap[pExp->DetShapeType()] << " ( ";
351  for(int i = 0; i < pExp->GetNumBases(); ++i)
352  {
353  cout << pExp->GetBasis(i)->GetNumModes() <<" ";
354  }
355  cout << ")" << " for ngeoms = " << pCollExp.size() << endl;
356  }
357  // set up an array of collections
358  CollectionVector coll;
359  for(int imp = 1; imp < SIZE_ImplementationType; ++imp)
360  {
361  ImplementationType impType = (ImplementationType)imp;
362  OperatorImpMap impTypes;
363  for (int i = 0; i < SIZE_OperatorType; ++i)
364  {
365  OperatorType opType = (OperatorType)i;
366  OperatorKey opKey(pCollExp[0]->DetShapeType(), opType, impType,
367  pCollExp[0]->IsNodalNonTensorialExp());
368 
369  if (GetOperatorFactory().ModuleExists(opKey))
370  {
371  impTypes[opType] = impType;
372  }
373  else
374  {
375  cout << "Note: Implementation does not exist: " << opKey << endl;
376  }
377  }
378 
379  Collection collloc(pCollExp,impTypes);
380  coll.push_back(collloc);
381  }
382 
383  // Determine the number of tests to do in one second
385  for(int i = 0; i < SIZE_OperatorType; ++i)
386  {
387  OperatorType OpType = (OperatorType)i;
388 
389  t.Start();
390  coll[0].ApplyOperator(OpType,
391  inarray,
392  outarray1,
393  outarray2,
394  outarray3);
395  t.Stop();
396 
397  NekDouble oneTest = t.TimePerTest(1);
398 
399  Ntest[i] = max((int)(0.25/oneTest),1);
400  }
401 
402  Array<OneD, NekDouble> timing(SIZE_ImplementationType);
403  // loop over all operators and determine fastest implementation
404  for(int i = 0; i < SIZE_OperatorType; ++i)
405  {
406  OperatorType OpType = (OperatorType)i;
407 
408  // call collection implementation in thorugh ExpList.
409  for (int imp = 0; imp < coll.size(); ++imp)
410  {
411  if (coll[imp].HasOperator(OpType))
412  {
413  t.Start();
414  for(int n = 0; n < Ntest[i]; ++n)
415  {
416  coll[imp].ApplyOperator(OpType,
417  inarray,
418  outarray1,
419  outarray2,
420  outarray3);
421  }
422  t.Stop();
423  timing[imp] = t.TimePerTest(Ntest[i]);
424  }
425  else
426  {
427  timing[imp] = 1000.0;
428  }
429  }
430  // determine optimal implementation. Note +1 to
431  // remove NoImplementationType flag
432  int minImp = Vmath::Imin(coll.size(),timing,1)+1;
433 
434  if(verbose)
435  {
436  cout << "\t " << OperatorTypeMap[i] << ": "
437  << ImplementationTypeMap[minImp] << "\t (";
438  for(int j = 0; j < coll.size(); ++j)
439  {
440  if (timing[j] > 999.0)
441  {
442  cout << "-";
443  }
444  else
445  {
446  cout << timing[j] ;
447  }
448  if(j != coll.size()-1)
449  {
450  cout <<", ";
451  }
452  }
453  cout << ")" <<endl;
454  }
455  // could reset global map if reusing method?
456  //m_global[OpType][pExp->DetShapeType()] = (ImplementationType)minImp;
457  // set up new map
458  ret[OpType] = (ImplementationType)minImp;
459  }
460 
461  // store map for use by another expansion.
462  m_opImpMap[OpKey] = ret;
463  return ret;
464 }
465 
466 }
467 }
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
const char *const ImplementationTypeMap[]
Definition: Operator.h:92
NekDouble TimePerTest(unsigned int n)
Returns amount of seconds per iteration in a test with n iterations.
Definition: Timer.cpp:57
std::vector< Collection > CollectionVector
Definition: Collection.h:95
std::tuple< LibUtilities::ShapeType, OperatorType, ImplementationType, ExpansionIsNodal > OperatorKey
Key for describing an Operator.
Definition: Operator.h:161
int Imin(int n, const T *x, const int incx)
Return the index of the minimum element in x.
Definition: Vmath.cpp:850
STL namespace.
const char *const OperatorTypeMap[]
Definition: Operator.h:74
const char *const ShapeTypeMap[]
Definition: ShapeType.hpp:67
std::shared_ptr< StdExpansion > StdExpansionSharedPtr
std::map< OperatorType, ImplementationType > OperatorImpMap
Definition: Operator.h:103
double NekDouble
OperatorFactory & GetOperatorFactory()
Returns the singleton Operator factory object.
Definition: Operator.cpp:108
std::pair< LibUtilities::ShapeType, int > ElmtOrder
std::shared_ptr< SessionReader > SessionReaderSharedPtr