Arrays

From HaskellWiki

Haskell'98 supports just one array constructor type, namely Array, which gives you immutable
boxed arrays. "Immutable" means that these arrays, like any other pure
functional data structure, have contents fixed at construction time.
You can't modify them, only query. There are "modification" operations,
but they just return new array and don't modify the original one. This
makes possible to use Arrays in pure functional code along with lists.
"Boxed" means that array elements are just ordinary Haskell (lazy)
values, which are evaluated on demand, and can even contain bottom
(undefined) value. You can learn how to use these arrays at
http://haskell.org/tutorial/arrays.html and I recommend that you read
this before proceeding to rest of this page

Nowadays main Haskell compilers, GHC and Hugs, ship with
the same set of Hierarchical Libraries,
and these libraries contain a new implementation of arrays which is
backward compatible with the Haskell'98 one, but which has far more features.
Suffice to say that these libraries support 9 types of array
constructors: Array, UArray, IOArray, IOUArray, STArray, STUArray,
DiffArray, DiffUArray and StorableArray. It is no wonder that the
array libraries are a source of so much confusion for new Haskellers. However, they are actually very simple - each provides just one of two interfaces, and one of these you already know.

The first interface provided by the new array library, is defined
by the typeclass IArray (which stands for "immutable array" and defined
in the module Data.Array.IArray)
and defines the same operations that were defined for Array in
Haskell'98. The big difference is that it is now a typeclass and there are 4
array type constructors, each of which implements this interface: Array,
UArray, DiffArray, and DiffUArray. We will later describe the differences
between them and the cases when these other types are preferable to use instead
of the good old Array. Also note that to use Array type constructor
together with other new array types, you need to import
Data.Array.IArray module instead of Data.Array

The second interface is defined by the type class MArray (which stands for
"mutable array" and is defined in the module Data.Array.MArray)
and contains operations to update array elements in-place. Mutable
arrays are very similar to IORefs, only they contain multiple values. Type
constructors for mutable arrays are IOArray and IOUArray and
operations which create, update and query these arrays all belong to the
IO monad:

This program creates an array of 10 elements with all values initially set to 37. Then it reads
the first element of the array. After that, the
program modifies the first element of the array and then reads it
again. The type declaration in the second line is necessary because our little
program doesn't provide enough context to allow the compiler to determine the concrete type of `arr`.

In the same way that IORef has its more general cousin STRef, IOArray has a more
general version STArray (and similarly, IOUArray is parodied by STUArray). These
array types allow one to work with mutable arrays in the ST monad:

Believe it or not, now you know all that is needed to use any
array type. Unless you are interested in speed issues, just use Array,
IOArray and STArray where appropriate. The following topics are almost
exclusively about selecting the proper array type to make programs run
faster.

As we already stated, the update operation on immutable arrays (IArray)
just creates a new copy of the array, which is very inefficient, but it is a
pure operation which can be used in pure functions. On the other hand,
updates on mutable arrays (MArray) are efficient but can be done only
in monadic code. DiffArray combines the best of both worlds - it
supports interface of IArray and therefore can be used in a pure
functional way, but internally uses the efficient update of MArrays.

How does this trick work? DiffArray has a pure external interface, but
internally it represented as a reference to an IOArray.

When the '//' operator is applied to a diff array, its contents
are physically updated in place. The old array silently changes
its representation without changing the visible behavior:
it stores a link to the new current array along with the
difference to be applied to get the old contents.

So if a diff array is used in a single-threaded style,
that is, after '//' application the old version is no longer used,
a!i takes O(1) time and a//d takes O(length d).
Accessing elements of older versions gradually becomes slower.

Updating an array which is not current makes a physical copy.
The resulting array is unlinked from the old family. So you
can obtain a version which is guaranteed to be current and
thus have fast element access by a//[].

The library provides two "differential" array constructors - DiffArray,
made internally from IOArray, and DiffUArray, based on IOUArray. If you really need to, you can construct new "differential" array types from any
'MArray' types living in the 'IO' monad. See the module documentation for further details.

