Introduction to the ArrayFire array

Brian Kloppenborg ArrayFire Leave a Comment

The fundamental primitive of the ArrayFire Library is our container object, the array. As the next post in our blog series, "Learning ArrayFire from scratch", we will describe how to construct arrays, what datatypes arrays support, indexing, how to query an array for various properties (content, dimensions), and how to write mathematical expressions that involve arrays.

ArrayFire abstracts away much of the details of programming parallel architectures by providing a high-level container object, the array, that represents data stored on a CPU, GPU, FPGA, or other type of accelerator. This abstraction permits developers to write massively parallel applications in a high-level language where they need not be concerned about low-level optimizations that are frequently required to achieve high throughput on most parallel architectures.

Creating ArrayFire Arrays

Arrays represent memory on a supported device. You can create arrays of uninitialized memory using the array constructor:

Or using one of many constructors that initialize memory:

Lastly, arrays may be populated with data from the host using our copy constructor:

Arrays can also be created from CUDA device pointers or OpenCL memory buffers. Please consult the documentation on these aspects.

Supported data types

ArrayFire arrays support standard integral and complex data types found in C/C++. The data type for an array is specified by providing an (optional) final parameter to any of the array constructors. The type field and supported types are as follows:

  • b8 8-bit boolean values (bool)
  • f32 real single-precision (float)
  • c32 complex single-precision (cfloat)
  • s32 32-bit signed integer (int)
  • u32 32-bit unsigned integer (unsigned)
  • f64 real double-precision (double)
  • c64 complex double-precision (cdouble)
  • s64 64-bit signed integer (intl)
  • u64 64-bit unsigned integer (uintl)
  • s16 16-bit signed integer (short)
  • u16 16-bit unsigned integer (unsigned short)

For example, if you wanted to construct a 2x1 (column vector) of uniformly distributed 32-bit complex numbers (c32 data type), you would use the following code:

Indexing

Like all functions in ArrayFire, indexing is also executed in parallel on the OpenCL/CUDA device. Because of this, indexing becomes part of a JIT operation and is accomplished using parentheses instead of square brackets (i.e. as A(0) instead of A[0]). To index af::arrays you may use one or a combination of the following functions:

  • integer scalars
  • seq() representing a linear sequence
  • end representing the last element of a dimension
  • span representing the entire dimension
  • row(i) or col(i) specifying a single row/column
  • rows(first,last) or cols(first,last) specifying a span of rows or columns

Here are a few examples of these indexing functions in operation:

For further examples, see the documentation on indexing.

Querying existing arrays for their properties

ArrayFire array objects carry some ancillary information which can be quite useful when developing applications. For example, once you have an array A , you can query for:

  • Number and size of dimensions via. A.numdims()  and A.dims(int)  functions.
  • The size of the array in bytes with A.bytes()
  • The underlying data type using A.type() or one of many type-specific queries.
  • Whether or not the array is complex or real using A.iscomplex()  or A.isreal()
  • If the data is scalar, A.isscalar() , a vector, A.isvector() , a row, A.isrow() , or a column, A.iscolumn() .

Writing mathematical expressions

ArrayFire features an intelligent Just-In-Time (JIT) compilation engine that converts expressions using arrays into the smallest number of CUDA/OpenCL kernels. For most operations on arrays, ArrayFire functions like a vector library. That means that an element-wise operation, like c[i] = a[i] + b[i]  in C, would be written more concisely without indexing, like c = a + b . When there are multiple expressions involving arrays, ArrayFire's JIT engine will merge them together. This "kernel fusion" technology not only decreases the number of kernel calls, but, more importantly, avoids extraneous global memory operations. Our JIT functionality extends across C/C++ function boundaries and only ends when a non-JIT function is encountered or a synchronization operation is explicitly called by the code. Thus writing mathematical functions in ArrayFire is as simple as writing the equations themselves!

ArrayFire provides hundreds of functions for element-wise operations. All of the standard operators (e.g. +,-,*,/) are supported as are most transcendental functions (sin, cos, log, sqrt, etc.).

In addition to mathematical functions, ArrayFire also includes frequently used computer vision functions (FAST, ORB, SIFT), and image processing functions (colorspace conversion, filters, histograms, transformations, and morphological operations).

 

Facebooktwittergoogle_plusredditlinkedinmail