OPAL (Object Oriented Parallel Accelerator Library) 2022.1
OPAL
LField.hpp
Go to the documentation of this file.
1//
2// Class LField
3// Local Field class
4//
5// Copyright (c) 2003 - 2020, Paul Scherrer Institut, Villigen PSI, Switzerland
6// All rights reserved
7//
8// This file is part of OPAL.
9//
10// OPAL is free software: you can redistribute it and/or modify
11// it under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// You should have received a copy of the GNU General Public License
16// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
17//
18#include "Field/LField.h"
19
20#include "Utility/PAssert.h"
21#include "Utility/IpplStats.h"
22#include "Utility/Unique.h"
23#include <cstdlib>
24
25// the number of bytes in a single cache line, this is generally set
26// by configuration options, but gets a default value if none is given
27#ifndef IPPL_CACHE_LINE_SIZE
28#define IPPL_CACHE_LINE_SIZE 32
29#endif
30
31// the number of "offset blocks" to use. We will add a small offset
32// to the beginning of where in each malloced storage block the LField
33// data is stored, to try to avoid having several blocks all map to
34// the same cache line. This is the maximum number of blocks that we
35// will add as an offset, where each block is the size of a cache line.
36#ifndef IPPL_OFFSET_BLOCKS
37#define IPPL_OFFSET_BLOCKS 16
38#endif
39
40// a debugging output message macro
41#ifdef DEBUG_LFIELD
42#define LFIELDMSG(x) x
43#else
44#define LFIELDMSG(x)
45#endif
46
47
49//
50// Initialize numeric types to zero.
51// Everything else uses the default ctor.
52//
54
55template<class T>
57{
58 static void apply(T&) {}
59};
60
61#define MAKE_INITIALIZER(T) \
62template <> \
63struct LFieldInitializer<T> \
64{ \
65 static void apply(T& x) { x=0; } \
66};
67
76
78//
79// Construct given the sizes.
80// This builds it compressed.
81//
83
84template<class T, unsigned Dim>
85LField<T,Dim>::LField(const NDIndex<Dim>& owned,
86 const NDIndex<Dim>& allocated,
87 int vnode)
88: vnode_m(vnode),
89 P(0),
90 Pinned(false),
91 Owned(owned),
92 Allocated(allocated),
93 Begin(owned, CompressedData),
94 End(CompressedData),
95 overlapCacheInited(false),
96 allocCompressIndex(0),
97 ownedCompressIndex(-1),
98 offsetBlocks(Unique::get() % IPPL_OFFSET_BLOCKS)
99{
100
101 // Give the LField some initial (compressed) value
103
104 // If we are not actually doing compression, expand the storage out,
105 // and copy the initial value to all the elements
107 this->ReallyUncompress(true);
108
109 //INCIPPLSTAT(incLFields);
110}
111
112//UL: for pinned mempory allocation
113template<class T, unsigned Dim>
115 const NDIndex<Dim>& allocated,
116 int vnode, bool p)
117 : vnode_m(vnode),
118 P(0),
119 Pinned(p),
120 Owned(owned),
121 Allocated(allocated),
122 Begin(owned, CompressedData),
123 End(CompressedData),
124 overlapCacheInited(false),
125 allocCompressIndex(0),
126 ownedCompressIndex(-1),
127 offsetBlocks(Unique::get() % IPPL_OFFSET_BLOCKS)
128{
129
130 // Give the LField some initial (compressed) value
132
133 // If we are not actually doing compression, expand the storage out,
134 // and copy the initial value to all the elements
136 this->ReallyUncompress(true);
137
138 //INCIPPLSTAT(incLFields);
139}
140
142//
143// Deep copy constructor.
144//
146
147template<class T, unsigned Dim>
149 : vnode_m(lf.vnode_m),
150 P(0),
151 Pinned(false),
152 Owned(lf.Owned),
153 Allocated(lf.Allocated),
154 Begin(CompressedData),
155 End(CompressedData),
156 overlapCacheInited(false),
157 allocCompressIndex(lf.allocCompressIndex),
158 ownedCompressIndex(lf.ownedCompressIndex),
159 offsetBlocks(Unique::get() % IPPL_OFFSET_BLOCKS)
160{
161
162
163
164 if ( lf.IsCompressed() )
165 {
166 // Build a compressed iterator.
168
169 // get the constant value in lf.
171 }
172 else
173 {
174 // Make sure we have something in this LField
175 PAssert_NE(lf.Allocated.size(), 0);
176
177 // If it is not compressed, allocate storage
178 int n = lf.Allocated.size();
180
181 // Copy the data over.
182 std::copy(lf.P, lf.P + n, P);
183
184 // Build an iterator that counts over the real data.
186 }
187
188 //INCIPPLSTAT(incLFields);
189}
190
191
193//
194// Destructor: just free the memory, if it's there.
195//
197
198template<class T, unsigned Dim>
200{
201 deallocateStorage();
202}
203
204
206//
207// Let the user tell us to try to compress.
208// Return quickly if we already are compressed.
209//
211
212template<class T, unsigned Dim>
213bool
214LField<T,Dim>::TryCompress(bool baseOnPhysicalCells)
215{
216
217
218
220 return false;
221
222 LFIELDMSG(Inform dbgmsg("LField::TryCompress", INFORM_ALL_NODES));
223 LFIELDMSG(dbgmsg << "Trying to compress LField with domain = "<<getOwned());
224 LFIELDMSG(dbgmsg << ", baseOnPhysicalCells = " << baseOnPhysicalCells<<endl);
225
226 if (baseOnPhysicalCells)
227 {
228 if (CanCompressBasedOnPhysicalCells())
229 {
230 CompressBasedOnPhysicalCells();
231 return true;
232 }
233 }
234 else
235 {
236 if (CanCompress() )
237 {
238 Compress();
239 return true;
240 }
241 }
242
243 return false;
244}
245
246
248//
249// Look through the data and figure out if it can be compressed
250// to the given value.
251//
253
254template<class T, unsigned Dim>
255bool
257{
258
259
260
261 // Debugging macro
262 LFIELDMSG(Inform dbgmsg("CanCompress"));
263
264 // We definitely can't do this if compression is disabled.
266 return false;
267
268 // If it is already compressed, we can compress it to any value.
269 if (IsCompressed())
270 //return *Begin == val;
271 return true;
272
273 // It is not currently compressed ... so go through and check
274 // to see if all the elements are the same as the given argument.
275
276 int sz = getAllocated().size();
277 ADDIPPLSTAT(incCompressionCompareMax, sz);
278 T *ptr1 = P;
279 T *mid1 = P + allocCompressIndex;
280 T *end1 = P + sz;
281
282 PAssert_GT(sz, 0);
283 PAssert(P != 0);
284 PAssert_GE(allocCompressIndex, 0);
285 PAssert_LT(allocCompressIndex, sz);
286
287 // Quick short-cut check: compare to the last value in the
288 // array that did not match before.
289
291 {
292 LFIELDMSG(dbgmsg << "Doing short-cut check, comparing " << *mid1);
293 LFIELDMSG(dbgmsg << " to " << val << " at last-alloc-domain-failed");
294 LFIELDMSG(dbgmsg << " index of " << allocCompressIndex << endl);
295 ADDIPPLSTAT(incCompressionCompares, 1);
296
297 if (!(*mid1 == val))
298 {
299 LFIELDMSG(dbgmsg << "Short-cut check determined we cannot ");
300 LFIELDMSG(dbgmsg << "compress, by comparing " << *mid1<<" to ");
301 LFIELDMSG(dbgmsg << val << " at last-alloc-domain-failed index");
302 LFIELDMSG(dbgmsg << " of " << allocCompressIndex << endl);
303
304 // It failed the test, so we can just keep the same index to
305 // check next time, and return.
306 return false;
307 }
308 }
309
310 // Check from the beginning to the last-checked-index
311
312 LFIELDMSG(dbgmsg << "Checking for compression for " << sz << " items, ");
313 LFIELDMSG(dbgmsg << "comparing to value = " << val << endl);
314
316 {
317 // First check from last-failed-position to end, since we've
318 // already looked at *mid1 and should have that section of memory
319 // in cache
320 T *checkptr = mid1 + 1;
321 while (checkptr != end1)
322 {
323 if (!(*checkptr++ == val))
324 {
325 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
326 LFIELDMSG(dbgmsg << (checkptr - mid1) << " compares (");
327 LFIELDMSG(dbgmsg << *(checkptr-1) << " != " << val << ")");
328 LFIELDMSG(dbgmsg << endl);
329 ADDIPPLSTAT(incCompressionCompares, (checkptr - mid1));
330 allocCompressIndex = (checkptr - ptr1) - 1;
331 return false;
332 }
333 }
334
335 // Next, check from the first position to the last-failed-position.
336 checkptr = ptr1;
337 while (checkptr != mid1)
338 {
339 if (!(*checkptr++ == val))
340 {
341 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
342 LFIELDMSG(dbgmsg << (checkptr - ptr1) + (end1 - mid1));
343 LFIELDMSG(dbgmsg << " compares (");
344 LFIELDMSG(dbgmsg << *(checkptr-1) << " != " << val << ")");
345 LFIELDMSG(dbgmsg << endl);
346 ADDIPPLSTAT(incCompressionCompares,
347 (checkptr - ptr1) + (end1 - mid1));
348 allocCompressIndex = (checkptr - ptr1) - 1;
349 return false;
350 }
351 }
352 }
353 else
354 {
355 while (ptr1 != end1)
356 {
357 if (!(*ptr1++ == val))
358 {
359 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
360 LFIELDMSG(dbgmsg << (ptr1 - P) << " compares (");
361 LFIELDMSG(dbgmsg << *(ptr1-1) << " != " << val << ")");
362 LFIELDMSG(dbgmsg << endl);
363 ADDIPPLSTAT(incCompressionCompares, (ptr1 - P));
364 allocCompressIndex = (ptr1 - P) - 1;
365 return false;
366 }
367 }
368 }
369
370 // If we are at this point, we did not find anything that did not
371 // match, so we can compress (woo hoo).
372
373 LFIELDMSG(dbgmsg << "Found that we CAN compress, after " << sz);
374 LFIELDMSG(dbgmsg << " compares." << endl);
375 ADDIPPLSTAT(incCompressionCompares, sz);
376 allocCompressIndex = 0;
377 return true;
378}
379
380
382//
383// Return true if this LField can be compressed based on physical
384// cells only and false if it could not.
385//
387
388template<class T, unsigned Dim>
390{
391
392
393
394 // Debugging macro
395
396 LFIELDMSG(Inform dbgmsg("LField::CanCompressBasedOnPhysicalCells",
398
399 // We definitely can't do this if compression is disabled.
401 return false;
402
403 // If it is already compressed, we can compress it to any value.
404 if (IsCompressed())
405 return true;
406
407 // Make an iterator over my owned domain. The cast is there because
408 // this version of begin() is not a const member function.
409
410 iterator p = const_cast<LField<T,Dim>*>(this)->begin(getOwned());
411
412 // Get the value to compare against, either the first item or
413 // an item from the last point where our compression check failed.
414
415 T val = *p;
416 int sz = getOwned().size();
417 if (IpplInfo::extraCompressChecks && ownedCompressIndex > 0)
418 {
419 // There was a previous value, so get that one to compare against
420 PAssert_LT((unsigned int) ownedCompressIndex, getAllocated().size());
421 val = *(P + ownedCompressIndex);
422 LFIELDMSG(dbgmsg << "Checking owned cells using previous ");
423 LFIELDMSG(dbgmsg << "comparison value " << val << " from index = ");
424 LFIELDMSG(dbgmsg << ownedCompressIndex << " against " << sz);
425 LFIELDMSG(dbgmsg << " elements." << endl);
426 }
427 else
428 {
429 // We just use the first element, and will compare against
430 // the rest, so we know we can skip comparing to this first element.
431 ++p;
432 --sz;
433 LFIELDMSG(dbgmsg << "Checking owned cells using first element " << val);
434 LFIELDMSG(dbgmsg << " for comparison against " << sz << " items."<<endl);
435 }
436
437 // Loop through the other physical cells until we encounter one that
438 // doesn't match the 1st cell. If this occurs, we can't compress.
439
440 ADDIPPLSTAT(incCompressionCompareMax, sz - 1);
441 for (int i=0; i < sz; ++i, ++p)
442 {
443 if (!(*p == val))
444 {
445 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
446 LFIELDMSG(dbgmsg << i + 1 << " compares." << endl);
447 ADDIPPLSTAT(incCompressionCompares, i + 1);
448 ownedCompressIndex = (&(*p)) - P;
449 LFIELDMSG(dbgmsg << "changed ownedCompressIndex to ");
450 LFIELDMSG(dbgmsg << ownedCompressIndex << endl);
451 return false;
452 }
453 }
454
455 // Since we made it here, we can compress.
456
457 LFIELDMSG(dbgmsg << "Found that we CAN compress, after ");
458 LFIELDMSG(dbgmsg << sz << " compares." << endl);
459 ADDIPPLSTAT(incCompressionCompares, sz);
460 ownedCompressIndex = (-1);
461 return true;
462}
463
464
466//
467// Force a compression to a specified value. This version compresses
468// the entire allocated domain. If this is called when compression
469// is turned off, it instead copies the given value into the whole
470// domain's storage, so that it at least makes the whole domain
471// equal to the value.
472//
474
475template<class T, unsigned Dim>
476void
478{
479
480
481
482 LFIELDMSG(Inform dbgmsg("LField::Compress", INFORM_ALL_NODES));
483 LFIELDMSG(dbgmsg << "Compressing LField with domain = " << getOwned());
484 LFIELDMSG(dbgmsg << " to new value = " << val << ", already compressed = ");
485 LFIELDMSG(dbgmsg << (IsCompressed() ? 1 : 0) << endl);
486
487 // When compression is disabled, interpret this to mean "assign every element
488 // of the LField to the specified value," which is equivalent to compressing
489 // the LField to the value then uncompressing it:
490
492 {
493 for (iterator lit = begin(); lit != end(); ++lit)
494 *lit = val;
495
496 return;
497 }
498
499 // Compression is enabled if we're here, so save the compressed value and
500 // free up memory if necessary. We copy the value into the compressed
501 // value storage, and then if we're currently compressed, we free up
502 // that memory and update our iterators.
503
504 CompressedData = val;
505 if (!IsCompressed())
506 {
507 Begin.Compress(CompressedData);
508 deallocateStorage();
509 }
510
511 //INCIPPLSTAT(incCompresses);
512}
513
514
516//
517// This function does a compressed based on physical cells only.
518// It will compress to the value of the first element in the owned
519// domain (instead of in the allocated domain). If compression is
520// turned off, this does nothing, it does not even attempt to fill
521// in the owned domain with a value.
522//
524
525template<class T, unsigned Dim>
526void
528{
529
530
531
532 // We do nothing in this case if compression is turned off.
533
535 return;
536
537 // Set compression value to first element in owned domain, and free up
538 // memory if necessary.
539
540 CompressedData = *(begin(getOwned()));
541 if (!IsCompressed())
542 {
543 Begin.Compress(CompressedData);
544 deallocateStorage();
545 }
546
547 //INCIPPLSTAT(incCompresses);
548}
549
550
552//
553// We know this is compressed, so uncompress it.
554//
556
557template<class T, unsigned Dim>
559{
560
561
562
563 PAssert_NE(Allocated.size(), 0);
564
565 // Allocate the data.
566
567 int n = Allocated.size();
568 allocateStorage(n);
569
570 LFIELDMSG(Inform dbgmsg("LField::ReallyUncompress", INFORM_ALL_NODES));
571 LFIELDMSG(dbgmsg << "Uncompressing LField with domain = " << getOwned());
572 LFIELDMSG(dbgmsg << ", fill_domain = " << (fill_domain ? 1 : 0) << endl);
573
574 // Copy the constant value into the new space.
575
576 if (fill_domain)
577 {
578 T val = *Begin;
579 for (int i=0; i<n; i++)
580 P[i] = val;
581 }
582
583 // Make the Begin iterator point to the new data.
584
585 Begin = iterator(P,Owned,Allocated,CompressedData);
586
587 // Indicate we've done one more decompress
588
589 //INCIPPLSTAT(incDecompresses);
590}
591
592
594//
595// get an iterator over a subrange.
596//
598
599template<class T, unsigned Dim>
602{
603 // Remove this profiling because this is too lightweight.
604 //
605 //
606 return iterator(P,domain,Allocated,CompressedData);
607}
608
609
611//
612// Get an iterator over a subrange, when we might want to try to
613// compress the data in the subrange without affecting the rest of
614// the LField data.
615//
617
618template<class T, unsigned Dim>
620LField<T,Dim>::begin(const NDIndex<Dim>& domain, T& compstore)
621{
622
623 if (IsCompressed())
624 compstore = CompressedData;
625 return iterator(P,domain,Allocated,compstore);
626}
627
628
630//
631// Swap the pointers between two LFields.
632//
634
635template<class T, unsigned Dim>
636void
638{
639
640
641
642 // Swap the pointers to the data.
643 {
644 T *temp=P;
645 P=a.P;
646 a.P=temp;
647 }
648
649 // Swap the compressed data.
650 {
651 T temp = CompressedData;
652 CompressedData = a.CompressedData;
653 a.CompressedData = temp;
654 }
655
656 // Swap the last-compared-for-compression indices
657 {
658 int temp = allocCompressIndex;
659 allocCompressIndex = a.allocCompressIndex;
660 a.allocCompressIndex = temp;
661 temp = ownedCompressIndex;
662 ownedCompressIndex = a.ownedCompressIndex;
663 a.ownedCompressIndex = temp;
664 }
665
666 // Swap the offset block value
667 {
668 int temp = offsetBlocks;
669 offsetBlocks = a.offsetBlocks;
670 a.offsetBlocks = temp;
671 }
672
673 // Reinitialize the begin iterators.
674 Begin = iterator(P,Owned,Allocated,CompressedData);
675 a.Begin = iterator(a.P,a.Owned,a.Allocated,a.CompressedData);
676
677 // Make sure the domains agree.
678 PAssert(Owned == a.Owned);
679 PAssert(Allocated == a.Allocated);
680
681 // Should we swap the overlap caches?
682}
683
684
686//
687// Actualy allocate storage for the LField data, doing any special
688// memory tricks needed for performance. Sets P pointer to new memory.
689//
691
692template<class T, unsigned Dim>
693void
695{
696 PAssert(P == 0);
697 PAssert_GT(newsize, 0);
698 PAssert_GE(offsetBlocks, 0);
699
700 // Determine how many blocks to offset the data, if we are asked to
701
702 int extra = 0;
704 extra = offsetBlocks*IPPL_CACHE_LINE_SIZE / sizeof(T);
705
706 // Allocate the storage, creating some extra to account for offset, and
707 // then add in the offset.
708 P = new T[newsize + extra]();
709 P += extra;
710
711 ADDIPPLSTAT(incLFieldBytes, (newsize+extra)*sizeof(T));
712}
713
714
716//
717// Actually free the storage used in the LField, if any. Resets P to zero.
718//
720
721template<class T, unsigned Dim>
722void
724{
725 if (P != 0)
726 {
727 // Determine how many blocks to offset the data, if we are asked to.
728 // If so, move the P pointer back.
729
731 P -= (offsetBlocks*IPPL_CACHE_LINE_SIZE / sizeof(T));
732
733 delete [] P;
734 P = 0;
735 }
736}
737
738
740//
741// print an LField out
742//
744
745template<class T, unsigned Dim>
746void LField<T,Dim>::write(std::ostream& out) const
747{
748
749
750 for (iterator p = begin(); p!=end(); ++p)
751 out << *p << " ";
752}
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
const unsigned Dim
#define IPPL_OFFSET_BLOCKS
Definition: LField.hpp:37
#define LFIELDMSG(x)
Definition: LField.hpp:44
#define IPPL_CACHE_LINE_SIZE
Definition: LField.hpp:28
#define MAKE_INITIALIZER(T)
Definition: LField.hpp:61
std::complex< double > a
Inform & endl(Inform &inf)
Definition: Inform.cpp:42
#define INFORM_ALL_NODES
Definition: Inform.h:39
#define PAssert_LT(a, b)
Definition: PAssert.h:106
#define PAssert(c)
Definition: PAssert.h:102
#define PAssert_GE(a, b)
Definition: PAssert.h:109
#define PAssert_GT(a, b)
Definition: PAssert.h:108
#define PAssert_NE(a, b)
Definition: PAssert.h:105
#define ADDIPPLSTAT(stat, amount)
Definition: IpplStats.h:237
std::string::iterator iterator
Definition: MSLang.h:16
Definition: LField.h:58
void Compress()
Definition: LField.h:161
void deallocateStorage()
Definition: LField.hpp:723
void ReallyUncompress(bool fill_domain)
Definition: LField.hpp:558
NDIndex< Dim > Allocated
Definition: LField.h:238
void swapData(LField< T, Dim > &a)
Definition: LField.hpp:637
iterator Begin
Definition: LField.h:242
CompressedBrickIterator< T, Dim > iterator
Definition: LField.h:62
void allocateStorage(int newsize)
Definition: LField.hpp:694
bool IsCompressed() const
Definition: LField.h:134
bool CanCompress() const
Definition: LField.h:146
bool CanCompressBasedOnPhysicalCells() const
Definition: LField.hpp:389
bool TryCompress(bool baseOnPhysicalCells=false)
Definition: LField.hpp:214
void write(std::ostream &) const
Definition: LField.hpp:746
void CompressBasedOnPhysicalCells()
Definition: LField.hpp:527
~LField()
Definition: LField.hpp:199
NDIndex< Dim > Owned
Definition: LField.h:234
T CompressedData
Definition: LField.h:250
const iterator & begin() const
Definition: LField.h:110
T * P
Definition: LField.h:226
int size(unsigned d) const
Definition: BrickIterator.h:43
static void apply(T &)
Definition: LField.hpp:58
Definition: Inform.h:42
static bool noFieldCompression
Definition: IpplInfo.h:262
static bool extraCompressChecks
Definition: IpplInfo.h:270
static bool offsetStorage
Definition: IpplInfo.h:266
Definition: Unique.h:29