6 Unboxed arrays

In most implementations of lazy evaluation, values are represented at runtime as pointers to either their value, or code for computing their value. This extra level of indirection, together with any extra tags needed by the runtime, is known as a box. The default "boxed" arrays consist of many of these boxes, each of which may compute its value separately. This allows for many neat tricks, like recursively defining an array's elements in terms of one another, or only computing the specific elements of the array which are ever needed. However, for large arrays, it costs a lot in terms of overhead, and if the entire array is always needed, it can be a waste.

Unboxed arrays are more like arrays in C - they contain just the plain
values without this extra level of indirection, so that, for example,
an array of 1024 values of type Int32 will use only 4 kb of memory.
Moreover, indexing of such arrays can be significantly faster.

Of course, unboxed arrays have their own disadvantages. First, unboxed
arrays can be made only of plain values having a fixed size - Int, Word,
Char, Bool, Ptr, Double, etc. (see the full list in the Data.Array.Unboxed module).
You can even implement unboxed arrays yourself for other
simple types, including enumerations. But Integer, String and any
other types defined with variable size cannot be elements of unboxed arrays.
Second, without that extra level of indirection, all of the elements in an unboxed array must be evaluated when the array is evaluated, so you lose the benefits of lazy evaluation. Indexing the array to read just one element will construct the entire array. This is not much of a loss if you will eventually need the whole array, but it does prevent recursively defining the array elements in terms of each other, and may be too expensive if you only ever need specific values. Nevertheless, unboxed arrays are a very useful optimization
instrument, and I recommend using them as much as possible.

So, basically replacing boxed arrays in your program with unboxed ones
is very simple - just add 'U' to the type signatures, and you are done! Of course, if you change Array to UArray, you also need to add "Data.Array.Unboxed"
to your imports list.

A storable array is an IO-mutable array which stores its
contents in a contiguous memory block living in the C
heap. Elements are stored according to the class 'Storable'.
You can obtain the pointer to the array contents to manipulate
elements from languages like C.

It is similar to 'IOUArray' (in particular, it implements the same
MArray interface) but slower. The advantage is that it's compatible
with C through the foreign function interface. The memory addresses of
storable arrays are fixed, so you can pass them to C routines.

The pointer to the array contents is obtained by 'withStorableArray'.
The idea is similar to 'ForeignPtr' (used internally here).
The pointer should be used only during execution of the 'IO' action
returned by the function passed as argument to 'withStorableArray'.

If you want to use this pointer afterwards, ensure that you call
'touchStorableArray' AFTER the last use of the pointer,
so that the array will be not freed too early.

Additional comments: GHC 6.6 (currently in beta stage) should make access to
'StorableArray' as fast as to any other unboxed arrays. The only difference between 'StorableArray' and 'UArray' will be that UArray lies in relocatable part of GHC heap while 'StorableArray' lies in non-relocatable part and therefore keep the fixed address, what allow to pass this address to the C routines and save it in the C data structures.

GHC 6.6 also adds 'unsafeForeignPtrToStorableArray' operation that allows
to use any Ptr as address of 'StorableArray' and in particular work with
arrays returned by C routines. Example of using this operation:

This example allocs memory for 10 Ints (which emulates array returned by some C function),
then converts returned 'Ptr Int' to 'ForeignPtr Int' and 'ForeignPtr Int' to
'StorableArray Int Int'. It then writes and reads first element of array. At the end,
memory used by array is deallocated by 'free' which again emulates deallocation
by C routines. We can also enable automatic freeing of allocated block by replacing
"newForeignPtr_ ptr" with "newForeignPtr finalizerFree ptr". In this case memory will be
automatically freed after last array usage, as for any other Haskell objects.

8 The Haskell Array Preprocessor (STPP)

Using mutable arrays in Haskell (IO and ST ones) is not very handy.
But there is one tool which adds syntax sugar and makes using of such
arrays very close to that in imperative languages. It is written by
Hal Daume III and you can get it as
http://www.isi.edu/~hdaume/STPP/stpp.tar.gz

