concurrency::array_view – data source lifetime

The previous posts in this series on C++ AMP array_view covered:

  1. Introduction to array_view and some of its key semantic aspects
  2. Implicit synchronization on destruction of array_views
  3. array_view discard_data function
  4. Caching and coherence policies underlying array_view implementation
  5. Using a staging array as an array_view’s data source

This post will talk about the lifetime management of an array_view’s data source.

array_view data source lifetime

An array_view is bound to a data source and the data source’s memory allocation must outlive the array_view. Any attempts to access an array_view after the data source memory has been de-allocated will result in undefined behavior. Remember to account for the implicit synchronization on destruction of the last array_view of a data source.

Guideline A: Ensure that the memory allocation corresponding to a data source outlives all array_views referencing that data source.

array_view<float> GenerateRandomNumbers(float *seeds, int count)
    array_view<const float> seedView(count, seeds);
    std::vector<float> outRandVec(count);
    array_view<float> outRandView(outRandVec.size(), outRandVec);
    parallel_for_each(outRandView.extent, [seedView, outRandView](index<1> idx) restrict(amp) {
    // Guideline A violation: Returning an array_view that uses a local std::vector as its data
    // source which will be destructed at the end of this function. Accessing the array_view after the
    // std::vector data source is destructed has undefined behavior.
    return outRandView;


An exception in this regard is the use of a concurrency::array container as the data source of an array_view. The memory allocation underlying a concurrency::array is reference counted and lives as long as there is a live array or array_view reference to it. Hence if you are using a concurrency::array as a data source for an array_view, you need not worry about having to keep the source array object live – it is fine for any array_views created from an array to outlive the array itself. In fact, this is a useful technique if an array_view needs to be encapsulated within another user-defined type. In such a scenario, it is often desirable that the type encapsulating the array_view also encapsulates the data source of the array_view so that their lifetimes are managed together. However, if an object of this user-defined type has to be captured in a parallel_for_each kernel, the data source itself cannot be a data member of this user-defined type (since a concurrency::array, a CPU pointer or an STL container cannot be captured by value in a parallel_for_each kernel per the restrict(amp) restrictions). Creating an array_view over a temporary array solves this problem – the buffer allocation underlying the array/array_view being reference counted, lives even after the temporary array is destructed until the array_view itself is destructed.

template <typename value_type>
class Matrix
    // The Matrix type's array_view data member can be constructed from 
    // a temporary array without the array itself being a data member of the type
    // Memory underlying the array is freed when the array_view member is 
    // destructed in the Matrix destructor
    Matrix(int height, int width)
    : _M_data_view(array<value_type, 2>(height, width))
    array_view<value_type> GetRow(int rowNumber) restrict(cpu, amp)
        return _M_data_view[rowNumber];
    value_type& operator(int i, int j) restrict(cpu, amp) 
        return _M_data_view(i, j);
    array_view<value_type, 2> _M_data_view;
Matrix<float> mA(M, W);
// The Matrix object can be captured in the parallel_for_each by value
// as it only contains an array_view data member. If the type has a concurrency::array
// data member, it would have been illegal to capture a Matrix object by value
// in the parallel_for_each kernel
parallel_for_each(mA.extent, [=](index<2> idx) restrict(amp) {
    mA(idx) = fast_math::sqrt(mA(idx));


In closing

In this post we looked at the importance of ensuring the right lifetime for an array_view’s data source.

If there are other array_view topics that are not covered on our blog, please let me know so I can address them too. I would love to hear your feedback, comments and questions below or in our MSDN forum.

Comments (1)

  1. Arman Schwarz says:

    I don't know if this is within the scope of what you're doing here, but I've been trying to enclose a series of structs such as "Matrix" in a concurrency::array. How would I achieve this? The following illustrates my problem:

    #include <amp.h>

    struct A


    A(std::vector<unsigned int> const& v) : arr(concurrency::array<unsigned int, 1>(v.size(),v.begin(),v.end())) {}

    concurrency::array_view<unsigned int, 1> arr;


    int main()


    unsigned arr_size = 100; // number of elements in A::arr

    unsigned A_vec_size = 10; // number of A instances

    // the template vector will be used to fill the arrays of each instance of A

    std::vector<unsigned int> template_vector(arr_size,0);

    // the A_vec object will contain a series of "A" instances (do it with A_arr as well to test)

    std::vector<A> A_vec;

    for (unsigned i = 0; i != A_vec_size; ++i)


    // construct a new instance of "A" on the back of A_vec:



    // attempt to create an array_view from the vector "A" instances we just created:

    concurrency::array_view<A> carrview(10,A_vec); // error C2973: 'Concurrency::array_view<_Value_type>' : invalid template argument 'A'

    return 0;


    Is there any way to achieve this??