]> git.uio.no Git - u/mrichter/AliRoot.git/blob - Vc/include/Vc/common/memory.h
Vc package added (version 0.6.79-dev)
[u/mrichter/AliRoot.git] / Vc / include / Vc / common / memory.h
1 /*  This file is part of the Vc library.
2
3     Copyright (C) 2009-2012 Matthias Kretz <kretz@kde.org>
4
5     Vc is free software: you can redistribute it and/or modify
6     it under the terms of the GNU Lesser General Public License as
7     published by the Free Software Foundation, either version 3 of
8     the License, or (at your option) any later version.
9
10     Vc is distributed in the hope that it will be useful, but
11     WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with Vc.  If not, see <http://www.gnu.org/licenses/>.
17
18 */
19
20 #ifndef VC_COMMON_MEMORY_H
21 #define VC_COMMON_MEMORY_H
22
23 #include "memorybase.h"
24 #include <assert.h>
25 #include <algorithm>
26 #include <cstring>
27 #include "memoryfwd.h"
28 #include "macros.h"
29
30 namespace Vc
31 {
32
33 /**
34  * Allocates memory on the Heap with alignment and padding.
35  *
36  * Memory that was allocated with this function must be released with Vc::free! Other methods might
37  * work but are not portable.
38  *
39  * \param n Specifies the number of scalar values the allocated memory must be able to store.
40  *
41  * \return Pointer to memory of the requested type and size, or 0 on error.
42  *
43  * \warning The standard malloc function specifies the number of Bytes to allocate whereas this
44  *          function specifies the number of values, thus differing in a factor of sizeof(T)
45  *
46  * \see Vc::free
47  *
48  * \ingroup Utilities
49  * \headerfile memory.h <Vc/Memory>
50  */
51 template<typename T, Vc::MallocAlignment A>
52 inline ALWAYS_INLINE_L T *ALWAYS_INLINE_R malloc(size_t n)
53 {
54     return static_cast<T *>(Internal::Helper::malloc<A>(n * sizeof(T)));
55 }
56
57 /**
58  * Frees memory that was allocated with Vc::malloc.
59  *
60  * \ingroup Utilities
61  * \headerfile memory.h <Vc/Memory>
62  */
63 template<typename T>
64 inline void ALWAYS_INLINE free(T *p)
65 {
66     Internal::Helper::free(p);
67 }
68
69 template<typename V, size_t Size> struct _MemorySizeCalculation
70 {
71     enum AlignmentCalculations {
72         Alignment = V::Size,
73         AlignmentMask = Alignment - 1,
74         MaskedSize = Size & AlignmentMask,
75         Padding = Alignment - MaskedSize,
76         PaddedSize = MaskedSize == 0 ? Size : Size + Padding
77     };
78 };
79
80 /**
81  * \ingroup Utilities
82  * \headerfile memory.h <Vc/Memory>
83  *
84  * A helper class for fixed-size two-dimensional arrays.
85  *
86  * \param V The vector type you want to operate on. (e.g. float_v or uint_v)
87  * \param Size1 Number of rows
88  * \param Size2 Number of columns
89  */
90 template<typename V, size_t Size1, size_t Size2> class Memory : public VectorAlignedBaseT<V>, public MemoryBase<V, Memory<V, Size1, Size2>, 2, Memory<V, Size2> >
91 {
92     public:
93         typedef typename V::EntryType EntryType;
94     private:
95         typedef MemoryBase<V, Memory<V, Size1, Size2>, 2, Memory<V, Size2> > Base;
96             friend class MemoryBase<V, Memory<V, Size1, Size2>, 2, Memory<V, Size2> >;
97             friend class MemoryDimensionBase<V, Memory<V, Size1, Size2>, 2, Memory<V, Size2> >;
98             enum InternalConstants {
99                 PaddedSize2 = _MemorySizeCalculation<V, Size2>::PaddedSize
100             };
101 #if defined(VC_ICC) && defined(_WIN32)
102             __declspec(align(__alignof(VectorAlignedBaseT<V>)))
103 #elif defined(VC_CLANG)
104             __attribute__((aligned(__alignof(VectorAlignedBaseT<V>))))
105 #endif
106             EntryType m_mem[Size1][PaddedSize2];
107         public:
108             using Base::vector;
109             enum Constants {
110                 RowCount = Size1,
111                 VectorsCount = PaddedSize2 / V::Size
112             };
113
114             /**
115              * \return the number of rows in the array.
116              *
117              * \note This function can be eliminated by an optimizing compiler.
118              */
119             inline size_t rowsCount() const { return RowCount; }
120             /**
121              * \return the number of scalar entries in the whole array.
122              *
123              * \warning Do not use this function for scalar iteration over the array since there will be
124              * padding between rows if \c Size2 is not divisible by \c V::Size.
125              *
126              * \note This function can be optimized into a compile-time constant.
127              */
128             inline size_t entriesCount() const { return Size1 * Size2; }
129             /**
130              * \return the number of vectors in the whole array.
131              *
132              * \note This function can be optimized into a compile-time constant.
133              */
134             inline size_t vectorsCount() const { return VectorsCount * Size1; }
135
136             /**
137              * Copies the data from a different object.
138              *
139              * \param rhs The object to copy the data from.
140              *
141              * \return reference to the modified Memory object.
142              *
143              * \note Both objects must have the exact same vectorsCount().
144              */
145             template<typename Parent, typename RM>
146             inline Memory &operator=(const MemoryBase<V, Parent, 2, RM> &rhs) {
147                 assert(vectorsCount() == rhs.vectorsCount());
148                 std::memcpy(m_mem, rhs.m_mem, vectorsCount() * sizeof(V));
149                 return *this;
150             }
151             /**
152              * Initialize all data with the given vector.
153              *
154              * \param v This vector will be used to initialize the memory.
155              *
156              * \return reference to the modified Memory object.
157              */
158             inline Memory &operator=(const V &v) {
159                 for (size_t i = 0; i < vectorsCount(); ++i) {
160                     vector(i) = v;
161                 }
162                 return *this;
163             }
164     }
165 #if defined(VC_ICC) && VC_ICC < 20120212 && !defined(_WIN32)
166     __attribute__((__aligned__(__alignof(VectorAlignedBaseT<V>))))
167 #endif
168     ;
169
170     /**
171      * A helper class to simplify usage of correctly aligned and padded memory, allowing both vector and
172      * scalar access.
173      *
174      * Example:
175      * \code
176         Vc::Memory<int_v, 11> array;
177
178         // scalar access:
179         for (size_t i = 0; i < array.entriesCount(); ++i) {
180             int x = array[i]; // read
181             array[i] = x;     // write
182         }
183         // more explicit alternative:
184         for (size_t i = 0; i < array.entriesCount(); ++i) {
185             int x = array.scalar(i); // read
186             array.scalar(i) = x;     // write
187         }
188
189         // vector access:
190         for (size_t i = 0; i < array.vectorsCount(); ++i) {
191             int_v x = array.vector(i); // read
192             array.vector(i) = x;       // write
193         }
194      * \endcode
195      * This code allocates a small array and implements three equivalent loops (that do nothing useful).
196      * The loops show how scalar and vector read/write access is best implemented.
197      *
198      * Since the size of 11 is not a multiple of int_v::Size (unless you use the
199      * scalar Vc implementation) the last write access of the vector loop would normally be out of
200      * bounds. But the Memory class automatically pads the memory such that the whole array can be
201      * accessed with correctly aligned memory addresses.
202      *
203      * \param V The vector type you want to operate on. (e.g. float_v or uint_v)
204      * \param Size The number of entries of the scalar base type the memory should hold. This
205      * is thus the same number as you would use for a normal C array (e.g. float mem[11] becomes
206      * Memory<float_v, 11> mem).
207      *
208      * \see Memory<V, 0u>
209      *
210      * \ingroup Utilities
211      * \headerfile memory.h <Vc/Memory>
212      */
213     template<typename V, size_t Size> class Memory<V, Size, 0u> : public VectorAlignedBaseT<V>, public MemoryBase<V, Memory<V, Size, 0u>, 1, void>
214     {
215         public:
216             typedef typename V::EntryType EntryType;
217         private:
218             typedef MemoryBase<V, Memory<V, Size, 0u>, 1, void> Base;
219             friend class MemoryBase<V, Memory<V, Size, 0u>, 1, void>;
220             friend class MemoryDimensionBase<V, Memory<V, Size, 0u>, 1, void>;
221             enum InternalConstants {
222                 Alignment = V::Size,
223                 AlignmentMask = Alignment - 1,
224                 MaskedSize = Size & AlignmentMask,
225                 Padding = Alignment - MaskedSize,
226                 PaddedSize = MaskedSize == 0 ? Size : Size + Padding
227             };
228 #if defined(__INTEL_COMPILER) && defined(_WIN32)
229             __declspec(align(__alignof(VectorAlignedBaseT<V>)))
230 #elif defined(VC_CLANG)
231             __attribute__((aligned(__alignof(VectorAlignedBaseT<V>))))
232 #endif
233             EntryType m_mem[PaddedSize];
234         public:
235             using Base::vector;
236             enum Constants {
237                 EntriesCount = Size,
238                 VectorsCount = PaddedSize / V::Size
239             };
240
241             /**
242              * \return the number of scalar entries in the whole array.
243              *
244              * \note This function can be optimized into a compile-time constant.
245              */
246             inline size_t entriesCount() const { return EntriesCount; }
247
248             /**
249              * \return the number of vectors in the whole array.
250              *
251              * \note This function can be optimized into a compile-time constant.
252              */
253             inline size_t vectorsCount() const { return VectorsCount; }
254
255             template<typename Parent, typename RM>
256             inline Memory<V> &operator=(const MemoryBase<V, Parent, 1, RM> &rhs) {
257                 assert(vectorsCount() == rhs.vectorsCount());
258                 std::memcpy(m_mem, rhs.m_mem, entriesCount() * sizeof(EntryType));
259                 return *this;
260             }
261             inline Memory<V> &operator=(const EntryType *rhs) {
262                 std::memcpy(m_mem, rhs, entriesCount() * sizeof(EntryType));
263                 return *this;
264             }
265             inline Memory &operator=(const V &v) {
266                 for (size_t i = 0; i < vectorsCount(); ++i) {
267                     vector(i) = v;
268                 }
269                 return *this;
270             }
271     }
272 #if defined(VC_ICC) && VC_ICC < 20120212 && !defined(_WIN32)
273     __attribute__((__aligned__(__alignof(VectorAlignedBaseT<V>)) ))
274 #endif
275     ;
276
277     /**
278      * A helper class that is very similar to Memory<V, Size> but with dynamically allocated memory and
279      * thus dynamic size.
280      *
281      * Example:
282      * \code
283         size_t size = 11;
284         Vc::Memory<int_v> array(size);
285
286         // scalar access:
287         for (size_t i = 0; i < array.entriesCount(); ++i) {
288             array[i] = i;
289         }
290
291         // vector access:
292         for (size_t i = 0; i < array.vectorsCount(); ++i) {
293             array.vector(i) = int_v::IndexesFromZero() + i * int_v::Size;
294         }
295      * \endcode
296      * This code allocates a small array with 11 scalar entries
297      * and implements two equivalent loops that initialize the memory.
298      * The scalar loop writes each individual int. The vectorized loop writes int_v::Size values to
299      * memory per iteration. Since the size of 11 is not a multiple of int_v::Size (unless you use the
300      * scalar Vc implementation) the last write access of the vector loop would normally be out of
301      * bounds. But the Memory class automatically pads the memory such that the whole array can be
302      * accessed with correctly aligned memory addresses.
303      * (Note: the scalar loop can be auto-vectorized, except for the last three assignments.)
304      *
305      * \note The internal data pointer is not declared with the \c __restrict__ keyword. Therefore
306      * modifying memory of V::EntryType will require the compiler to assume aliasing. If you want to use
307      * the \c __restrict__ keyword you need to use a standard pointer to memory and do the vector
308      * address calculation and loads and stores manually.
309      *
310      * \param V The vector type you want to operate on. (e.g. float_v or uint_v)
311      *
312      * \see Memory<V, Size>
313      *
314      * \ingroup Utilities
315      * \headerfile memory.h <Vc/Memory>
316      */
317     template<typename V> class Memory<V, 0u, 0u> : public MemoryBase<V, Memory<V, 0u, 0u>, 1, void>
318     {
319         public:
320             typedef typename V::EntryType EntryType;
321         private:
322             typedef MemoryBase<V, Memory<V>, 1, void> Base;
323             friend class MemoryBase<V, Memory<V>, 1, void>;
324             friend class MemoryDimensionBase<V, Memory<V>, 1, void>;
325         enum InternalConstants {
326             Alignment = V::Size,
327             AlignmentMask = Alignment - 1
328         };
329         size_t m_entriesCount;
330         size_t m_vectorsCount;
331         EntryType *m_mem;
332         size_t calcPaddedEntriesCount(size_t x)
333         {
334             size_t masked = x & AlignmentMask;
335             return (masked == 0 ? x : x + (Alignment - masked));
336         }
337     public:
338         using Base::vector;
339
340         /**
341          * Allocate enough memory to access \p size values of type \p V::EntryType.
342          *
343          * The allocated memory is aligned and padded correctly for fully vectorized access.
344          *
345          * \param size Determines how many scalar values will fit into the allocated memory.
346          */
347         inline Memory(size_t size)
348             : m_entriesCount(size),
349             m_vectorsCount(calcPaddedEntriesCount(m_entriesCount)),
350             m_mem(Vc::malloc<EntryType, Vc::AlignOnVector>(m_vectorsCount))
351         {
352             m_vectorsCount /= V::Size;
353         }
354
355         /**
356          * Copy the memory into a new memory area.
357          *
358          * The allocated memory is aligned and padded correctly for fully vectorized access.
359          *
360          * \param rhs The Memory object to copy from.
361          */
362         template<typename Parent, typename RM>
363         inline Memory(const MemoryBase<V, Parent, 1, RM> &rhs)
364             : m_entriesCount(rhs.entriesCount()),
365             m_vectorsCount(rhs.vectorsCount()),
366             m_mem(Vc::malloc<EntryType, Vc::AlignOnVector>(m_vectorsCount * V::Size))
367         {
368             std::memcpy(m_mem, rhs.m_mem, entriesCount() * sizeof(EntryType));
369         }
370
371         /**
372          * Overload of the above function.
373          *
374          * (Because C++ would otherwise not use the templated cctor and use a default-constructed cctor instead.)
375          *
376          * \param rhs The Memory object to copy from.
377          */
378         inline Memory(const Memory<V, 0u> &rhs)
379             : m_entriesCount(rhs.entriesCount()),
380             m_vectorsCount(rhs.vectorsCount()),
381             m_mem(Vc::malloc<EntryType, Vc::AlignOnVector>(m_vectorsCount * V::Size))
382         {
383             std::memcpy(m_mem, rhs.m_mem, entriesCount() * sizeof(EntryType));
384         }
385
386         /**
387          * Frees the memory which was allocated in the constructor.
388          */
389         inline ALWAYS_INLINE ~Memory()
390         {
391             Vc::free(m_mem);
392         }
393
394         /**
395          * Swap the contents and size information of two Memory objects.
396          *
397          * \param rhs The other Memory object to swap.
398          */
399         inline void swap(Memory &rhs) {
400             std::swap(m_mem, rhs.m_mem);
401             std::swap(m_entriesCount, rhs.m_entriesCount);
402             std::swap(m_vectorsCount, rhs.m_vectorsCount);
403         }
404
405         /**
406          * \return the number of scalar entries in the whole array.
407          */
408         inline size_t entriesCount() const { return m_entriesCount; }
409
410         /**
411          * \return the number of vectors in the whole array.
412          */
413         inline size_t vectorsCount() const { return m_vectorsCount; }
414
415         /**
416          * Overwrite all entries with the values stored in \p rhs.
417          *
418          * \param rhs The object to copy the data from.
419          *
420          * \return reference to the modified Memory object.
421          *
422          * \note this function requires the vectorsCount() of both Memory objects to be equal.
423          */
424         template<typename Parent, typename RM>
425         inline Memory<V> &operator=(const MemoryBase<V, Parent, 1, RM> &rhs) {
426             assert(vectorsCount() == rhs.vectorsCount());
427             std::memcpy(m_mem, rhs.m_mem, entriesCount() * sizeof(EntryType));
428             return *this;
429         }
430
431         /**
432          * Overwrite all entries with the values stored in the memory at \p rhs.
433          *
434          * \param rhs The array to copy the data from.
435          *
436          * \return reference to the modified Memory object.
437          *
438          * \note this function requires that there are entriesCount() many values accessible from \p rhs.
439          */
440         inline Memory<V> &operator=(const EntryType *rhs) {
441             std::memcpy(m_mem, rhs, entriesCount() * sizeof(EntryType));
442             return *this;
443         }
444 };
445
446 /**
447  * Prefetch the cacheline containing \p addr for a single read access.
448  *
449  * This prefetch completely bypasses the cache, not evicting any other data.
450  *
451  * \param addr The cacheline containing \p addr will be prefetched.
452  *
453  * \ingroup Utilities
454  * \headerfile memory.h <Vc/Memory>
455  */
456 inline void ALWAYS_INLINE prefetchForOneRead(const void *addr)
457 {
458     Internal::Helper::prefetchForOneRead(addr);
459 }
460
461 /**
462  * Prefetch the cacheline containing \p addr for modification.
463  *
464  * This prefetch evicts data from the cache. So use it only for data you really will use. When the
465  * target system supports it the cacheline will be marked as modified while prefetching, saving work
466  * later on.
467  *
468  * \param addr The cacheline containing \p addr will be prefetched.
469  *
470  * \ingroup Utilities
471  * \headerfile memory.h <Vc/Memory>
472  */
473 inline void ALWAYS_INLINE prefetchForModify(const void *addr)
474 {
475     Internal::Helper::prefetchForModify(addr);
476 }
477
478 /**
479  * Prefetch the cacheline containing \p addr to L1 cache.
480  *
481  * This prefetch evicts data from the cache. So use it only for data you really will use.
482  *
483  * \param addr The cacheline containing \p addr will be prefetched.
484  *
485  * \ingroup Utilities
486  * \headerfile memory.h <Vc/Memory>
487  */
488 inline void ALWAYS_INLINE prefetchClose(const void *addr)
489 {
490     Internal::Helper::prefetchClose(addr);
491 }
492
493 /**
494  * Prefetch the cacheline containing \p addr to L2 cache.
495  *
496  * This prefetch evicts data from the cache. So use it only for data you really will use.
497  *
498  * \param addr The cacheline containing \p addr will be prefetched.
499  *
500  * \ingroup Utilities
501  * \headerfile memory.h <Vc/Memory>
502  */
503 inline void ALWAYS_INLINE prefetchMid(const void *addr)
504 {
505     Internal::Helper::prefetchMid(addr);
506 }
507
508 /**
509  * Prefetch the cacheline containing \p addr to L3 cache.
510  *
511  * This prefetch evicts data from the cache. So use it only for data you really will use.
512  *
513  * \param addr The cacheline containing \p addr will be prefetched.
514  *
515  * \ingroup Utilities
516  * \headerfile memory.h <Vc/Memory>
517  */
518 inline void ALWAYS_INLINE prefetchFar(const void *addr)
519 {
520     Internal::Helper::prefetchFar(addr);
521 }
522
523 } // namespace Vc
524
525 namespace std
526 {
527     template<typename V> inline void swap(Vc::Memory<V> &a, Vc::Memory<V> &b) { a.swap(b); }
528 } // namespace std
529
530 #include "undomacros.h"
531
532 #endif // VC_COMMON_MEMORY_H