Using this tool, you can index array elements in arbitrary complex
expressions with just "arr[|i|]" notation and this preprocessor will
automatically convert such syntax forms to appropriate calls to
'readArray' and 'writeArray'. Multi-dimensional arrays are also
supported, with indexing in the form "arr[|i|][|j|]". See further
descriptions at http://www.isi.edu/~hdaume/STPP/

9 ArrayRef library

The ArrayRef library reimplements array libraries with the following extensions:

dynamic (resizable) arrays

polymorphic unboxed arrays

It also adds syntax sugar
what simplifies arrays usage. Although not
so elegant as with STPP, it's on other hand implemented entirely
inside Haskell language, without any preprocessors.

10 Unsafe indexing, freezing/thawing, running over array elements

There are operations that converts between mutable and immutable
arrays of the same type, namely 'freeze' (mutable->immutable) and
'thaw' (immutable->mutable). They make a new array copy. If you are
sure that mutable array will not be modified or immutable array will
not be used after the conversion, you can use unsafeFreeze/unsafeThaw
- these operations convert array in-place if input and resulting
arrays have the same memory representation (i.e. the same type and
boxing). Please note that "unsafe*" operations modifies memory - they
sets/clears flag in array header which tells about array mutability.
So these operations can't be used together with multi-threaded access
to array (using threads or some form of coroutines).

There are also operations that converts unboxed arrays to another
element type, namely castIOUArray and castSTUArray. These operations
rely on actual type representation in memory and therefore there is no
any guarantees on their results. In particular, these operations can
be used to convert any unboxable value to the sequence of bytes and
vice versa, f.e. it's used in AltBinary library to serialize
floating-point values. Please note that these operations don't
recompute array bounds according to changed size of elements. You
should do it yourself using 'sizeOf' operation!

While arrays can have any type of indexes, internally any index after
bounds checking is converted to plain Int value (position) and then one of
low-level operations, unsafeAt, unsafeRead, unsafeWrite, is used. You
can use these operations yourself in order to outpass bounds checking
and make your program faster. These operations are especially useful
if you need to walk through entire array:

-- | Returns a list of all the elements of an array, in the same order-- as their indices.
elems arr =[ unsafeAt arr i
| i <-[0.. rangeSize(bounds arr)-1]]

"unsafe*" operations in such loops are really safe because 'i' loops
only through positions of existing array elements.

11.1 Parallel arrays (module GHC.PArr)

As we already mentioned, array library supports two array varieties -
lazy boxed arrays and strict unboxed ones. Parallel array implements
something intervening - it's a strict boxed immutable array. This
keeps flexibility of using any data type as array element while makes
both creation and access to such arrays much faster. Array creation
implemented as one imperative loop that fills all array elements,
while access to array elements don't need to check "box". It should be
obvious that parallel arrays are not efficient in cases where
calculation of array elements are rather complex and you will not use
most of array elements. One more drawback of practical usage is that
parallel arrays don't support IArray interface which means that you
can't write generic algorithms which work both with Array and parallel
array constructor.

GHC heap contains two kinds of objects - some are just byte sequences,
other contains pointers to other objects (so called "boxes"). These
segregation allows to find chains of references when performing
garbage collection and update these pointers when memory used by heap
is compacted and objects are moved to new places. Internal (raw) GHC's
type Array# represents a sequence of object pointers (boxes). There is
low-level operation in ST monad which allocates array of specified size in heap,
its type is something like (Int -> ST s Array#). The Array# type is used
inside Array type which represents boxed immutable arrays.

There is a different type for _mutable_ boxed arrays
(IOArray/STArray), namely MutableArray#. Separate type for mutable
arrays required because of 2-stage garbage collection mechanism.
Internal representation of Array# and MutableArray# are the same
excluding for some flags in header and this make possible to on-place
convert MutableArray# to Array# and vice versa (this is that
unsafeFreeze and unsafeThaw operations do).

