source: tests/array-collections/array-md-sbscr-cases.cfa@ 4904b05

Last change on this file since 4904b05 was eb0d9b7, checked in by Michael Brooks <mlbrooks@…>, 5 weeks ago

Improve libcfa-array's bound-check removal and write that thesis section.

The libcfa change adds a more performant alternative for a subset of multidimensional indexing cases that were already functionally correct.
That the new alternative is more performant is not shown in the test suite.
There is an associated new high-performance option for passing an array-or-slice to a function.
The added test cases cover those options.

The added in-thesis demos rely on the new more-performant alternative for multidimensional indexing.

  • Property mode set to 100644
File size: 11.7 KB
Line 
1#include <collections/array.hfa>
2
3#include <assert.h>
4
5float getMagicNumber( ptrdiff_t w, ptrdiff_t x, ptrdiff_t y, ptrdiff_t z ) {
6
7 assert( 0 <= w && w < 3 );
8 assert( 0 <= x && x < 4 );
9 assert( 0 <= y && y < 5 );
10 assert( 0 <= z && z < 6 );
11
12 float ww = (2.0f \ w) / 1.0f;
13 float xx = (2.0f \ x) / 100.0f;
14 float yy = (2.0f \ y) / 10000.0f;
15 float Nz = (2.0f \ z) / 1000000.0f;
16
17 return ww+xx+yy+Nz;
18}
19
20forall( [Nw], [Nx], [Ny], [Nz] )
21void fillHelloData( array( float, Nw, Nx, Ny, Nz ) & wxyz ) {
22 for (w; Nw)
23 for (x; Nx)
24 for (y; Ny)
25 for (z; Nz)
26 wxyz[w][x][y][z] = getMagicNumber(w, x, y, z);
27}
28
29// Work around a compiler optimization that can lead to false failures.
30// Think of `valExpected` as a constant local to each test function.
31// When implemented that way, an optimization, run on some hardware, makes
32// its value be off-by-a-little, compared with the values that have been
33// stored-loaded (in the array under test). This effect has been observed
34// on x86-32 with -O3. Declaring it as below forces the expected value
35// to be stored-loaded too, which keeps the (admittedly lazily done)
36// `assert(f1 == f2)` checks passing, when the intended <w,x,y,z> location
37// is recovered, which is the point of all these tests.
38volatile float valExpected = 0.0;
39
40// Tests all the ways to split dimensions into CFA-supported chunks, by the only order that C supports: coarsest to finest stride.
41forall( [Nw], [Nx], [Ny], [Nz] )
42void test_inOrderSplits( tag(Nw), tag(Nx), tag(Ny), tag(Nz) ) {
43
44 array( float, Nw, Nx, Ny, Nz ) wxyz;
45 fillHelloData(wxyz);
46
47 ptrdiff_t iw = 2, ix = 3, iy=4, iz=5;
48
49 valExpected = getMagicNumber(iw, ix, iy, iz);
50 float valGot = wxyz[iw][ix][iy][iz];
51 assert( valGot == valExpected );
52
53 // order wxyz, natural split (4-0 or 0-4, no intermediate to declare)
54
55 assert(( wxyz[iw, ix, iy, iz] == valExpected ));
56
57 // order wxyz, unnatural split 1-3 (three ways declared)
58
59 typeof( wxyz[iw] ) xyz1 = wxyz[iw];
60 assert(( xyz1[ix, iy, iz] == valExpected ));
61
62 typeof( wxyz[iw] ) xyz2;
63 &xyz2 = &wxyz[iw];
64 assert(( xyz2[ix, iy, iz] == valExpected ));
65
66 assert(( wxyz[iw][ix, iy, iz] == valExpected ));
67
68 // order wxyz, unnatural split 2-2 (three ways declared)
69
70 typeof( wxyz[iw, ix] ) yz1 = wxyz[iw,ix];
71 assert(( yz1[iy, iz] == valExpected ));
72
73 typeof( wxyz[iw, ix] ) yz2;
74 &yz2 = &wxyz[iw, ix];
75 assert(( yz2[iy, iz] == valExpected ));
76
77 assert(( wxyz[iw, ix][iy, iz] == valExpected ));
78
79 // order wxyz, unnatural split 3-1 (three ways declared)
80
81 typeof( wxyz[iw, ix, iy] ) z1 = wxyz[iw, ix, iy];
82 assert(( z1[iz] == valExpected ));
83
84 typeof( wxyz[iw, ix, iy] ) z2;
85 &z2 = &wxyz[iw, ix, iy];
86 assert(( z2[iz] == valExpected ));
87
88 assert(( wxyz[iw, ix, iy][iz] == valExpected ));
89}
90
91// All orders that skip a single dimension, each in its most natural split.
92forall( [Nw], [Nx], [Ny], [Nz] )
93void test_skipSingle( tag(Nw), tag(Nx), tag(Ny), tag(Nz) ) {
94
95 array( float, Nw, Nx, Ny, Nz ) wxyz;
96 fillHelloData(wxyz);
97
98 ptrdiff_t iw = 2, ix = 3, iy=4, iz=5;
99
100 valExpected = getMagicNumber(iw, ix, iy, iz);
101 assert( wxyz[iw][ix][iy][iz] == valExpected );
102
103
104 // order wxyz (no intermediates to declare)
105
106 assert(( wxyz[iw , ix , iy , iz ] == valExpected ));
107 assert(( wxyz[iw-1, ix , iy , iz ] != valExpected ));
108
109 // order xyzw: *xyz, w
110
111 assert(( wxyz[all , ix , iy , iz ][iw ] == valExpected ));
112 assert(( wxyz[all , ix-1, iy , iz ][iw ] != valExpected ));
113 assert(( wxyz[all , ix , iy , iz ][iw-1] != valExpected ));
114
115 // order wyzx: w*yz, x
116
117 assert(( wxyz[iw , all , iy , iz ][ix ] == valExpected ));
118 assert(( wxyz[iw , all , iy-1, iz ][ix ] != valExpected ));
119 assert(( wxyz[iw , all , iy , iz ][ix-1] != valExpected ));
120
121 // order wxzy: wx*z, y
122 #if 0
123 // not working on 32-bit
124 assert(( wxyz[iw , ix , all , iz ][iy ] == valExpected ));
125 assert(( wxyz[iw , ix , all , iz-1][iy ] != valExpected ));
126 assert(( wxyz[iw , ix , all , iz ][iy-1] != valExpected ));
127 #endif
128}
129
130
131// The comments specify a covering set of orders, each in its most natural split.
132// Covering means that each edge on the lattice of dimesnions-provided is used.
133// Natural split means the arity of every -[-,...] tuple equals the dimensionality of its "this" operand, then that the fewest "all" subscripts are given.
134// The commented-out test code shows cases that don't work. We wish all the comment-coverd cases worked.
135forall( [Nw], [Nx], [Ny], [Nz] )
136void test_latticeCoverage( tag(Nw), tag(Nx), tag(Ny), tag(Nz) ) {
137
138 array( float, Nw, Nx, Ny, Nz ) wxyz;
139 fillHelloData(wxyz);
140
141 ptrdiff_t iw = 2, ix = 3, iy=4, iz=5;
142
143 valExpected = getMagicNumber(iw, ix, iy, iz);
144 assert( wxyz[iw][ix][iy][iz] == valExpected );
145
146
147 // order wxyz (no intermediates to declare)
148
149 assert(( wxyz[iw, ix, iy, iz] == valExpected ));
150
151 {
152 // order wyxz: w*y*, xz
153 assert( wxyz[iw][all][iy][all] [ix][iz] == valExpected );
154
155 typeof( wxyz[iw, all, iy, all] ) xz1 = wxyz[iw, all, iy, all];
156 assert(( xz1[ix, iz] == valExpected ));
157
158 typeof( wxyz[iw, all, iy, all] ) xz2;
159 &xz2 = &wxyz[iw, all, iy, all];
160 assert(( xz2[ix, iz] == valExpected ));
161
162 assert(( wxyz[iw , all, iy , all][ix , iz ] == valExpected ));
163 assert(( wxyz[iw-1, all, iy , all][ix , iz ] != valExpected ));
164 assert(( wxyz[iw , all, iy-1, all][ix , iz ] != valExpected ));
165 assert(( wxyz[iw , all, iy , all][ix-1, iz ] != valExpected ));
166 assert(( wxyz[iw , all, iy , all][ix , iz-1] != valExpected ));
167 }
168 {
169 // order wzxy: w**z, xy
170 assert( wxyz[iw][all][all][iz] [ix][iy] == valExpected );
171
172 // typeof( wxyz[iw, all, all, iz] ) xy1 = wxyz[iw, all, all, iz];
173 // assert(( xy1[ix, iy] == valExpected ));
174
175 // typeof( wxyz[iw, all, all, iz] ) xy2;
176 // &xy2 = &wxyz[iw, all, all, iz];
177 // assert(( xy2[ix, iy] == valExpected ));
178
179 // assert(( wxyz[iw , all, all, iz ][ix , iy ] == valExpected ));
180 // assert(( wxyz[iw-1, all, all, iz ][ix , iy ] != valExpected ));
181 // assert(( wxyz[iw , all, all, iz-1][ix , iy ] != valExpected ));
182 // assert(( wxyz[iw , all, all, iz ][ix-1, iy ] != valExpected ));
183 // assert(( wxyz[iw , all, all, iz ][ix , iy-1] != valExpected ));
184 }
185 {
186 // order xywz: *xy*, wz
187 assert( wxyz[all][ix][iy][all] [iw][iz] == valExpected );
188
189 typeof( wxyz[all, ix, iy, all] ) wz1 = wxyz[all, ix, iy, all];
190 assert(( wz1[iw, iz] == valExpected ));
191
192 assert(( wxyz[all , ix, iy , all][iw , iz ] == valExpected ));
193 }
194 {
195 // order xzwy: *x*z, wy
196 assert( wxyz[all][ix][all][iz] [iw][iy] == valExpected );
197
198 // assert(( wxyz[all , ix , all , iz ][iw , iy ] == valExpected ));
199 }
200 {
201 // order yzwx: **yz, wx
202 assert( wxyz[all][all][iy][iz] [iw][ix] == valExpected );
203
204 // assert(( wxyz[all , all , iy , iz ][iw , ix ] == valExpected ));
205 }
206 {
207 // order xwzy: *x**, w*z, y
208 assert( wxyz[all][ix][all][all] [iw][all][iz] [iy] == valExpected );
209
210 typeof( wxyz[all][ix][all][all] ) wyz_workaround = wxyz[all , ix , all , all ];
211 typeof( wyz_workaround[iw][all][iz] ) y_workaround = wyz_workaround[iw , all , iz ];
212 assert( y_workaround[iy] == valExpected );
213
214 // assert(( wxyz[all , ix , all , all ][iw , all , iz ][iy ] == valExpected ));
215 }
216 {
217 // order ywzx: **y*, w*z, x
218 }
219 {
220 // order zwyx: ***z, w*y, x
221 }
222 {
223 // order yxzw: **y*, *xz, w
224 }
225 {
226 // order zxyw: ***z, *xy, w
227 }
228 {
229 // order zyxw: ***z, **y, *x, w
230 }
231}
232
233// common function body, working on parameter wxyz, but for wxyz being different types
234#define CHECK_NUM_SUBSCR_TYPE_COMPAT \
235 valExpected = getMagicNumber(2, 3, 4, 5); \
236 assert(( wxyz[2] [3] [4] [5] == valExpected )); \
237 assert(( wxyz[2, 3] [4] [5] == valExpected )); \
238 assert(( wxyz[2] [3, 4] [5] == valExpected )); \
239 assert(( wxyz[2] [3] [4, 5] == valExpected )); \
240 assert(( wxyz[2, 3, 4] [5] == valExpected )); \
241 assert(( wxyz[2] [3, 4, 5] == valExpected )); \
242 assert(( wxyz[2, 3, 4, 5] == valExpected )); \
243 for ( i; Nw ) { \
244 assert(( wxyz[ i, 3, 4, 5 ] == getMagicNumber(i, 3, 4, 5) )); \
245 } \
246 for ( i; Nx ) { \
247 assert(( wxyz[ 2, i, 4, 5 ] == getMagicNumber(2, i, 4, 5) )); \
248 } \
249 for ( i; Ny ) { \
250 assert(( wxyz[ 2, 3, i, 5 ] == getMagicNumber(2, 3, i, 5) )); \
251 } \
252 for ( i; Nz ) { \
253 assert(( wxyz[ 2, 3, 4, i ] == getMagicNumber(2, 3, 4, i) )); \
254 }
255#define CHECK_NUM_SUBSCR_TYPE_COMPAT_ADDENDUM_RESHAPE \
256 for ( i; Nw ) { \
257 assert(( wxyz[ i, all, 4, 5 ][3] == getMagicNumber(i, 3, 4, 5) )); \
258 } \
259 for ( i; Nw ) { \
260 assert(( wxyz[ all, 3, 4, 5 ][i] == getMagicNumber(i, 3, 4, 5) )); \
261 }
262
263// Low abstraction: simple declaration, cannot send a slice, can make a slice, runs fast
264forall( [Nw], [Nx], [Ny], [Nz] )
265void test_numSubscrTypeCompatibility_lo( array( float, Nw, Nx, Ny, Nz ) & wxyz ) {
266 CHECK_NUM_SUBSCR_TYPE_COMPAT
267 CHECK_NUM_SUBSCR_TYPE_COMPAT_ADDENDUM_RESHAPE
268}
269
270// Medium abstraction: complex declaration, can send or make a slice, runs fast
271forall( [Nw], Sw*
272 , [Nx], Sx*
273 , [Ny], Sy*
274 , [Nz], Sz* )
275void test_numSubscrTypeCompatibility_mid(
276 arpk( Nw, Sw,
277 arpk( Nx, Sx,
278 arpk( Ny, Sy,
279 arpk( Nz, Sz, float, float)
280 , float)
281 , float)
282 , float) & wxyz
283 ) {
284 CHECK_NUM_SUBSCR_TYPE_COMPAT
285 CHECK_NUM_SUBSCR_TYPE_COMPAT_ADDENDUM_RESHAPE
286}
287
288// High abstraction: mid-complexity declaration, can send a slice or a non-arpk, cannot make a slice, may not run fast
289forall( [Nw], Awxyz &
290 , [Nx], Axyz &
291 , [Ny], Ayz &
292 , [Nz], Az &
293 | ar( Awxyz, Axyz, Nw )
294 | ar( Axyz, Ayz, Nx )
295 | ar( Ayz, Az, Ny )
296 | ar( Az, float, Nz ) )
297void test_numSubscrTypeCompatibility_hi( Awxyz & wxyz ) {
298 CHECK_NUM_SUBSCR_TYPE_COMPAT
299}
300
301forall( [Nw], [Nx], [Ny], [Nz] )
302void test_numSubscrTypeCompatibility( tag(Nw), tag(Nx), tag(Ny), tag(Nz) ) {
303
304 array( float, Nw, Nx, Ny, Nz ) wxyz;
305 fillHelloData(wxyz);
306
307 test_numSubscrTypeCompatibility_lo ( wxyz );
308 test_numSubscrTypeCompatibility_mid( wxyz );
309 test_numSubscrTypeCompatibility_hi ( wxyz );
310}
311
312const size_t KW = 3, KX = 4, KY = 5, KZ = 6;
313
314int main() {
315
316 test_inOrderSplits ( ztag(KW), ztag(KX), ztag(KY), ztag(KZ) );
317 test_skipSingle ( ztag(KW), ztag(KX), ztag(KY), ztag(KZ) );
318 test_latticeCoverage( ztag(KW), ztag(KX), ztag(KY), ztag(KZ) );
319 test_numSubscrTypeCompatibility( ztag(KW), ztag(KX), ztag(KY), ztag(KZ) );
320 printf("done\n");
321}
Note: See TracBrowser for help on using the repository browser.