2
$\begingroup$

This question is about the best strategy to choose a local_size in OpenGL's compute shader. The application is a Lattice Boltzmann Method (LBM) with a number of cells ranging from 100,000 to 1 million. This LBM will be run by end-users with a variety of different graphics cards and they will set the number of cells depending on the capability of their machine.

Although this LBM is 2d, the work to dispatch to the compute shader is, as far as I understand, one-dimensional since each cell can be processed independently.

As it is right now, I've set up the work in the following way.

In the compute shader:

#define LOCAL_GROUP_SIZE 64
layout (local_size_x = LOCAL_GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;

And in the calling function

int nGroups = int(nCells/LOCAL_GROUP_SIZE);
nGroups++; // nGroups x LOCAL_GROUP_SIZE must be greater than the number of cells, otherwise the back ones won't be processed
// [...]
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_ssboCells.bufferId());
glDispatchCompute(nGroups, 1, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glFinish();

The questions I have in mind:

  1. How to choose the LOCAL_GROUP_SIZE to get good performance on average over a variety of GPUs? As far as I understand this size is required to be hard-coded in the CS and cannot be set at run time depending on the user's GPU.
  2. Is there any reason to use a 2 or 3 dimensional work group in this case?
  3. Not sure if the glMemoryBarrier is of any use given that each cell is processed independently.

Thanks in advance for any insight.

$\endgroup$

1 Answer 1

1
$\begingroup$

So you say, that each cell do their own stuff without communication in between... So the local work group size will not affect on the result.

How to choose the LOCAL_GROUP_SIZE to get good performance on average over a variety of GPUs? As far as I understand this size is required to be hard-coded in the CS and cannot be set at run time depending on the user's GPU.

In general, Nvidia uses 32 invocations per warp, whereas AMD uses 64 invocations. As you mentioned, the local work group size is hardcoded in the shader... But the shader can be compiled on runtime. So you can request the GPU model and decide which number to use.

Is there any reason to use a 2 or 3 dimensional work group in this case?

Your problem seems to be one dimensional. So I would keep it one dimensional.

I don't know the Lattice Boltzmann Method... But have in mind, that when invocations within the same workgroup go to different conditional states (if and else), your performance will drop! Because the invocations do all the same at nearly the same time. So if e. G. One invocation within the group goes into the 'if' part, the others will wait until the entire 'if' part is done. Same with the other invocations... If they go to the 'else' part, the one invocation which goes to the 'if' part will wait until the 'else' part is done... Because of that there are different strategies on how to group the tasks.

For example, we have a long 'if' and a long 'else' part... It can make sense to first check where each invocation goes to and then in a second step group the tasks which go to the same condition together.

But this does not make sense for all conditional paths, because this method also takes some time.

But in general a rule of thumb is, keep the workgroups as small as possible with respect to the multiple of a warp/wave size (nvidia 32, AMD 64). This is useful because your GPU is limited in invocations which can do work at the same time. So your GPU will load a few workgroups to execute. When a workgroup has finished, another workgroup can be loaded. So when having only one workgroup where invocations go to both conditional states, only this one workgroup will need time. The other workgroups will be calculated fast and will free their slot for the next workgroup in the queue while the long workgroup still do some work. When keeping the local work group size small, less workgroups will pop up with both conditions. And less invocations are blocked.

$\endgroup$
1
  • $\begingroup$ Thanks, that clarifies things. The shader is compiled at runtime, but unfortunately it seems that OpenGl does not provide a method to query warp size. $\endgroup$
    – techwinder
    Commented Jan 8 at 1:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.