Unboxed arrays are represented by the ByteArray# type. It's just a plain
memory area in the heap, like the C's array. There are two primitive operations
that creates a ByteArray# of specified size - one allocates memory in
normal heap and so this byte array can be moved each time when
garbage collection occurs. This prevents converting of ByteArray#
to plain memory pointer that can be used in C procedures (although
it's still possible to pass current ByteArray# pointer to "unsafe
foreign" procedure if it don't try to store this pointer somewhere).
The second primitive allocates ByteArray# of specified size in
"pinned" heap area, which contains objects with fixed place. Such byte
array will never be moved by GC so it's address can be used as plain
Ptr and shared with C world. First way to create ByteArray# used
inside implementation of all UArray types, second way used in
StorableArray (although StorableArray can also point to data,
allocated by C malloc).

There is also MutableByteArray# type what don't had much difference
with ByteArray#, but GHC's primitives support only monadic read/write
operations for MutableByteArray#, and only pure reads for ByteArray#,
plus unsafeFreeze/unsafeThaw operations that changes appropriate
fields in headers of this arrays. This differentiation don't make much
sense except for additional safety checks.

Based on these primitive operations, the arrays library implements
indexing with any type and with any lower bound, bounds checking and
all other high-level operations. Operations that creates/updates
immutable arrays just creates them as mutable arrays in ST monad, make
all required updates on this array and then use unsafeFreeze before
returning array from runST. Operations on IO arrays are implemented
via operations on ST arrays using stToIO operation.

11.3 Mutable arrays and GC

GHC implements 2-stage GC which is very fast - minor GC occurs after
each 256 kb allocated and scans only this area (plus recent stack
frames) when searching for a "live" data. This solution uses the fact
that usual Haskell data are immutable and therefore any data
structures that was created before previous minor GC can't point to
the data structures created after this GC (due to immutability data
can contain only "backward" references).

But this simplicity breaks when we add to the language mutable
boxed references (IORef/STRef) and arrays (IOArray/STArray).
On each GC, including minor ones, each element in
mutable data structures should be scanned because it may be updated
since last GC and now point to the data allocated since last GC.

For programs that contains a lot of data in mutable boxed
arrays/references GC times may easily outweigh useful computation time.
Ironically, one of such programs is GHC itself.
Solution for such programs is to add to cmdline option like "+RTS -A10m"
which increases size of minor GC chunks from 256 kb to 10 mb, i.e.
makes minor GC 40 times less frequent. You can see effect of this
change using "+RTS -sstderr" option - "%GC time" should significantly decrease.

There is a way to include this option into your executable so it will
be used automatically on each execution - you should just add to yur
project C source file that contains the following line:

char *ghc_rts_opts = "-A10m";

Of course, you can increase or decrease this value according to your needs.

Increasing "-A" value don't comes for free - aside from obvious
increasing of memory usage, execution times (of useful code) will also
grow. Default "-A" value tuned to be close to modern CPU cache sizes
that means that most of memory references are fall inside the cache.
When 10 mb of memory are allocated before doing GC, this data locality
is no more holds. So, increasing "-A" can either increase or decrease
program speed, depending on its nature. Try various settings between
64 kb and 16 mb while running program with "typical" parameters and
try to select a best setting for your concrete program and cpu combination.

There is also another way to avoid increasing GC times - use either
unboxed or immutable arrays. Also note that immutable arrays are build
as mutable ones and then "freezed", so during the construction time GC
will also scan their contents.

Hopefully, GHC 6.6 fixed the problem - it remembers what
references/arrays was updated since last GC and scans only them. You
can suffer from the old problems only in the case when you use very
large arrays.

12 Notes for contributors to this page

if you have any questions, please
ask at the IRC/mailing list. If you have any answers, please submit them
directly to this page. please don't sign your contributions, so that
anyone will feel free to further improve this page. but if you are
compiler/Array libraries author - please sign your text to let us know
that it is the Last Word of Truth :-)