Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
The previous posts in this series on C++ AMP array_view covered:
- Introduction to array_view and some of its key semantic aspects
- Implicit synchronization on destruction of array_views
- array_view discard_data function
- Caching and coherence policies underlying array_view implementation
- 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);
outRandView.discard_data();
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
{
public:
// 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);
}
private:
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
- Anonymous
May 22, 2013
The comment has been removed