## Advanced data flow
## Learning Objectives * Learn about ways to use initial data and pinned memory * Learn about uninitialized buffers and when to use them
#### Precursor
Parts of this lecture will focus primarily on the buffer/accessor model.
#### Initial data
* Often when writing SYCL kernel functions there is initial data which has been allocated somewhere else in the application.
#### Initial data (USM)
auto devicePtr = sycl::malloc_device(sizeInBytes, gpuQueue);

gpuQueue.memcpy(devicePtr, initialData, sizeInBytes).wait();
						
* When using the USM model (unless using system USM) pointers passed to kernel functions must be allocated by the SYCL runtime. * This means you have to copy the initial data to the USM memory allocation using `memcpy`.
#### Initial data (USM)
auto sharedData = sycl::malloc_shared(sizeInBytes, gpuQueue);
						
* Alternatively if your device supports shared USM allocations you can allocate memory which is shared across the host and device. * Then there is no need to copy the data to the device.
#### Initial data (buffer/accessor)
auto buf = sycl::buffer{initialData, sycl::range{size}};
						
* When using the buffer/accessor model a `buffer` can manage already allocated memory or have the SYCL runtime allocate it. * To do this simply provide an initial pointer when constructing a `buffer`. * Note that the SYCL runtime is free to allocate memory and copy this into it, which can introduce an overhead.
#### Use_host_pointer property
auto buf = sycl::buffer{initialData, sycl::range{size},
  {sycl::property::buffer::use_host_ptr{}}};
						
* To prevent the runtime allocation memory you can provide the `property::buffer::use_host_ptr` property when constructing the `buffer`. * This instructs the SYCL runtime that it may not allocate any additional memory. * Though note that the backend (such as OpenCL) may still allocate memory.
#### Copy back
* A `buffer` will synchronize the latest modified copy of the data it manages back to the initial pointer on destruction.
#### Set_final_data
auto buf = sycl::buffer{initialData, sycl::range{size}};

buf.set_final_data(finalData);
						
* To change the destination that a `buffer` will synchronize to on destruction you can call `set_final_data` with another. * The address provided must be capable of holding the size of the data the `buffer` manages.
#### Set_final_data
auto buf = sycl::buffer{initialData, sycl::range{size}};

buf.set_final_data(nullptr);
						
* Alternatively to prevent the `buffer` from synchronizing back to the initial data entirely you can call `set_final_data` with `nullptr`. * A `buffer` with no final data address is useful because the data can left on a device and not copied back to the host from the device.
#### Uninitialized buffers (buffer/accessor)
auto buf = sycl::buffer<T>{sycl::range{size}};
						
* As we've seen in the USM model all memory is allocated initialized, but `buffer`s can be constructed without initial data. * A `buffer` like this is called uninitialized. * To do this simply construct a `buffer` without initial data. Just remember to explicitly specify the buffer's data type as it can't be inferred from the initial data any more. * Uninitialized `buffer`s are useful for a couple of reasons because they can be allocated directly on a device and don't require moving data from the host.
#### Using initial data and uninitialized buffers
![SYCL](../common-revealjs/images/uninitialized_buffer.png "SYCL")
* Here we have an example of using these techniques: * **Input data** is initialized with initial data but doesn't need to be copied back so it can use `set_final_data(nullptr)`. * **Temporary** is only used on the device so can be an uninitialized `buffer`. * **Output data** is initialized on the device and needs to be copied back so it can be an uninitialized `buffer` and use `set_final_data` to provide the final data address.
#### Pinned memory (buffer/accessor)
* Pinned memory is a feature supported by most SYCL backends and devices. * It allows you to allocate memory which can be mapped between the host and device more efficiently, providing similar benefits to USM. * Though the requirements can vary from one device to another. * It's always best to check the vendor's programming guide.
#### Pinned memory (buffer/accessor)
* The SYCL runtime will always aim to manage the memory for you in the most efficient way for the target device. * Generally there are two approaches to facilitate pinned memory: * Allocate memory according to the vendor's programming guide, usually involved allocating a size of a particular multiple and aligned to a particular size, and then use the `property::buffer::use_host_ptr` property. * Create an uninitialized `buffer` and allow the runtime to allocate the memory the appropriate way.
## Questions
#### Exercise
Code_Exercises/Advanced_Data_Flow/source
Write a SYCL application which uses uninitialized `buffer`s and disabling write back.