OPAL (Object Oriented Parallel Accelerator Library) 2024.2
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 CompressedData{},
94 Begin(owned, CompressedData),
95 End(CompressedData),
96 overlapCacheInited(false),
97 allocCompressIndex(0),
98 ownedCompressIndex(-1),
99 offsetBlocks(Unique::get() % IPPL_OFFSET_BLOCKS)
100{
101
102 // Give the LField some initial (compressed) value
104
105 // If we are not actually doing compression, expand the storage out,
106 // and copy the initial value to all the elements
108 this->ReallyUncompress(true);
109
110 //INCIPPLSTAT(incLFields);
111}
112
113//UL: for pinned mempory allocation
114template<class T, unsigned Dim>
116 const NDIndex<Dim>& allocated,
117 int vnode, bool p)
118 : vnode_m(vnode),
119 P(0),
120 Pinned(p),
121 Owned(owned),
122 Allocated(allocated),
123 CompressedData{},
124 Begin(owned, CompressedData),
125 End(CompressedData),
126 overlapCacheInited(false),
127 allocCompressIndex(0),
128 ownedCompressIndex(-1),
129 offsetBlocks(Unique::get() % IPPL_OFFSET_BLOCKS)
130{
131
132 // Give the LField some initial (compressed) value
134
135 // If we are not actually doing compression, expand the storage out,
136 // and copy the initial value to all the elements
138 this->ReallyUncompress(true);
139
140 //INCIPPLSTAT(incLFields);
141}
142
144//
145// Deep copy constructor.
146//
148
149template<class T, unsigned Dim>
151 : vnode_m(lf.vnode_m),
152 P(0),
153 Pinned(false),
154 Owned(lf.Owned),
155 Allocated(lf.Allocated),
156 CompressedData{},
157 Begin(CompressedData),
158 End(CompressedData),
159 overlapCacheInited(false),
160 allocCompressIndex(lf.allocCompressIndex),
161 ownedCompressIndex(lf.ownedCompressIndex),
162 offsetBlocks(Unique::get() % IPPL_OFFSET_BLOCKS)
163{
164
165
166
167 if ( lf.IsCompressed() )
168 {
169 // Build a compressed iterator.
171
172 // get the constant value in lf.
174 }
175 else
176 {
177 // Make sure we have something in this LField
178 PAssert_NE(lf.Allocated.size(), 0);
179
180 // If it is not compressed, allocate storage
181 int n = lf.Allocated.size();
183
184 // Copy the data over.
185 std::copy(lf.P, lf.P + n, P);
186
187 // Build an iterator that counts over the real data.
189 }
190
191 //INCIPPLSTAT(incLFields);
192}
193
194
196//
197// Destructor: just free the memory, if it's there.
198//
200
201template<class T, unsigned Dim>
203{
204 deallocateStorage();
205}
206
207
209//
210// Let the user tell us to try to compress.
211// Return quickly if we already are compressed.
212//
214
215template<class T, unsigned Dim>
216bool
217LField<T,Dim>::TryCompress(bool baseOnPhysicalCells)
218{
219
220
221
223 return false;
224
225 LFIELDMSG(Inform dbgmsg("LField::TryCompress", INFORM_ALL_NODES));
226 LFIELDMSG(dbgmsg << "Trying to compress LField with domain = "<<getOwned());
227 LFIELDMSG(dbgmsg << ", baseOnPhysicalCells = " << baseOnPhysicalCells<<endl);
228
229 if (baseOnPhysicalCells)
230 {
231 if (CanCompressBasedOnPhysicalCells())
232 {
233 CompressBasedOnPhysicalCells();
234 return true;
235 }
236 }
237 else
238 {
239 if (CanCompress() )
240 {
241 Compress();
242 return true;
243 }
244 }
245
246 return false;
247}
248
249
251//
252// Look through the data and figure out if it can be compressed
253// to the given value.
254//
256
257template<class T, unsigned Dim>
258bool
260{
261
262
263
264 // Debugging macro
265 LFIELDMSG(Inform dbgmsg("CanCompress"));
266
267 // We definitely can't do this if compression is disabled.
269 return false;
270
271 // If it is already compressed, we can compress it to any value.
272 if (IsCompressed())
273 //return *Begin == val;
274 return true;
275
276 // It is not currently compressed ... so go through and check
277 // to see if all the elements are the same as the given argument.
278
279 int sz = getAllocated().size();
280 ADDIPPLSTAT(incCompressionCompareMax, sz);
281 T *ptr1 = P;
282 T *mid1 = P + allocCompressIndex;
283 T *end1 = P + sz;
284
285 PAssert_GT(sz, 0);
286 PAssert(P != 0);
287 PAssert_GE(allocCompressIndex, 0);
288 PAssert_LT(allocCompressIndex, sz);
289
290 // Quick short-cut check: compare to the last value in the
291 // array that did not match before.
292
294 {
295 LFIELDMSG(dbgmsg << "Doing short-cut check, comparing " << *mid1);
296 LFIELDMSG(dbgmsg << " to " << val << " at last-alloc-domain-failed");
297 LFIELDMSG(dbgmsg << " index of " << allocCompressIndex << endl);
298 ADDIPPLSTAT(incCompressionCompares, 1);
299
300 if (!(*mid1 == val))
301 {
302 LFIELDMSG(dbgmsg << "Short-cut check determined we cannot ");
303 LFIELDMSG(dbgmsg << "compress, by comparing " << *mid1<<" to ");
304 LFIELDMSG(dbgmsg << val << " at last-alloc-domain-failed index");
305 LFIELDMSG(dbgmsg << " of " << allocCompressIndex << endl);
306
307 // It failed the test, so we can just keep the same index to
308 // check next time, and return.
309 return false;
310 }
311 }
312
313 // Check from the beginning to the last-checked-index
314
315 LFIELDMSG(dbgmsg << "Checking for compression for " << sz << " items, ");
316 LFIELDMSG(dbgmsg << "comparing to value = " << val << endl);
317
319 {
320 // First check from last-failed-position to end, since we've
321 // already looked at *mid1 and should have that section of memory
322 // in cache
323 T *checkptr = mid1 + 1;
324 while (checkptr != end1)
325 {
326 if (!(*checkptr++ == val))
327 {
328 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
329 LFIELDMSG(dbgmsg << (checkptr - mid1) << " compares (");
330 LFIELDMSG(dbgmsg << *(checkptr-1) << " != " << val << ")");
331 LFIELDMSG(dbgmsg << endl);
332 ADDIPPLSTAT(incCompressionCompares, (checkptr - mid1));
333 allocCompressIndex = (checkptr - ptr1) - 1;
334 return false;
335 }
336 }
337
338 // Next, check from the first position to the last-failed-position.
339 checkptr = ptr1;
340 while (checkptr != mid1)
341 {
342 if (!(*checkptr++ == val))
343 {
344 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
345 LFIELDMSG(dbgmsg << (checkptr - ptr1) + (end1 - mid1));
346 LFIELDMSG(dbgmsg << " compares (");
347 LFIELDMSG(dbgmsg << *(checkptr-1) << " != " << val << ")");
348 LFIELDMSG(dbgmsg << endl);
349 ADDIPPLSTAT(incCompressionCompares,
350 (checkptr - ptr1) + (end1 - mid1));
351 allocCompressIndex = (checkptr - ptr1) - 1;
352 return false;
353 }
354 }
355 }
356 else
357 {
358 while (ptr1 != end1)
359 {
360 if (!(*ptr1++ == val))
361 {
362 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
363 LFIELDMSG(dbgmsg << (ptr1 - P) << " compares (");
364 LFIELDMSG(dbgmsg << *(ptr1-1) << " != " << val << ")");
365 LFIELDMSG(dbgmsg << endl);
366 ADDIPPLSTAT(incCompressionCompares, (ptr1 - P));
367 allocCompressIndex = (ptr1 - P) - 1;
368 return false;
369 }
370 }
371 }
372
373 // If we are at this point, we did not find anything that did not
374 // match, so we can compress (woo hoo).
375
376 LFIELDMSG(dbgmsg << "Found that we CAN compress, after " << sz);
377 LFIELDMSG(dbgmsg << " compares." << endl);
378 ADDIPPLSTAT(incCompressionCompares, sz);
379 allocCompressIndex = 0;
380 return true;
381}
382
383
385//
386// Return true if this LField can be compressed based on physical
387// cells only and false if it could not.
388//
390
391template<class T, unsigned Dim>
393{
394
395
396
397 // Debugging macro
398
399 LFIELDMSG(Inform dbgmsg("LField::CanCompressBasedOnPhysicalCells",
401
402 // We definitely can't do this if compression is disabled.
404 return false;
405
406 // If it is already compressed, we can compress it to any value.
407 if (IsCompressed())
408 return true;
409
410 // Make an iterator over my owned domain. The cast is there because
411 // this version of begin() is not a const member function.
412
413 iterator p = const_cast<LField<T,Dim>*>(this)->begin(getOwned());
414
415 // Get the value to compare against, either the first item or
416 // an item from the last point where our compression check failed.
417
418 T val = *p;
419 int sz = getOwned().size();
420 if (IpplInfo::extraCompressChecks && ownedCompressIndex > 0)
421 {
422 // There was a previous value, so get that one to compare against
423 PAssert_LT((unsigned int) ownedCompressIndex, getAllocated().size());
424 val = *(P + ownedCompressIndex);
425 LFIELDMSG(dbgmsg << "Checking owned cells using previous ");
426 LFIELDMSG(dbgmsg << "comparison value " << val << " from index = ");
427 LFIELDMSG(dbgmsg << ownedCompressIndex << " against " << sz);
428 LFIELDMSG(dbgmsg << " elements." << endl);
429 }
430 else
431 {
432 // We just use the first element, and will compare against
433 // the rest, so we know we can skip comparing to this first element.
434 ++p;
435 --sz;
436 LFIELDMSG(dbgmsg << "Checking owned cells using first element " << val);
437 LFIELDMSG(dbgmsg << " for comparison against " << sz << " items."<<endl);
438 }
439
440 // Loop through the other physical cells until we encounter one that
441 // doesn't match the 1st cell. If this occurs, we can't compress.
442
443 ADDIPPLSTAT(incCompressionCompareMax, sz - 1);
444 for (int i=0; i < sz; ++i, ++p)
445 {
446 if (!(*p == val))
447 {
448 LFIELDMSG(dbgmsg << "Found that we cannot compress, after ");
449 LFIELDMSG(dbgmsg << i + 1 << " compares." << endl);
450 ADDIPPLSTAT(incCompressionCompares, i + 1);
451 ownedCompressIndex = (&(*p)) - P;
452 LFIELDMSG(dbgmsg << "changed ownedCompressIndex to ");
453 LFIELDMSG(dbgmsg << ownedCompressIndex << endl);
454 return false;
455 }
456 }
457
458 // Since we made it here, we can compress.
459
460 LFIELDMSG(dbgmsg << "Found that we CAN compress, after ");
461 LFIELDMSG(dbgmsg << sz << " compares." << endl);
462 ADDIPPLSTAT(incCompressionCompares, sz);
463 ownedCompressIndex = (-1);
464 return true;
465}
466
467
469//
470// Force a compression to a specified value. This version compresses
471// the entire allocated domain. If this is called when compression
472// is turned off, it instead copies the given value into the whole
473// domain's storage, so that it at least makes the whole domain
474// equal to the value.
475//
477
478template<class T, unsigned Dim>
479void
481{
482
483
484
485 LFIELDMSG(Inform dbgmsg("LField::Compress", INFORM_ALL_NODES));
486 LFIELDMSG(dbgmsg << "Compressing LField with domain = " << getOwned());
487 LFIELDMSG(dbgmsg << " to new value = " << val << ", already compressed = ");
488 LFIELDMSG(dbgmsg << (IsCompressed() ? 1 : 0) << endl);
489
490 // When compression is disabled, interpret this to mean "assign every element
491 // of the LField to the specified value," which is equivalent to compressing
492 // the LField to the value then uncompressing it:
493
495 {
496 for (iterator lit = begin(); lit != end(); ++lit)
497 *lit = val;
498
499 return;
500 }
501
502 // Compression is enabled if we're here, so save the compressed value and
503 // free up memory if necessary. We copy the value into the compressed
504 // value storage, and then if we're currently compressed, we free up
505 // that memory and update our iterators.
506
507 CompressedData = val;
508 if (!IsCompressed())
509 {
510 Begin.Compress(CompressedData);
511 deallocateStorage();
512 }
513
514 //INCIPPLSTAT(incCompresses);
515}
516
517
519//
520// This function does a compressed based on physical cells only.
521// It will compress to the value of the first element in the owned
522// domain (instead of in the allocated domain). If compression is
523// turned off, this does nothing, it does not even attempt to fill
524// in the owned domain with a value.
525//
527
528template<class T, unsigned Dim>
529void
531{
532
533
534
535 // We do nothing in this case if compression is turned off.
536
538 return;
539
540 // Set compression value to first element in owned domain, and free up
541 // memory if necessary.
542
543 CompressedData = *(begin(getOwned()));
544 if (!IsCompressed())
545 {
546 Begin.Compress(CompressedData);
547 deallocateStorage();
548 }
549
550 //INCIPPLSTAT(incCompresses);
551}
552
553
555//
556// We know this is compressed, so uncompress it.
557//
559
560template<class T, unsigned Dim>
562{
563
564
565
566 PAssert_NE(Allocated.size(), 0);
567
568 // Allocate the data.
569
570 int n = Allocated.size();
571 allocateStorage(n);
572
573 LFIELDMSG(Inform dbgmsg("LField::ReallyUncompress", INFORM_ALL_NODES));
574 LFIELDMSG(dbgmsg << "Uncompressing LField with domain = " << getOwned());
575 LFIELDMSG(dbgmsg << ", fill_domain = " << (fill_domain ? 1 : 0) << endl);
576
577 // Copy the constant value into the new space.
578
579 if (fill_domain)
580 {
581 T val = *Begin;
582 for (int i=0; i<n; i++)
583 P[i] = val;
584 }
585
586 // Make the Begin iterator point to the new data.
587
588 Begin = iterator(P,Owned,Allocated,CompressedData);
589
590 // Indicate we've done one more decompress
591
592 //INCIPPLSTAT(incDecompresses);
593}
594
595
597//
598// get an iterator over a subrange.
599//
601
602template<class T, unsigned Dim>
605{
606 // Remove this profiling because this is too lightweight.
607 //
608 //
609 return iterator(P,domain,Allocated,CompressedData);
610}
611
612
614//
615// Get an iterator over a subrange, when we might want to try to
616// compress the data in the subrange without affecting the rest of
617// the LField data.
618//
620
621template<class T, unsigned Dim>
623LField<T,Dim>::begin(const NDIndex<Dim>& domain, T& compstore)
624{
625
626 if (IsCompressed())
627 compstore = CompressedData;
628 return iterator(P,domain,Allocated,compstore);
629}
630
631
633//
634// Swap the pointers between two LFields.
635//
637
638template<class T, unsigned Dim>
639void
641{
642
643
644
645 // Swap the pointers to the data.
646 {
647 T *temp=P;
648 P=a.P;
649 a.P=temp;
650 }
651
652 // Swap the compressed data.
653 {
654 T temp = CompressedData;
655 CompressedData = a.CompressedData;
656 a.CompressedData = temp;
657 }
658
659 // Swap the last-compared-for-compression indices
660 {
661 int temp = allocCompressIndex;
662 allocCompressIndex = a.allocCompressIndex;
663 a.allocCompressIndex = temp;
664 temp = ownedCompressIndex;
665 ownedCompressIndex = a.ownedCompressIndex;
666 a.ownedCompressIndex = temp;
667 }
668
669 // Swap the offset block value
670 {
671 int temp = offsetBlocks;
672 offsetBlocks = a.offsetBlocks;
673 a.offsetBlocks = temp;
674 }
675
676 // Reinitialize the begin iterators.
677 Begin = iterator(P,Owned,Allocated,CompressedData);
678 a.Begin = iterator(a.P,a.Owned,a.Allocated,a.CompressedData);
679
680 // Make sure the domains agree.
681 PAssert(Owned == a.Owned);
682 PAssert(Allocated == a.Allocated);
683
684 // Should we swap the overlap caches?
685}
686
687
689//
690// Actualy allocate storage for the LField data, doing any special
691// memory tricks needed for performance. Sets P pointer to new memory.
692//
694
695template<class T, unsigned Dim>
696void
698{
699 PAssert(P == 0);
700 PAssert_GT(newsize, 0);
701 PAssert_GE(offsetBlocks, 0);
702
703 // Determine how many blocks to offset the data, if we are asked to
704
705 int extra = 0;
707 extra = offsetBlocks*IPPL_CACHE_LINE_SIZE / sizeof(T);
708
709 // Allocate the storage, creating some extra to account for offset, and
710 // then add in the offset.
711 P = new T[newsize + extra]();
712 P += extra;
713
714 ADDIPPLSTAT(incLFieldBytes, (newsize+extra)*sizeof(T));
715}
716
717
719//
720// Actually free the storage used in the LField, if any. Resets P to zero.
721//
723
724template<class T, unsigned Dim>
725void
727{
728 if (P != 0)
729 {
730 // Determine how many blocks to offset the data, if we are asked to.
731 // If so, move the P pointer back.
732
734 P -= (offsetBlocks*IPPL_CACHE_LINE_SIZE / sizeof(T));
735
736 delete [] P;
737 P = 0;
738 }
739}
740
741
743//
744// print an LField out
745//
747
748template<class T, unsigned Dim>
749void LField<T,Dim>::write(std::ostream& out) const
750{
751
752
753 for (iterator p = begin(); p!=end(); ++p)
754 out << *p << " ";
755}
PartBunchBase< T, Dim >::ConstIterator end(PartBunchBase< T, Dim > const &bunch)
PartBunchBase< T, Dim >::ConstIterator begin(PartBunchBase< T, Dim > const &bunch)
const unsigned Dim
std::complex< double > a
#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
#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
Inform & endl(Inform &inf)
Definition Inform.cpp:42
#define ADDIPPLSTAT(stat, amount)
Definition IpplStats.h:237
#define INFORM_ALL_NODES
Definition Inform.h:39
void Compress()
Definition LField.h:161
void deallocateStorage()
Definition LField.hpp:726
void ReallyUncompress(bool fill_domain)
Definition LField.hpp:561
NDIndex< Dim > Allocated
Definition LField.h:238
void swapData(LField< T, Dim > &a)
Definition LField.hpp:640
iterator Begin
Definition LField.h:246
CompressedBrickIterator< T, Dim > iterator
Definition LField.h:62
void allocateStorage(int newsize)
Definition LField.hpp:697
bool IsCompressed() const
Definition LField.h:134
bool CanCompress() const
Definition LField.h:146
bool CanCompressBasedOnPhysicalCells() const
Definition LField.hpp:392
bool TryCompress(bool baseOnPhysicalCells=false)
Definition LField.hpp:217
void write(std::ostream &) const
Definition LField.hpp:749
void CompressBasedOnPhysicalCells()
Definition LField.hpp:530
~LField()
Definition LField.hpp:202
NDIndex< Dim > Owned
Definition LField.h:234
T CompressedData
Definition LField.h:242
const iterator & begin() const
Definition LField.h:110
T * P
Definition LField.h:226
int size(unsigned d) const
static void apply(T &)
Definition LField.hpp:58
static bool noFieldCompression
Definition IpplInfo.h:262
static bool extraCompressChecks
Definition IpplInfo.h:270
static bool offsetStorage
Definition IpplInfo.h:266
static type get()
Definition Unique.h:33