буферы в образцах кода CCL вместе с инструментарием oneapi
Я просматривал образцы кода CCL вместе с инструментарием oneapi. В приведенном ниже коде DPC++(SYCL) изначально sendbuf буфер создается на стороне процессора и не инициализируется, а в той части, где происходит разгрузка на целевое устройство, изменяется переменная dev_acc_sbuf [id], которая является переменной в области ядра.. Следовательно, эта переменная (dev_acc_sbuf) не используется в программе и ее значение не копируется обратно в sendbuf. Затем в следующей строке переменная sendbuf используется для allreduce. Я не могу понять, как изменение dev_acc_sbuf влияет на sendbuf.
cl::sycl::queue q;
cl::sycl::buffer<int, 1> sendbuf(COUNT);
/* open sendbuf and modify it on the target device side */
q.submit([&](cl::sycl::handler& cgh) {
auto dev_acc_sbuf = sendbuf.get_access<mode::write>(cgh);
cgh.parallel_for<class allreduce_test_sbuf_modify>(range<1>{COUNT}, [=](item<1> id) {
dev_acc_sbuf[id] += 1;
});
});
/* invoke ccl_allreduce on the CPU side */
ccl_allreduce(&sendbuf,
&recvbuf,
COUNT,
ccl_dtype_int,
ccl_reduction_sum,
NULL,
NULL,
stream,
&request);
2 ответа
В соответствии "auto dev_acc_sbuf = sendbuf.get_access<mode::write>(cgh);
"the dev_acc_sbuf
это дескриптор, который обращается к sendbuf
а не отдельный буфер. Изменения, сделанные в дескрипторе dev_acc_sbuf, отражаются в исходном буфере, то есть в буфере отправки. Это преимущество SYCL, поскольку изменения, сделанные в области ядра, автоматически копируются обратно в исходную переменную.
В большинстве систем хост и устройство не совместно используют физическую память, ЦП может использовать ОЗУ, а графический процессор может использовать свою собственную глобальную память. SYCL необходимо знать, какие данные он будет передавать между хостом и устройствами.
Для этой цели SYCL использует свои буферы, класс буфера является универсальным по типу элемента и количеству измерений. Когда передается необработанный указатель, конструктор буфера (T* ptr, размер диапазона) становится владельцем переданной памяти. Это означает, что мы абсолютно не можем использовать эту память сами, пока существует буфер, поэтому мы начинаем область видимости C++. По окончании их действия буферы будут уничтожены, а память будет возвращена пользователю. Аргумент размера - это объект диапазона, который должен иметь такое же количество измерений, что и буфер, и инициализируется количеством элементов в каждом измерении. Здесь у нас есть одно измерение с одним элементом.
Буферы не связаны с конкретной очередью или контекстом, поэтому они могут прозрачно обрабатывать данные между несколькими устройствами.
Аксессоры используются для управления запросами доступа к памяти устройства из буферных объектов. Их режимы позаботятся о перемещении данных между хостом и устройством. Таким образом, нам не нужно явно копировать результат с устройства на хост.
Ниже приведен пример для пояснения:
#include <bits/stdc++.h>
#include <CL/sycl.hpp>
using namespace std;
class vector_addition;
int main(int, char**) {
//creating host memory
int *a=(int *)malloc(10*sizeof(int));
int *b=(int *)malloc(10*sizeof(int));
int *c=(int *)malloc(10*sizeof(int));
for(int i=0;i<10;i++){
a[i]=i;
b[i]=10-i;
}
cl::sycl::default_selector device_selector;
cl::sycl::queue queue(device_selector);
std::cout << "Running on "<< queue.get_device().get_info<cl::sycl::info::device::name>()<< "\n";
{
//creating buffer from pointer of host memory
cl::sycl::buffer<int, 1> a_sycl{a, cl::sycl::range<1>{10} };
cl::sycl::buffer<int, 1> b_sycl{b, cl::sycl::range<1>{10} };
cl::sycl::buffer<int, 1> c_sycl{c, cl::sycl::range<1>{10} };
queue.submit([&] (cl::sycl::handler& cgh) {
//creating accessor of buffer with proper mode
auto a_acc = a_sycl.get_access<cl::sycl::access::mode::read>(cgh);
auto b_acc = b_sycl.get_access<cl::sycl::access::mode::read>(cgh);
auto c_acc = c_sycl.get_access<cl::sycl::access::mode::write>(cgh);//responsible for copying back to host memory
//kernel for execution
cgh.parallel_for<class vector_addition>(cl::sycl::range<1>{ 10 }, [=](cl::sycl::id<1> idx) {
c_acc[idx] = a_acc[idx] + b_acc[idx];
});
});
}
for(int i=0;i<10;i++){
cout<<c[i]<<" ";
}
cout<<"\n";
return 0;
}