当前位置: 首页 > 工具软件 > ArrayFire > 使用案例 >

玩转 ArrayFire:04 初识 array(一)

宗政天逸
2023-12-01


前言

在《玩转 ArrayFire:03 第一个 ArrayFire 程序》中,我们已经创建第一个 ArrayFire 程序,然而却没有深入了解 ArrayFire 的用法,在这一篇中,我们将继续学习 ArrayFire 的具体用法。


一、array 支持的数据类型

     ArrayFire 提供了一个通用的容器对象,在 array 上执行函数和数学操作。该数组 array 可以表示许多不同的基本数据类型,如下表。并且,除非另外指定,否则数组的默认数据类型是 f32 (实单精度浮点数)。

f32实单精度浮点数float
c32复单精度浮点数cfloat
f64实双精度浮点数double
c64复双精度浮点数cdouble
b88位布尔值bool
s3232位有符号整数int
u3232位无符号整数unsigned
u88位无符号整数unsigned char
s644位有符号整数intl
s166位有符号整数short
u1616位无符号整数unsigned short

二、创建并填充 array

     array 表示存储在设备上的内存。因此,创建和填充数组将消耗设备上的内存,这些内存只有在数组对象超出作用域时才能释放。由于设备内存分配可能是昂贵的,ArrayFire还包括一个内存管理器,它将在任何可能的情况下重用设备内存。
    当需要使用数组类型时,便可以创建 array 。下面我们展示了如何使用未初始化值创建1D、2D和3D数组:

    // Arrays may be created using the af::array constructor and dimensioned
    // as 1D, 2D, 3D; however, the values in these arrays will be undefined
    array undefined_1D(100);        // 1D array with 100 elements
    array undefined_2D(10, 100);    // 2D array of size 10 x 100
    array undefined_3D(10, 10, 10); // 3D array of size 10 x 10 x 10

    但是,未初始化的内存在应用程序中可能没有用处。ArrayFire 提供了几个方便的函数来创建包含预填充值的数组,包括常量、均匀随机数、均匀正态分布数和单位矩阵:

    // Generate an array of size three filled with zeros.
    // If no data type is specified, ArrayFire defaults to f32.
    // The af::constant function generates the data on the device.
    array zeros      = constant(0, 3);
    // Generate a 1x4 array of uniformly distributed [0,1] random numbers
    // The af::randu function generates the data on the device.
    array rand1      = randu(1, 4);
    // Generate a 2x2 array (or matrix, if you prefer) of random numbers
    // sampled from a normal distribution.
    // The af::randn function generates data on the device.
    array rand2      = randn(2, 2);
    // Generate a 3x3 identity matrix. The data is generated on the device.
    array iden       = af::identity(3, 3);
    // Lastly, create a 2x1 array (column vector) of uniformly distributed
    // 32-bit complex numbers (c32 data type):
    array randcplx   = randu(2, 1, c32);

     array 也可以用主机上的数据填充。例如:

    // Create a six-element array on the host
    float hA[] = {0, 1, 2, 3, 4, 5};
    // Which can be copied into an ArrayFire Array using the pointer copy
    // constructor. Here we copy the data into a 2x3 matrix:
    array A(2, 3, hA);
    // ArrayFire provides a convenince function for printing af::array
    // objects in case you wish to see how the data is stored:
    af_print(A);
    // This technique can also be used to populate an array with complex
    // data (stored in {{real, imaginary}, {real, imaginary},  ... } format
    // as found in C's complex.h and C++'s <complex>.
    // Below we create a 3x1 column vector of complex data values:
    array dB(3, 1, (cfloat*) hA); // 3x1 column vector of complex numbers
    af_print(dB);

     ArrayFire 还支持从 GPU 的内存中进行 array 初始化。例如,使用 CUDA 可以直接调用 cudaMemcpy 来填充 array

    // Create an array on the host, copy it into an ArrayFire 2x3 ArrayFire array
    float host_ptr[] = {0,1,2,3,4,5};
    array a(2, 3, host_ptr);
    // Create a CUDA device pointer, populate it with data from the host
    float *device_ptr;
    cudaMalloc((void**)&device_ptr, 6*sizeof(float));
    cudaMemcpy(device_ptr, host_ptr, 6*sizeof(float), cudaMemcpyHostToDevice);
    // Convert the CUDA-allocated device memory into an ArrayFire array:
    array b(2,3, device_ptr, afDevice); // Note: afDevice (default: afHost)
    // Note that ArrayFire takes ownership over `device_ptr`, so memory will
    // be freed when `b` id destructed. Do not call cudaFree(device_ptr)!

     OpenCL 也有类似的功能。如果您希望将 ArrayFire 与 CUDA 或 OpenCL 代码混合使用,我们建议您查阅 CUDA 互操作性或 OpenCL 互操作性页面以获取详细说明。

