Предположим, у меня есть массив данных, например, массив трехмерных векторов размера N. Предположим, что каждая итерация моего ядра SYCL исключительно или в основном связана только с одним вектором. Какой из следующих способов разбить это на непрерывные буферы, как правило, более эффективен — или это имеет значение?
Я понимаю, что целевое устройство сильно влияет на это, поэтому давайте предположим, что это дискретный графический процессор (т. е. данные действительно должны быть скопированы на другой чип памяти, и устройство не имеет какой-то сумасшедшей архитектуры, такой как FPGA — я в основном нацелен на GTX 1080 через CUDA, но я ожидаю, что ответ, вероятно, будет аналогичным, когда код компилируется в OpenCL или мы используем другой современный графический процессор.
- Создайте отдельный буфер для каждой координаты, например.
sycl::buffer<float> x, y, z;
, каждый размером N. Таким образом, при доступе к ним я могу использоватьsycl::id<1>
, переданный моему лямбда-выражению ядра, в качестве индекса без математики. (Я подозреваю, что компилятор может оптимизировать это.) - Создайте один упакованный буфер для всех из них, например.
sycl::buffer<float> coords;
размером 3N. При доступе к ним с помощьюsycl::id<1>
, называемогоi
, я получаю координату x какbuffer_accessor[3*i]
, координату y какbuffer_accessor[3*i+1]
и координату z какbuffer_accessor[3*i+2]
. (Я не знаю, может ли компилятор оптимизировать это, и я не уверен, что могут возникнуть проблемы с выравниванием.) - Создайте один распакованный буфер, используя структуру, например.
struct Coord { float x,y,z; }; sycl::buffer<Coord> coords;
. Это имеет довольно тревожную стоимость увеличения использования памяти, в этом примере на 33%, из-за заполнения выравнивания, что также увеличивает время, необходимое для копирования буфера на устройство. Но компромисс заключается в том, что вы можете получить доступ к данным, не манипулируяsycl::id<1>
, среда выполнения должна иметь дело только с одним буфером, и на устройстве не должно быть неэффективного выравнивания строк кэша. - Используйте двумерный буфер размера (N,3) и выполняйте итерации только в диапазоне первого измерения. Это менее гибкое решение, и я не понимаю, зачем мне использовать многомерные буферы, если я не перебираю все измерения, если только для этого варианта использования не встроено много оптимизации.
Я не могу найти какие-либо рекомендации по архитектуре данных, чтобы получить представление о таких вещах. Прямо сейчас (4) кажется глупым, (3) включает неприемлемую трату памяти, и я использую (2), но задаюсь вопросом, не следует ли мне вместо этого использовать (1), чтобы избежать манипуляций с идентификатором и 3 * sizeof (float) выровненные фрагменты доступа.