## Multiple Devices
## Learning Objectives * Learn about contexts and what they are used for * Learn about how create dependencies across devices * Learn about moving data between devices
#### What is a context
* In SYCL the underlying execution and memory resources of a platform and its devices is managed by creating a context * A context represents one or more devices, but all devices must be associated with the same platform
#### Implicit context
![Queue](../common-revealjs/images/queue-5.png "SYCL-Queue")
* Every `queue` requires a `context` to manage memory allocation and data movement. * If one is not specified explicitly a `queue` will create a `context` implicitly.
#### Shared context
![Queue](../common-revealjs/images/queue-1.png "SYCL-Queue")
* In order to ensure data is efficiently moved between devices in the same platform you can create a common `context`.
#### Creating queues
![Queue](../common-revealjs/images/queue-3.png "SYCL-Queue")
* You can then create a `queue` for each of the devices from the common `context`.
#### Creating an implicit context
auto defaultQueue = queue{};
						
* A default constructed queue object will use the `default_selector` to choose a device and create an implicit `context`.
#### Creating a context from devices
auto sharedContext = context{{cpuDevice, gpuDevice}};
						
* You can construct a `context` from a `std::vector` of `device`s.
#### Creating a context from a platform
auto sharedContext = context{intelPlatform};
						
* You can construct a `context` from a `platform` in which case it will be associated with all of the devices of that `platform`.
#### Targeting multiple devices
* A single SYCL application will often want to target multiple different devices. * This can be useful for task level parallelism and load balancing. * When doing so you want to ensure that data is moved between devices in a `context` efficiently.
#### Moving between devices
* Often in heterogeneous applications it's necessary to move data from one device to another. * In the USM model this is done explicit via `memcpy` as we've seen before. * In the buffer/accessor model this is done automatically based on dependency analysis.
#### Accessing data on a device
* Remember that a `buffer` will move data to a device when required by an `accessor`.
![Buffer Copied](../common-revealjs/images/buffer-hostmemory-accessor-cg-device.png "Buffer Copied")
#### Accessing data on another device (same context)
* Now if a `buffer` is accessed on a device when the latest copy of the data is on another device, the data will be moved between the devices. * If the two devices are of the same context the data can be copied directly.
![Buffer Copied](../common-revealjs/images/buffer-copied.png "Buffer Copied")
#### Accessing data on another device (different context)
* If the devices are of different `context`s the data must be copied via host memory. * It's important to consider this as it could incur further overhead when moving data between devices.
![Buffer Copied](../common-revealjs/images/copy-via-host.png "Buffer Copied")
#### Moving between devices (buffer/accessor)
![SYCL](../common-revealjs/images/moving_data_between_devices.png "SYCL")
* If a `buffer` is accessed by kernel functions in two different devices commands are enqueued to automatically move the data to the devices it is being accessed on. * If both of those devices are associated with the same context (i.e. same vendor) then the copy is direct. * Otherwise the copy will generally go via the host and has additional overhead.
## Questions
#### Exercise
Code_Exercises/Multiple_Devices/source
Write a SYCL application that splits up a task between two devices.