Vulkan-docs: Why do command pools exist?

Created on 13 Aug 2017  Â·  2Comments  Â·  Source: KhronosGroup/Vulkan-Docs

This seems to make no sense to me.

  1. Since you can't immediately reset/free a command buffer after submission, you need to maintain a pool/queue of them anyway to know when they're safe to reset/free.
  2. Since vkResetCommandBuffer exists, there's no point to constantly allocating/freeing command buffers either, so “faster allocation!” seems a non-argument.

In summary, I find myself pretty much required to write my own “command buffer pool” abstraction which statically allocates a bunch of command buffers and cycles through them as needed, resetting them once they're done executing. Isn't that basically reimplementing a command pool? Why do they even exist then?

I would have expected that either

  1. Command pools get removed, and clients are expected to pool command buffers themselves
  2. vkResetCommandBuffer gets removed and queue submission receives a flag that makes it “take over” a command buffer and implicitly frees it once it's done executing, so you can do 1. allocate, 2. record, 3. submit for a typical “streaming command buffer” scenario.

Could somebody be so kind as to explain to me what I'm missing?

Most helpful comment

First, the forum for Vulkan is over here. And your question would be somewhat appropriate for Stack Overflow, where your question was more or less asked and answered already. This place is for bugs in the standard or feature requests, not for asking why a feature is the way it is.

Secondly, your question is answered directly in the specification, the first paragraph of section 5.2:

Command pools are opaque objects that command buffer memory is allocated from, and which
allow the implementation to amortize the cost of resource creation across multiple command
buffers. Command pools are externally synchronized, meaning that a command pool must not be
used concurrently in multiple threads. That includes use via recording commands on any command buffers allocated from the pool, as well as operations that allocate, free, and reset command buffers or the pool itself.

The emphasized part is, to me, the key reason why command pools exist as a concept. They represent a set of command buffers which will be used for a particular thread. As such, any command that allocates memory from command buffers from the pool (among other things, every vkCmd call) does not have to lock some mutex that's shared with other threads.

Compare malloc. This function is defined by the C standard to be fully reentrant. You can call malloc simultaneously from multiple threads, and the implementation of the C standard library has to have mechanisms in place to handle that. Furthermore, malloc has to be reentrant with free and other memory allocation calls. This is typically done via some form of mutex.

By contrast, every vkCmd* function doesn't have to have such mechanisms. Each of these functions might allocate memory from the pool. But because such allocations are specifically not reentrant, the system doesn't have to worry about what happens if two threads call a vkCmd* function on command buffers for the same pool. That's explicitly forbidden by the standard.

So none of those functions have to lock mutexes. Now granted, when they allocate memory from a global allocator, they may still lock a mutex (ie: by calling malloc or an OS-specific API). But if a pool is doing sub-allocations for different command buffers from that larger allocation, they don't have to lock their own individual mutex, since it's forbidden for two threads to call into the same pool. So you get faster performance.

Think of a command pool as the _memory allocator_ for a set of command buffers. It is what does the allocation for all of the data they internally use. Note that vkAllocateCommandBuffers does not take user-defined allocators; it uses the allocators set into the pool.

Note also that the command pool flags are principly about what kind of usage pattern you'll be using. Will your command buffers be reused or are they transient? Will you be resetting command buffers individually, or will you reset them all in one go? These change how the implementation will go about the task of allocating memory. If you're resetting command buffers individually, then the implementation probably should segregate the memory for those individual buffers from one another. Whereas if you're resetting the whole pool, then it's fine for it to sub-allocate different portions of the same allocation to different CBs.

Maybe a more descriptive name would have been "command memory pool", but the current name is adequate.

All 2 comments

First, the forum for Vulkan is over here. And your question would be somewhat appropriate for Stack Overflow, where your question was more or less asked and answered already. This place is for bugs in the standard or feature requests, not for asking why a feature is the way it is.

Secondly, your question is answered directly in the specification, the first paragraph of section 5.2:

Command pools are opaque objects that command buffer memory is allocated from, and which
allow the implementation to amortize the cost of resource creation across multiple command
buffers. Command pools are externally synchronized, meaning that a command pool must not be
used concurrently in multiple threads. That includes use via recording commands on any command buffers allocated from the pool, as well as operations that allocate, free, and reset command buffers or the pool itself.

The emphasized part is, to me, the key reason why command pools exist as a concept. They represent a set of command buffers which will be used for a particular thread. As such, any command that allocates memory from command buffers from the pool (among other things, every vkCmd call) does not have to lock some mutex that's shared with other threads.

Compare malloc. This function is defined by the C standard to be fully reentrant. You can call malloc simultaneously from multiple threads, and the implementation of the C standard library has to have mechanisms in place to handle that. Furthermore, malloc has to be reentrant with free and other memory allocation calls. This is typically done via some form of mutex.

By contrast, every vkCmd* function doesn't have to have such mechanisms. Each of these functions might allocate memory from the pool. But because such allocations are specifically not reentrant, the system doesn't have to worry about what happens if two threads call a vkCmd* function on command buffers for the same pool. That's explicitly forbidden by the standard.

So none of those functions have to lock mutexes. Now granted, when they allocate memory from a global allocator, they may still lock a mutex (ie: by calling malloc or an OS-specific API). But if a pool is doing sub-allocations for different command buffers from that larger allocation, they don't have to lock their own individual mutex, since it's forbidden for two threads to call into the same pool. So you get faster performance.

Think of a command pool as the _memory allocator_ for a set of command buffers. It is what does the allocation for all of the data they internally use. Note that vkAllocateCommandBuffers does not take user-defined allocators; it uses the allocators set into the pool.

Note also that the command pool flags are principly about what kind of usage pattern you'll be using. Will your command buffers be reused or are they transient? Will you be resetting command buffers individually, or will you reset them all in one go? These change how the implementation will go about the task of allocating memory. If you're resetting command buffers individually, then the implementation probably should segregate the memory for those individual buffers from one another. Whereas if you're resetting the whole pool, then it's fine for it to sub-allocate different portions of the same allocation to different CBs.

Maybe a more descriptive name would have been "command memory pool", but the current name is adequate.

Thanks, that clears it up. And apologies for asking off-topic questions here, I wasn't sure what a more appropriate place would be.

Was this page helpful?
0 / 5 - 0 ratings