1 | \chapter{Array} |
---|
2 | |
---|
3 | \section{Features Added} |
---|
4 | |
---|
5 | The present work adds a type @array@ to the \CFA standard library~\cite{Cforall}. |
---|
6 | |
---|
7 | This array's length is statically governed and dynamically valued. This static governance achieves argument safety and suggests a path to subscript safety as future work (TODO: cross reference). In present state, this work is a runtime libray accessed through a system of macros, while section [TODO: discuss C conexistence] discusses a path for the new array type to be accessed directly by \CFA's array syntax, replacing the lifted C array that this syntax currently exposes. |
---|
8 | |
---|
9 | This section presents motivating examples of the new array type's usage, and follows up with definitions of the notations that appear. |
---|
10 | |
---|
11 | The core of the new array governance is tracking all array lengths in the type system. Dynamically valued lengths are represented using type variables. The stratification of type variables preceding object declarations makes a length referenceable everywhere that it is needed. For example, a declaration can share one length, @N@, among a pair of parameters and the return. |
---|
12 | \lstinputlisting[language=CFA, firstline=50, lastline=59]{hello-array.cfa} |
---|
13 | Here, the function @f@ does a pointwise comparison, checking if each pair of numbers is within half a percent of each other, returning the answers in a newly allocated bool array. |
---|
14 | |
---|
15 | The array type uses the parameterized length information in its @sizeof(-)@ determination, illustrated in the example's call to @alloc@. That call requests an allocation of type @array(bool, N)@, which the type system deduces from the left-hand side of the initialization, into the return type of the @alloc@ call. Preexesting \CFA behaviour is leveraged here, both in the return-type-only polymorphism, and the @sized(T)@-aware standard-library @alloc@ routine. The new @array@ type plugs into this behaviour by implementing the @sized@/@sizeof(-)@ assertion to have the intuitive meaning. As a result, this design avoids an opportunity for programmer error by making the size/length communication to a called routine implicit, compared with C's @calloc@ (or the low-level \CFA analog @aalloc@) which take an explicit length parameter not managed by the type system. |
---|
16 | |
---|
17 | A harness for this @f@ function shows how dynamic values are fed into the system. |
---|
18 | \lstinputlisting[language=CFA, firstline=100, lastline=119]{hello-array.cfa} |
---|
19 | Here, the @a@ sequence is loaded with decreasing values, and the @b@ sequence with amounts off by a constant, giving relative differences within tolerance at first and out of tolerance later. The driver program is run with two different inputs of sequence length. |
---|
20 | |
---|
21 | The loops in the driver follow the more familiar pattern of using the ordinary variable @n@ to convey the length. The type system implicitly captures this value at the call site (@main@ calling @f@) and makes it available within the callee (@f@'s loop bound). |
---|
22 | |
---|
23 | The two parts of the example show @Z(n)@ adapting a variable into a type-system governed length (at @main@'s declarations of @a@, @b@, and @result@), @z(N)@ adapting in the opposite direction (at @f@'s loop bound), and a passthru use of a governed length (at @f@'s declaration of @ret@.) It is hoped that future language integration will allow the macros @Z@ and @z@ to be omitted entirely from the user's notation, creating the appearance of seamlessly interchanging numeric values with appropriate generic parameters. |
---|
24 | |
---|
25 | The macro-assisted notation, @forall...ztype@, participates in the user-relevant declaration of the name @N@, which becomes usable in parameter/return declarations and in the function body. So future language integration only sweetens this form and does not seek to elimimate the declaration. The present form is chosen to parallel, as closely as a macro allows, the existing forall forms: |
---|
26 | \begin{lstlisting} |
---|
27 | forall( dtype T ) ... |
---|
28 | forall( otype T ) ... |
---|
29 | forall( ztype(N) ) ... |
---|
30 | \end{lstlisting} |
---|
31 | |
---|
32 | The notation @array(thing, N)@ is also macro-assisted, though only in service of enabling multidimensional uses discussed further in section \ref{toc:mdimpl}. In a single-dimensional case, the marco expansion gives a generic type instance, exactly like the original form suggests. |
---|
33 | |
---|
34 | |
---|
35 | |
---|
36 | In summary: |
---|
37 | |
---|
38 | \begin{tabular}{p{15em}p{20em}} |
---|
39 | @ztype( N )@ & within a forall, declares the type variable @N@ to be a governed length \\[0.25em] |
---|
40 | @Z( @ $e$ @ )@ & a type representing the value of $e$ as a governed length, where $e$ is a @size_t@-typed expression \\[0.25em] |
---|
41 | @z( N )@ & an expression of type @size_t@, whose value is the governed length @N@ \\[0.25em] |
---|
42 | @array( thing, N0, N1, ... )@ |
---|
43 | & a type wrapping $\prod_i N_i$ adjacent occurrences of @thing@ objects |
---|
44 | \end{tabular} |
---|
45 | |
---|
46 | Unsigned integers have a special status in this type system. Unlike how C++ allows @template< size_t N, char * msg, typename T >...@ declarations, this system does not accommodate values of any user-provided type. TODO: discuss connection with dependent types. |
---|
47 | |
---|
48 | |
---|
49 | An example of a type error demonstrates argument safety. The running example has @f@ expecting two arrays of the same length. A compile-time error occurs when attempting to call @f@ with arrays whose lengths may differ. |
---|
50 | \lstinputlisting[language=CFA, firstline=150, lastline=155]{hello-array.cfa} |
---|
51 | As is common practice in C, the programmer is free to cast, to assert knownledge not shared with the type system. |
---|
52 | \lstinputlisting[language=CFA, firstline=200, lastline=202]{hello-array.cfa} |
---|
53 | |
---|
54 | Argument safety, and the associated implicit communication of length, work with \CFA's generic types too. As a structure can be defined over a parameterized element type, so can it be defined over a parameterized length. Doing so gives a refinement of C's ``flexible array member'' pattern, that allows nesting structures with array members anywhere within other structures. |
---|
55 | \lstinputlisting[language=CFA, firstline=20, lastline=26]{hello-accordion.cfa} |
---|
56 | This structure's layout has the starting offest of @cost_contribs@ varying in @Nclients@, and the offset of @total_cost@ varying in both generic paramters. For a function that operates on a @request@ structure, the type system handles this variation transparently. |
---|
57 | \lstinputlisting[language=CFA, firstline=50, lastline=57]{hello-accordion.cfa} |
---|
58 | In the example runs of a driver program, different offset values are navigated in the two cases. |
---|
59 | \lstinputlisting[language=CFA, firstline=100, lastline=115]{hello-accordion.cfa} |
---|
60 | The output values show that @summarize@ and its caller agree on both the offsets (where the callee starts reading @cost_contribs@ and where the callee writes @total_cost@). Yet the call site still says just, ``pass the request.'' |
---|
61 | |
---|
62 | |
---|
63 | \section{Multidimensional implementation} |
---|
64 | \label{toc:mdimpl} |
---|
65 | |
---|
66 | |
---|
67 | TODO: introduce multidimensional array feature and approaches |
---|
68 | |
---|
69 | The new \CFA standard library @array@ datatype supports multidimensional uses more richly than the C array. The new array's multimentsional interface and implementation, follows an array-of-arrays setup, meaning, like C's @float[n][m]@ type, one contiguous object, with coarsely-strided dimensions directly wrapping finely-strided dimensions. This setup is in contrast with the pattern of array of pointers to other allocations representing a sub-array. Beyond what C's type offers, the new array brings direct support for working with a noncontiguous array slice, allowing a program to work with dimension subscripts given in a non-physical order. C and C++ require a programmer with such a need to manage pointer/offset arithmetic manually. |
---|
70 | |
---|
71 | Examples are shown using a $5 \times 7$ float array, @a@, loaded with increments of $0.1$ when stepping across the length-7 finely-strided dimension shown on columns, and with increments of $1.0$ when stepping across the length-5 corsely-strided dimension shown on rows. |
---|
72 | \lstinputlisting[language=CFA, firstline=120, lastline=128]{hello-md.cfa} |
---|
73 | The memory layout of @a@ has strictly increasing numbers along its 35 contiguous positions. |
---|
74 | |
---|
75 | A trivial form of slicing extracts a contiguous inner array, within an array-of-arrays. Like with the C array, a lesser-dimensional array reference can be bound to the result of subscripting a greater-dimensional array, by a prefix of its dimensions. This action first subscripts away the most coaresly strided dimensions, leaving a result that expects to be be subscripted by the more finely strided dimensions. |
---|
76 | \lstinputlisting[language=CFA, firstline=60, lastline=66]{hello-md.cfa} |
---|
77 | \lstinputlisting[language=CFA, firstline=140, lastline=140]{hello-md.cfa} |
---|
78 | |
---|
79 | This function declaration is asserting too much knowledge about its parameter @c@, for it to be usable for printing either a row slice or a column slice. Specifically, declaring the parameter @c@ with type @array@ means that @c@ is contiguous. However, the function does not use this fact. For the function to do its job, @c@ need only be of a container type that offers a subscript operator (of type @ptrdiff_t@ $\rightarrow$ @float@), with governed length @N@. The new-array library provides the trait @ix@, so-defined. With it, the original declaration can be generalized, while still implemented with the same body, to the latter declaration: |
---|
80 | \lstinputlisting[language=CFA, firstline=40, lastline=44]{hello-md.cfa} |
---|
81 | \lstinputlisting[language=CFA, firstline=145, lastline=145]{hello-md.cfa} |
---|
82 | |
---|
83 | Nontrivial slicing, in this example, means passing a noncontiguous slice to @print1d@. The new-array library provides a ``subscript by all'' operation for this purpose. In a multi-dimensional subscript operation, any dimension given as @all@ is left ``not yet subscripted by a value,'' implementing the @ix@ trait, waiting for such a value. |
---|
84 | \lstinputlisting[language=CFA, firstline=150, lastline=151]{hello-md.cfa} |
---|
85 | |
---|
86 | The example has shown that @a[2]@ and @a[[2, all]]@ both refer to the same, ``2.*'' slice. Indeed, the various @print1d@ calls under discussion access the entry with value 2.3 as @a[2][3]@, @a[[2,all]][3]@, and @a[[all,3]][2]@. This design preserves (and extends) C array semantics by defining @a[[i,j]]@ to be @a[i][j]@ for numeric subscripts, but also for ``subscripting by all''. That is: |
---|
87 | |
---|
88 | \begin{tabular}{cccccl} |
---|
89 | @a[[2,all]][3]@ & $=$ & @a[2][all][3]@ & $=$ & @a[2][3]@ & (here, @all@ is redundant) \\ |
---|
90 | @a[[all,3]][2]@ & $=$ & @a[all][3][2]@ & $=$ & @a[2][3]@ & (here, @all@ is effective) |
---|
91 | \end{tabular} |
---|
92 | |
---|
93 | Narrating progress through each of the @-[-][-][-]@ expressions gives, firstly, a definition of @-[all]@, and secondly, a generalization of C's @-[i]@. |
---|
94 | |
---|
95 | \noindent Where @all@ is redundant: |
---|
96 | |
---|
97 | \begin{tabular}{ll} |
---|
98 | @a@ & 2-dimensional, want subscripts for coarse then fine \\ |
---|
99 | @a[2]@ & 1-dimensional, want subscript for fine; lock coarse = 2 \\ |
---|
100 | @a[2][all]@ & 1-dimensional, want subscript for fine \\ |
---|
101 | @a[2][all][3]@ & 0-dimensional; lock fine = 3 |
---|
102 | \end{tabular} |
---|
103 | |
---|
104 | \noindent Where @all@ is effective: |
---|
105 | |
---|
106 | \begin{tabular}{ll} |
---|
107 | @a@ & 2-dimensional, want subscripts for coarse then fine \\ |
---|
108 | @a[all]@ & 2-dimensional, want subscripts for fine then coarse \\ |
---|
109 | @a[all][3]@ & 1-dimensional, want subscript for coarse; lock fine = 3 \\ |
---|
110 | @a[all][3][2]@ & 0-dimensional; lock coarse = 2 |
---|
111 | \end{tabular} |
---|
112 | |
---|
113 | The semantics of @-[all]@ is to dequeue from the front of the ``want subscripts'' list and re-enqueue at its back. The semantics of @-[i]@ is to dequeue from the front of the ``want subscripts'' list and lock its value to be @i@. |
---|
114 | |
---|
115 | Contiguous arrays, and slices of them, are all realized by the same underlying parameterized type. It includes stride information in its metatdata. The @-[all]@ operation is a conversion from a reference to one instantiation, to a reference to another instantiation. The running example's @all@-effective step, stated more concretely, is: |
---|
116 | |
---|
117 | \begin{tabular}{ll} |
---|
118 | @a@ & : 5 of ( 7 of float each spaced 1 float apart ) each spaced 7 floats apart \\ |
---|
119 | @a[all]@ & : 7 of ( 5 of float each spaced 7 floats apart ) each spaced 1 float apart |
---|
120 | \end{tabular} |
---|
121 | |
---|
122 | \begin{figure} |
---|
123 | \includegraphics{measuring-like-layout} |
---|
124 | \caption{Visualization of subscripting by value and by \lstinline[language=CFA,basicstyle=\ttfamily]{all}, for \lstinline[language=CFA,basicstyle=\ttfamily]{a} of type \lstinline[language=CFA,basicstyle=\ttfamily]{array( float, Z(5), Z(7) )}. The horizontal dimension represents memory addresses while vertical layout is conceptual.} |
---|
125 | \label{fig:subscr-all} |
---|
126 | \end{figure} |
---|
127 | |
---|
128 | \noindent While the latter description implies overlapping elements, Figure \ref{fig:subscr-all} shows that the overlaps only occur with unused spaces between elements. Its depictions of @a[all][...]@ show the navigation of a memory layout with nontrivial strides, that is, with ``spaced \_ floats apart'' values that are greater or smaller than the true count of valid indeces times the size of a logically indexed element. Reading from the bottom up, the expression @a[all][3][2]@ shows a float, that is masquerading as a @float[7]@, for the purpose of being arranged among its peers; five such occurrences form @a[all][3]@. The tail of flatter boxes extending to the right of a poper element represents this stretching. At the next level of containment, the structure @a[all][3]@ masquerades as a @float[1]@, for the purpose of being arranged among its peers; seven such occurrences form @a[all]@. The verical staircase arrangement represents this compression, and resulting overlapping. |
---|
129 | |
---|
130 | The new-array library defines types and operations that ensure proper elements are accessed soundly in spite of the overlapping. The private @arpk@ structure (array with explicit packing) is generic over these two types (and more): the contained element, what it is masquerading as. This structure's public interface is the @array(...)@ construction macro and the two subscript operators. Construction by @array@ initializes the masquerading-as type information to be equal to the contained-element information. Subscrpting by @all@ rearranges the order of masquerading-as types to achieve, in genernal, nontrivial striding. Subscripting by a number consumes the masquerading-as size of the contained element type, does normal array stepping according to that size, and returns there element found there, in unmasked form. |
---|
131 | |
---|
132 | The @arpk@ structure and its @-[i]@ operator are thus defined as: |
---|
133 | \begin{lstlisting} |
---|
134 | forall( ztype(N), // length of current dimension |
---|
135 | dtype(S) | sized(S), // masquerading-as |
---|
136 | dtype E_im, // immediate element, often another array |
---|
137 | dtype E_base // base element, e.g. float, never array |
---|
138 | ) { |
---|
139 | struct arpk { |
---|
140 | S strides[z(N)]; // so that sizeof(this) is N of S |
---|
141 | }; |
---|
142 | |
---|
143 | // expose E_im, stride by S |
---|
144 | E_im & ?[?]( arpk(N, S, E_im, E_base) & a, ptrdiff_t i ) { |
---|
145 | return (E_im &) a.strides[i]; |
---|
146 | } |
---|
147 | } |
---|
148 | \end{lstlisting} |
---|
149 | |
---|
150 | An instantion of the @arpk@ generic is given by the @array(E_base, N0, N1, ...)@ exapnsion, which is @arpk( N0, Rec, Rec, E_base )@, where @Rec@ is @array(E_base, N1, ...)@. In the base case, @array(E_base)@ is just @E_base@. Because this construction uses the same value for the generic parameters @S@ and @E_im@, the resulting layout has trivial strides. |
---|
151 | |
---|
152 | Subscripting by @all@, to operate on nontrivial strides, is a dequeue-enqueue operation on the @E_im@ chain, which carries @S@ instatiations, intact, to new positions. Expressed as an operation on types, this rotation is: |
---|
153 | \begin{eqnarray*} |
---|
154 | suball( arpk(N, S, E_i, E_b) ) & = & enq( N, S, E_i, E_b ) \\ |
---|
155 | enq( N, S, E_b, E_b ) & = & arpk( N, S, E_b, E_b ) \\ |
---|
156 | enq( N, S, arpk(N', S', E_i', E_b), E_b ) & = & arpk( N', S', enq(N, S, E_i', E_b), E_b ) |
---|
157 | \end{eqnarray*} |
---|
158 | |
---|