三、array 的内容、尺寸和属性

     ArrayFire 提供了一些函数来确定 array 的特征,这包括用于打印内容、查询维度等各方面特征的函数。
     af_print 函数可用于打印已经生成的 array 或任何涉及 array 的表达式:

    // Generate two arrays
    array a = randu(2, 2);
    array b = constant(1, 2, 1);
    // Print them to the console using af_print
    af_print(a);
    af_print(b);
    // Print the results of an expression involving arrays:
    af_print(a.col(0) + b + .4);

     array 的维数可以通过 dim4 对象确定,也可以使用 dims( )numdims( ) 函数直接访问维数来确定:

    // Create a 4x5x2 array of uniformly distributed random numbers
    array a = randu(4,5,2);
    // Determine the number of dimensions using the numdims() function:
    printf("numdims(a)  %d\n",  a.numdims()); // 3
    // We can also find the size of the individual dimentions using either
    // the `dims` function:
    printf("dims = [%lld %lld]\n", a.dims(0), a.dims(1)); // 4,5
    // Or the elements of a af::dim4 object:
    dim4 dims = a.dims();
    printf("dims = [%lld %lld]\n", dims[0], dims[1]); // 4,5

    除了维度之外, array 还具有其他属性,如确定底层类型和大小(以字节为单位)。并且还可以确定 array 是空、实/复数、行/列、标量还是向量:

    // Get the type stored in the array. This will be one of the many
    // `af_dtype`s presented above:
    printf("underlying type: %d\n", a.type());
    // Arrays also have several conveience functions to determine if
    // an Array contains complex or real values:
    printf("is complex? %d    is real? %d\n", a.iscomplex(), a.isreal());
    // if it is a column or row vector
    printf("is vector? %d  column? %d  row? %d\n", a.isvector(), a.iscolumn(), a.isrow());
    // and whether or not the array is empty and how much memory it takes on
    // the device:
    printf("empty? %d  total elements: %lld  bytes: %lu\n", a.isempty(), a.elements(), a.bytes());

四、array 的数学表达式

     ArrayFire 提供了一个智能即时(JIT)编译引擎,可以将使用 array 的表达式转换为最少的CUDA/OpenCL内核。对于 array 的大多数操作,ArrayFire 函数类似于 vector 库。这意味着,像 C 中的 c[i] = a[i] + b[i] 这样的元素操作,在没有索引的情况下,可以写得更简洁,像 c = a + b 。当有多个涉及 array 的表达式时,ArrayFire 的JIT引擎会将它们合并在一起。这种“内核融合”技术不仅减少了内核调用的数量,而且更重要的是,避免了外部的全局内存操作。我们的 JIT 功能扩展了 C/C++ 函数边界,并且只在遇到非JIT函数或代码显式调用同步操作时才结束。
     ArrayFire 为元素操作提供了数百个函数。支持所有的标准运算符(例如+、-、*、/),也支持大多数超越函数(sin、cos、log、sqrt等)。下面是一些例子:

    array R = randu(3, 3);
    af_print(constant(1, 3, 3) + af::complex(sin(R)));  // will be c32
    // rescale complex values to unit circle
    array a = randn(5, c32);
    af_print(a / abs(a));
    // calculate L2 norm of vectors
    array X = randn(3, 4);
    af_print(sqrt(sum(pow(X, 2))));     // norm of every column vector
    af_print(sqrt(sum(pow(X, 2), 0)));  // same as above
    af_print(sqrt(sum(pow(X, 2), 1)));  // norm of every row vector

五、数学常量

     ArrayFire 包含一些与平台无关的常量,比如 Pi、NaN 和 Inf。如果 ArrayFire 没有你需要的常量,你可以使用 af::constant 数组构造函数创建自己的常量。
    常量可以在 ArrayFire 的所有函数中使用。下面我们将演示它们在元素选择和数学表达式中的使用:

    array A = randu(5,5);
    A(where(A > .5)) = af::NaN;
    array x = randu(10e6), y = randu(10e6);
    double pi_est = 4 * sum<float>(hypot(x,y) < 1) / 10e6;
    printf("estimation error: %g\n", fabs(Pi - pi_est));

    请注意,我们的常量有时可能会与标准头文件中的宏定义冲突。当出现这种情况时,请使用 af:: 命名空间引用我们的常量。


 类似资料: