table of Contents
Preface
In "Playing with ArrayFire: 04 First Know Array (1)", we have learned some specific uses of ArrayFire. In this article, we will continue to learn more uses of ArrayFire.
Six, index
Like all functions in ArrayFire, indexing can also be executed in parallel on OpenCL/CUDA devices. Therefore, the index becomes part of the JIT operation and is done using parentheses instead of square brackets (ie as (0) instead of [0]). To index af:: array , you can use one or a combination of the following functions:
- Integer scalar
- seq() represents a linear sequence
- end represents the last element of the dimension
- span represents the entire dimension
- row(i) or col(i) specify a single row/column
- rows(first,last) or cols(first,last) specify the span of rows/columns
Seven, access the array memory on the host and device
The memory in the array can be accessed using the host() and device() functions. The host function copies data from the device and makes it available in a C-style array on the host. Therefore, the developer manages the memory returned by the host. The device function returns a pointer/reference to the device memory for interoperability with external CUDA/OpenCL kernels. Since this memory belongs to ArrayFire, programmers should not try to release/release this pointer. For example, here is how we interact with OpenCL and CUDA:
// Create an array consisting of 3 random numbers
array a = randu(3, f32);
// Copy an array on the device to the host:
float * host_a = a.host<float>();
// access the host data as a normal array
printf("host_a[2] = %g\n", host_a[2]); // last element
// and free memory using delete:
delete[] host_a;
// Get access to the device memory for a CUDA kernel
float * d_cuda = a.device<float>(); // no need to free this
float value;
cudaMemcpy(&value, d_cuda + 2, sizeof(float), cudaMemcpyDeviceToHost);
printf("d_cuda[2] = %g\n", value);
a.unlock(); // unlock to allow garbage collection if necessary
// Because OpenCL uses references rather than pointers, accessing memory
// is similar, but has a somewhat clunky syntax. For the C-API
cl_mem d_opencl = (cl_mem) a.device<float>();
// for the C++ API, you can just wrap this object into a cl::Buffer
// after calling clRetainMemObject.
ArrayFire also provides several auxiliary functions for creating af::array from OpenCL cl_mem references and cl::Buffer objects . For more information, please refer to the include/af/opencl.h file. Finally, if you only want the first value of af::array , you can use the scalar() function to get it:
array a = randu(3);
float val = a.scalar<float>();
printf("scalar value: %g\n", val)
Eight, bitwise operators
In addition to supporting standard mathematical functions, arrays containing integral data types also support bitwise operators, including AND, OR, XOR:
int h_A[] = {
1, 1, 0, 0, 4, 0, 0, 2, 0};
int h_B[] = {
1, 0, 1, 0, 1, 0, 1, 1, 1};
array A = array(3, 3, h_A), B = array(3, 3, h_B);
af_print(A); af_print(B);
array A_and_B = A & B; af_print(A_and_B);
array A_or_B = A | B; af_print(A_or_B);
array A_xor_B = A ^ B; af_print(A_xor_B);
9. Use ArrayFire API in C and C++
The ArrayFire API is packaged in a unified C/C++ header file. To use this library, just include the arrayfire.h header file and start coding!
This is an example of using C API:
#include <arrayfire.h>
// Generate random data and sum and print the result
int main(void)
{
// generate random values
int n = 10000;
af_array a;
af_randu(&a, n);
// sum all the values
float result;
af_sum_all(&result, a, 0);
printf("sum: %g\n", result);
return 0;
}
This is an example of using C++ API:
#include <arrayfire.h>
// Generate random data, sum and print the result.
int main(void)
{
// Generate 10,000 random values
af::array a = af::randu(10000);
// Sum the values and copy the result to the CPU:
double sum = af::sum<float>(a);
printf("sum: %g\n", sum);
return 0;
}