Views

1. What is Kokkos View ?

Kokkos Views are a fundamental abstraction in the Kokkos programming model, designed to provide a portable and efficient way to manage multidimensional arrays across diverse computing architectures. These Views serve as the primary data structure in Kokkos, offering a powerful mechanism for handling data in high-performance computing applications.

Views can have up to eight dimensions, and these dimensions can be specified at either compile-time or run-time. The flexibility in dimension specification allows for efficient memory management and optimization across different hardware architectures.

The motivation behind the View abstraction stems from the evolving landscape of high-performance computing. The memory space, which can be explicitly specified as an additional template parameter, determines where the data resides and which execution units can access it.

The View life cycle is an essential aspect of using Kokkos effectively. When a View is constructed, it allocates memory in the specified memory space. Kokkos employs a reference-counting mechanism to manage the lifetime of this allocation. As Views are copied or assigned, the reference count is adjusted accordingly. The View abstraction is part of Kokkos' broader machine model, which assumes a hierarchy of execution spaces and memory spaces. This model anticipates future shared-memory computing architectures, where nodes might contain multiple types of compute engines and memory systems. By abstracting these details through Views, Kokkos allows developers to write code that can adapt to evolving hardware landscapes without significant rewrites.

In the context of high-performance computing, Kokkos Views shine in their ability to handle large, multidimensional data structures efficiently. They are particularly useful in scientific simulations, linear algebra operations, and other computationally intensive tasks.

2. View key concepts and template parameters

A view is a lightweight object that provides a way to access data in a multi-dimensional array. They behave like pointers, so they can be used in the same way as pointers in (C++).

View<double*, ...> x(...), y(...);
...

parallel_for("DAXPY", N, [=] const int64_t i {
    // Views x and y are captured by value (copy)
  y(i) = a * x(i) + y(i);
});

Some general aspects of views:

  • They are multi-diemensional arrays of dimension 0 (scalar), 1 (vector), 2 (matrix), etc (to 8). Their number of dimension (named rank) is fixed at compilation.

  • They are rectangular arrays, i.e., all dimensions are fixed at construction, and

  • the sizes of dimensions are set either at compile-time or runtime.

  • The elements can easily be accessed via the operator ().

Example of a 3D array with dimension set either at compilation or at runtime
View<double***> data("label", N, M, K);     // 3 at runtime, 0 at compilation
View<double**[K]> data("label", N, M);      // 2 at runtime, 1 at compilation
View<double[N][M][K]> data("label");        // 3 at compilation

data(i, j, k) = 3.14;
The label of the view is not mandatory, but it is useful for debugging and profiling.

3. Usage

First example: build and fill-up a view
1D example
#include <Kokkos_Core.hpp>
#include <iostream>

int main(int argc, char* argv[]) {
Kokkos::initialize(argc, argv);
{
    const size_t N = 100000;
    // Create a 1D View of doubles
    Kokkos::View<int*> myView("MyView", N);
    // Fill the View with data
    Kokkos::parallel_for(N, KOKKOS_LAMBDA(const int i) { myView(i) = i; });
    // Compute the sum of all elements
    int sum = 0.0;
    Kokkos::parallel_reduce(N, KOKKOS_LAMBDA(const int i, int& partial_sum) {
    partial_sum += myView(i);
    }, sum);

    std::cout << "Sum: " << sum <<  std::endl;
}
Kokkos::finalize();
return 0;
}
2D example
#include <Kokkos_Core.hpp>

int main(int argc, char* argv[]) {
    Kokkos::initialize(argc, argv);
    {
        // Define a 2D view of doubles with 10 elements
        Kokkos::View<double**> view("view", 10, 2);

        // Initialize the view using parallel_for
        Kokkos::parallel_for("InitView", 10, KOKKOS_LAMBDA(const int i) {
            view(i, 0) = i * 1.0;
            view(i, 1) = i * 2.0;
        });

        // Print the view elements
        Kokkos::parallel_for("PrintView", 10, KOKKOS_LAMBDA(const int i) {
            printf("view(%d) = %f\n", i, view(i, 0));
            printf("view(%d) = %f\n", i, view(i, 1));
        });
    }
    Kokkos::finalize();
    return 0;
}

Views behave like std::shared_ptr in the sense that they are reference-counted objects. They are automatically deleted when the last reference to them is removed:

#include <Kokkos_Core.hpp>
#include <iostream>

int main(int argc, char* argv[]) {
    int N = 5, K = 10;
    Kokkos::initialize(argc, argv);
    {
        Kokkos::View<double*[5]> a("a", N), b("b", K);
        a = b;                                                  // a gets deallocated and both a and b are points to the same thing
        Kokkos::View<double**> c(b);                            // copy constructor
        std::cout << "Label of c: " << c.label() << std::endl;  // The label of c is the same as the label of b
        a(0, 2) = 1;
        b(0, 2) = 2;
        c(0, 2) = 3;
        std::cout << "a(0, 2) = " << a(0, 2) << std::endl;
    }
    Kokkos::finalize();
    return 0;
}
Result
Label of c: b
a(0, 2) = 3

4. Properties

Views have several properties that can be queried at runtime:

  • label(): the label of the view

  • rank(): the number of dimensions of the view

  • extent(i): the size of the i-th dimension

  • span(): the total number of elements in the view

  • data(): a pointer to the data

  • operator(): access to the data

5. Exercice

Exercice taken from the Kokkos tutorial: Inner Product, Flat Parallelism an the CPU, with Views.