Имеет ли смысл предварительно загружать данные в общее хранилище вычислительного шейдера для более быстрого доступа для чтения? - PullRequest
0 голосов
/ 13 февраля 2019

У меня есть следующий вычислительный шейдер:

#version 450

layout (local_size_x = 128, local_size_y = 1, local_size_z = 1) in;

layout(push_constant) uniform PushConstant
{
    vec2 topLeft;
    vec2 bottomRight;
};

struct Position {
  float x, y, z;
};

layout (set=0, binding=0) buffer PositionBuffer
{
    Position positions[];
};

layout (set=0, binding=1) buffer SelectionBuffer
{
    uint selected[];
};

void main()
{
    uint ind = gl_GlobalInvocationID.z * (gl_WorkGroupSize.x * gl_NumWorkGroups.x) * (gl_WorkGroupSize.y * gl_NumWorkGroups.y)
               + gl_GlobalInvocationID.y * (gl_WorkGroupSize.x * gl_NumWorkGroups.x)
               + gl_GlobalInvocationID.x;

    Position pos = positions[ind];

    selected[ind] = 0;

    if(pos.x > topLeft.x && pos.x < bottomRight.x && pos.y > topLeft.y && pos.y < bottomRight.y)
    {
        selected[ind] = 1;
    }
}

Что он проверяет, находится ли точка (из буфера positions) внутри предоставленного пользователем прямоугольника (из PushConstant).Если это так - шейдер отмечает точку, записывая 1 в буфер selected.

Этот код работает нормально.Но так как у меня нет опыта работы с компьютером, я ищу способы сделать его лучше.Я знаю, что есть общие переменные, к которым обращается вся группа.Идея состоит в том, чтобы создать массив разделяемых позиций и заполнить его одним потоком, скажем, потоком 0. Тогда, теоретически, другим потокам не нужно читать буферную память, а вместо этого использовать более быструю разделяемую память.

Стоит ли это того?
Как правильно выполнить синхронизацию?
Можно ли сделать что-то подобное для записи данных в массив selected?

1 Ответ

0 голосов
/ 13 февраля 2019

Посмотрите на это с точки зрения вашей общей работы.По порядку вы:

  1. Читаете один непрерывный блок памяти.
  2. Выполняете одну операцию с каждым значением этой памяти.
  3. Записываете результатэта операция в другой блок памяти.

Ваш код никогда не должен читать значение более одного раза.И хотя написанный код потенциально записывает значение дважды, нет никаких причин, по которым должен .Вы также можете легко вычислить значение, основанное на условии, и затем записать это значение в память.И я предполагаю, что хороший компилятор преобразует ваш код именно в это.

Поскольку никакие потоки не читают и не пишут в более чем одно местоположение одновременно, кэшированный доступ к памяти помогает только в том, что он позволяет поворачивать "читать X байтов "в более эффективные" читать байты кэширования "читать.Два вызова, которые пытаются прочитать адреса, которые находятся в одной и той же строке кэша, должны выполнять только одну выборку из памяти.То же самое касается письма;несколько вызовов, записывающих в одну и ту же строку кэша, должны быть объединены в одну запись.

Конечно, это предполагает разумное аппаратное обеспечение.

Это остается гипотетически возможным для такой системывызвать несколько операций чтения / записи из одной и той же памяти.Это связано с количеством вызовов в деформации / волновом фронте (т. Е. С числом вызовов шейдера, который выполняется в режиме блокировки).Если размер данных, считываемых за деформацию, не выровнен по кешу, два деформации могут выполнить чтение для одной и той же строки кеша, поскольку разные деформации могут выполняться одновременно.То же самое касается записи.Но даже это предполагает, что кэш-память и решение о выполнении выборки памяти принимаются на основе деформации.

Независимо от того, если это должно быть определено, правильное решение для этого заключается в выравниваниичитать как можно лучше, а не пытаться выполнить для него работу кэша.

Бывают случаи, когда предварительное кэширование данных было бы полезно, но это в основном происходит в тех случаях, когда вызовы часто читают изодни и те же адреса, и обычно, когда они читают из памяти друг друга.Даже тогда это то, что вы должны профилировать, а не пытаться так кодировать априори.

...