1 /**
2 Module introduces memory management utilities to help manage memory for SIMD compatible arrays,
3 with or without use of GC.
4 
5 Copyright: Copyright Relja Ljubobratovic 2016.
6 
7 Authors: Relja Ljubobratovic
8 
9 License: $(LINK3 http://www.boost.org/LICENSE_1_0.txt, Boost Software License - Version 1.0).
10 */
11 module dcv.core.memory;
12 
13 import core.simd;
14 import core.memory;
15 import core.cpuid;
16 
17 import std.experimental.allocator.mallocator : AlignedMallocator, Mallocator;
18 
19 /**
20 Allocate array using strict memory alignment.
21 
22 Uses std.experimental.allocator.mallocator.AlignmedMallocator to 
23 allocate memory. 
24 
25 params:
26 count = Count of elements to be allocated for the array.
27 alignment = size in bytes for memory alignment pattern.
28 
29 returns:
30 Dynamic array of type T, with lenght of given count, aligned using
31 given alignment size.
32 
33 note:
34 Dynamic array is not added to GC, so it has to be destoyed explicitly
35 using alignedFree. If GC is needed, use alignedAllocGC.
36  */
37 @nogc @trusted T[] alignedAlloc(T)(size_t count, uint alignment = 16)
38 {
39     auto buff = AlignedMallocator.instance.alignedAllocate(count * T.sizeof, alignment);
40     return (cast(T*)buff)[0 .. count][];
41 }
42 
43 /**
44 Allocate array using strict memory alignment.
45 
46 Uses std.experimental.allocator.mallocator.AlignmedMallocator to 
47 reallocate memory. 
48 Forwards to AlignedMallocator.reallocate.
49 
50 params:
51 ptr = Pointer to a memory where the reallocation is to be performed at.
52 newSize = Size of the reallocated array.
53 
54 returns:
55 Status of reallocation. Returns AlignedMallocator.reallocate out status.
56 */
57 @nogc bool alignedRealloc(ref void[] ptr, size_t newSize)
58 {
59     return AlignedMallocator.instance.reallocate(ptr, newSize);
60 }
61 
62 /**
63 Frees memory allocated using alignedAlloc function.
64 
65 Uses AlignedMallocator.deallocate.
66 
67 params:
68 ptr = Pointer to memory that is to be freed.
69 */
70 void alignedFree(void[] ptr) @nogc
71 {
72     AlignedMallocator.instance.deallocate(ptr);
73 }
74 
75 version (skipSIMD)
76 {
77     T[] allocArray(T)(size_t length)
78     {
79         return new T[length];
80     }
81 
82     void freeArray(void[] ptr) @nogc
83     {
84         ptr.destroy;
85     }
86 }
87 else
88 {
89     T[] allocArray(T)(size_t length) @trusted @nogc
90     {
91         return alignedAlloc!T(length, 16);
92     }
93 
94     void freeArray(void[] ptr) @nogc
95     {
96         ptr.alignedFree;
97     }
98 }
99 
100 unittest
101 {
102     // TODO: design the test...
103 
104     import mir.ndslice;
105 
106     int[] arr = allocArray!int(3);
107     scope (exit)
108         arr.freeArray;
109 
110     auto slice = arr.sliced(3);
111     assert(&arr[0] == &slice[0]);
112 }
113 
114 /**
115  * Template to get alias to SSE2 compatible vector for given type.
116  */
117 template VectorSSE2(T)
118 {
119     import std.traits : isNumeric;
120 
121     static assert(isNumeric!T, "SIMD vector has to be composed of numberic type");
122     static if (is(T == ubyte))
123     {
124         alias VectorSSE2 = ubyte16;
125     }
126     else static if (is(T == ushort))
127     {
128         alias VectorSSE2 = ushort8;
129     }
130     else static if (is(T == uint))
131     {
132         alias VectorSSE2 = uint4;
133     }
134     else static if (is(T == ulong))
135     {
136         alias VectorSSE2 = ulong2;
137     }
138     else static if (is(T == byte))
139     {
140         alias VectorSSE2 = byte16;
141     }
142     else static if (is(T == short))
143     {
144         alias VectorSSE2 = short8;
145     }
146     else static if (is(T == int))
147     {
148         alias VectorSSE2 = int4;
149     }
150     else static if (is(T == long))
151     {
152         alias VectorSSE2 = long2;
153     }
154     else static if (is(T == float))
155     {
156         alias VectorSSE2 = float4;
157     }
158     else static if (is(T == double))
159     {
160         alias VectorSSE2 = double2;
161     }
162     else
163     {
164         alias VectorSSE2 = void;
165     }
166 }
167 
168 /**
169  * Template to get alias to AVX compatible vector for given type.
170  */
171 template VectorAVX(T)
172 {
173     import std.traits : isNumeric;
174 
175     static assert(isNumeric!T, "SIMD vector has to be composed of numberic type");
176     static if (is(T == ubyte))
177     {
178         alias VectorAVX = ubyte32;
179     }
180     else static if (is(T == ushort))
181     {
182         alias VectorAVX = ushort16;
183     }
184     else static if (is(T == uint))
185     {
186         alias VectorAVX = uint8;
187     }
188     else static if (is(T == ulong))
189     {
190         alias VectorAVX = ulong4;
191     }
192     else static if (is(T == byte))
193     {
194         alias VectorAVX = byte32;
195     }
196     else static if (is(T == short))
197     {
198         alias VectorAVX = short16;
199     }
200     else static if (is(T == int))
201     {
202         alias VectorAVX = int8;
203     }
204     else static if (is(T == long))
205     {
206         alias VectorAVX = long4;
207     }
208     else static if (is(T == float))
209     {
210         alias VectorAVX = float8;
211     }
212     else static if (is(T == double))
213     {
214         alias VectorAVX = double4;
215     }
216     else
217     {
218         alias VectorAVX = void;
219     }
220 }
221 
222 enum size_t vectorSize(T) = T.init.length;