Skip to content

Commit e88b598

Browse files
committed
GPU Metal: run the work of a stream in order
Consecutive command buffers of one Metal queue are only ordered as far as the hazard tracking of the bound buffers sees, and the kernels reach most memory through addresses stored in constant memory. A kernel could thus start before the one before it on the same stream had finished: on a 5-event pp time frame the cluster finder found no clusters, and between 12,125 and 12,142 instead of 19,009 with every kernel synchronised. Make each command buffer of a stream wait on a per-stream event that the previous one signals, which gives a CUDA stream's in-order semantics without blocking the host.
1 parent 16b92a1 commit e88b598

2 files changed

Lines changed: 12 additions & 0 deletions

File tree

‎GPU/GPUTracking/Base/metal/GPUReconstructionMetal.mm‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@
105105
if (mInternals->commandBuffers[i] == nil) {
106106
GPUErrorReturn("Error creating Metal command buffer");
107107
}
108+
mInternals->streamOrder[i] = [mInternals->device newSharedEvent];
109+
mInternals->streamOrderValue[i] = 0;
108110
}
109111

110112
mInternals->mem_host = [mInternals->device newBufferWithLength:mHostMemorySize options:MTLResourceStorageModeShared];
@@ -179,6 +181,7 @@
179181
DrainStream(i);
180182
[mInternals->commandBuffers[i] release];
181183
[mInternals->commandQueues[i] release];
184+
[mInternals->streamOrder[i] release];
182185
}
183186
[mInternals->mem_host release];
184187
}
@@ -323,11 +326,17 @@ MetalMemRef metalResolve(const GPUReconstructionMetalInternals* internals, const
323326
void GPUReconstructionMetal::CommitStream(int32_t stream)
324327
{
325328
id<MTLCommandBuffer> cb = mInternals->commandBuffers[stream];
329+
// A CUDA stream runs its work in order, a Metal queue only as far as its
330+
// hazard tracking sees, and the kernels reach most memory through addresses
331+
// in constant memory. So each buffer of a stream waits for the previous one.
332+
const uint64_t value = ++mInternals->streamOrderValue[stream];
333+
[cb encodeSignalEvent:mInternals->streamOrder[stream] value:value];
326334
[cb commit];
327335
// Hand the committed buffer over to the stream's submission list, which owns
328336
// it until it is waited for, and open a fresh one to encode the next work in.
329337
mInternals->submittedBuffers[stream].emplace_back(cb);
330338
mInternals->commandBuffers[stream] = [[mInternals->commandQueues[stream] commandBuffer] retain];
339+
[mInternals->commandBuffers[stream] encodeWaitForEvent:mInternals->streamOrder[stream] value:value];
331340
}
332341

333342
void GPUReconstructionMetal::DrainStream(int32_t stream)

‎GPU/GPUTracking/Base/metal/GPUReconstructionMetalIncludesHost.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ struct GPUReconstructionMetalInternals {
5959
// encoded into afterwards, so the two cannot be the same object.
6060
std::array<id<MTLCommandBuffer>, constants::GPU_MAX_STREAMS> commandBuffers;
6161
std::array<std::vector<id<MTLCommandBuffer>>, constants::GPU_MAX_STREAMS> submittedBuffers;
62+
// Signalled by each committed buffer and waited for by the next one, see CommitStream
63+
std::array<id<MTLSharedEvent>, constants::GPU_MAX_STREAMS> streamOrder;
64+
std::array<uint64_t, constants::GPU_MAX_STREAMS> streamOrderValue;
6265

6366
std::vector<id<MTLFunction>> functions;
6467
std::vector<id<MTLComputePipelineState>> pipelines;

0 commit comments

Comments
 (0)