Skip to content

Commit 3a5cb62

Browse files
authored
vulkan: async and event fixes (#20518)
* vulkan: fix event wait submission, event command buffer reset * fix event command buffer reset validation error * also reset command buffers before reuse * use timeline semaphores instead of fences for event_synchronize * don't use initializer list for semaphore wait info * use multiple events to avoid reset issues * fix event reuse issue with multiple vectors * add semaphore wait condition also if compute_ctx already exists * remove event pending stage
1 parent 8cc2d81 commit 3a5cb62

1 file changed

Lines changed: 95 additions & 33 deletions

File tree

ggml/src/ggml-vulkan/ggml-vulkan.cpp

Lines changed: 95 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ struct vk_queue;
191191

192192
struct vk_command_buffer {
193193
vk::CommandBuffer buf;
194+
uint64_t use_counter = 0;
194195
bool in_use = false;
195196
};
196197

@@ -938,19 +939,24 @@ struct vk_subbuffer {
938939
}
939940
};
940941

941-
// vk_event is used for the event-related backend interfaces. It uses 'event' for
942-
// event_wait and 'fence' for event_synchronize. Polling on an event for
942+
struct vk_semaphore {
943+
vk::Semaphore s;
944+
uint64_t value;
945+
};
946+
947+
// vk_event is used for the event-related backend interfaces. It uses vk::Events for
948+
// event_wait and a timeline semaphore for event_synchronize. Polling on an event for
943949
// event_synchronize wouldn't be sufficient to wait for command buffers to complete,
944950
// and would lead to validation errors.
945951
struct vk_event {
952+
std::vector<vk::Event> events_free; // Events available for reuse
953+
std::vector<vk::Event> events_submitted; // Events that are fully submitted and can be reused on next synchronize
946954
vk::Event event;
947-
vk::Fence fence;
948-
vk_command_buffer* cmd_buffer = nullptr;
949-
};
955+
bool has_event;
950956

951-
struct vk_semaphore {
952-
vk::Semaphore s;
953-
uint64_t value;
957+
vk_semaphore tl_semaphore;
958+
vk_command_buffer* cmd_buffer = nullptr;
959+
uint64_t cmd_buffer_use_counter = 0;
954960
};
955961

956962
struct vk_submission {
@@ -2319,7 +2325,7 @@ static vk_command_buffer* ggml_vk_create_cmd_buffer(vk_device& device, vk_comman
23192325
vk::CommandBufferLevel::ePrimary,
23202326
1);
23212327
const std::vector<vk::CommandBuffer> cmd_buffers = device->device.allocateCommandBuffers(command_buffer_alloc_info);
2322-
p.cmd_buffers.push_back({ cmd_buffers.front(), true });
2328+
p.cmd_buffers.push_back({ cmd_buffers.front(), 0, true });
23232329
return &p.cmd_buffers[p.cmd_buffers.size()-1];
23242330
}
23252331

@@ -2788,6 +2794,15 @@ static void ggml_vk_sync_buffers(ggml_backend_vk_context* ctx, vk_context& subct
27882794
);
27892795
}
27902796

2797+
static void ggml_vk_reset_event(vk_context& ctx, vk::Event& event) {
2798+
VK_LOG_DEBUG("ggml_vk_set_event()");
2799+
2800+
ctx->s->buffer->buf.resetEvent(
2801+
event,
2802+
ctx->p->q->stage_flags
2803+
);
2804+
}
2805+
27912806
static void ggml_vk_set_event(vk_context& ctx, vk::Event& event) {
27922807
VK_LOG_DEBUG("ggml_vk_set_event()");
27932808

@@ -6396,6 +6411,7 @@ static vk_subbuffer ggml_vk_tensor_subbuffer(
63966411
static vk_command_buffer* ggml_vk_get_or_create_cmd_buffer(vk_device& device, vk_command_pool& pool) {
63976412
for (auto& cmd_buffer : pool.cmd_buffers) {
63986413
if (!cmd_buffer.in_use) {
6414+
cmd_buffer.use_counter++;
63996415
cmd_buffer.in_use = true;
64006416
return &cmd_buffer;
64016417
}
@@ -6500,14 +6516,15 @@ static void ggml_vk_ctx_begin(vk_device& device, vk_context& subctx) {
65006516
}
65016517

65026518
static vk_context ggml_vk_get_compute_ctx(ggml_backend_vk_context * ctx) {
6519+
vk_context result;
65036520
if (!ctx->compute_ctx.expired()) {
6504-
return ctx->compute_ctx.lock();
6505-
}
6506-
6507-
vk_context result = ggml_vk_create_context(ctx, ctx->compute_cmd_pool);
6521+
result = ctx->compute_ctx.lock();
6522+
} else {
6523+
result = ggml_vk_create_context(ctx, ctx->compute_cmd_pool);
65086524

6509-
ctx->compute_ctx = result;
6510-
ggml_vk_ctx_begin(ctx->device, result);
6525+
ctx->compute_ctx = result;
6526+
ggml_vk_ctx_begin(ctx->device, result);
6527+
}
65116528

65126529
if (ctx->device->async_use_transfer_queue && ctx->transfer_semaphore_last_submitted < ctx->transfer_semaphore.value) {
65136530
result->s->wait_semaphores.push_back(ctx->transfer_semaphore);
@@ -13801,6 +13818,7 @@ static void ggml_vk_synchronize(ggml_backend_vk_context * ctx) {
1380113818
ctx->submit_pending = false;
1380213819
if (cmd_buf) {
1380313820
cmd_buf->in_use = false;
13821+
cmd_buf->buf.reset();
1380413822
}
1380513823
}
1380613824

@@ -14862,18 +14880,31 @@ static void ggml_backend_vk_event_record(ggml_backend_t backend, ggml_backend_ev
1486214880
vk_context compute_ctx = ggml_vk_get_compute_ctx(ctx);
1486314881
auto* cmd_buf = compute_ctx->s->buffer; // retrieve pointer before it gets reset
1486414882

14865-
// the backend interface doesn't have an explicit reset, so reset it here
14866-
// before we record the command to set it
14867-
ctx->device->device.resetEvent(vkev->event);
14868-
ctx->device->device.resetFences({ vkev->fence });
14883+
if (vkev->has_event) {
14884+
// Move existing event into submitted
14885+
vkev->events_submitted.push_back(vkev->event);
14886+
}
14887+
14888+
// Grab the next event and record it, create one if necessary
14889+
if (vkev->events_free.empty()) {
14890+
vkev->event = ctx->device->device.createEvent({});
14891+
} else {
14892+
vkev->event = vkev->events_free.back();
14893+
vkev->events_free.pop_back();
14894+
}
14895+
14896+
vkev->has_event = true;
1486914897

1487014898
ggml_vk_set_event(compute_ctx, vkev->event);
1487114899

14900+
vkev->tl_semaphore.value++;
14901+
compute_ctx->s->signal_semaphores.push_back(vkev->tl_semaphore);
1487214902
ggml_vk_ctx_end(compute_ctx);
1487314903

14874-
ggml_vk_submit(compute_ctx, {vkev->fence});
14904+
ggml_vk_submit(compute_ctx, {});
1487514905
ctx->submit_pending = true;
1487614906
vkev->cmd_buffer = cmd_buf;
14907+
vkev->cmd_buffer_use_counter = cmd_buf->use_counter;
1487714908
ctx->compute_ctx.reset();
1487814909
}
1487914910

@@ -14884,9 +14915,10 @@ static void ggml_backend_vk_event_wait(ggml_backend_t backend, ggml_backend_even
1488414915

1488514916
vk_context compute_ctx = ggml_vk_get_compute_ctx(ctx);
1488614917

14887-
ggml_vk_wait_events(compute_ctx, {vkev->event});
14888-
ggml_vk_ctx_end(compute_ctx);
14889-
ctx->compute_ctx.reset();
14918+
if (vkev->has_event) {
14919+
// Wait for latest event
14920+
ggml_vk_wait_events(compute_ctx, { vkev->event });
14921+
}
1489014922
}
1489114923

1489214924
// TODO: enable async and synchronize
@@ -15676,10 +15708,13 @@ static ggml_backend_event_t ggml_backend_vk_device_event_new(ggml_backend_dev_t
1567615708
return nullptr;
1567715709
}
1567815710

15679-
// The event/fence is expected to initially be in the signaled state.
15680-
vkev->event = device->device.createEvent({});
15681-
vkev->fence = device->device.createFence({vk::FenceCreateFlagBits::eSignaled});
15682-
device->device.setEvent(vkev->event);
15711+
// No events initially, they get created on demand
15712+
vkev->has_event = false;
15713+
15714+
vk::SemaphoreTypeCreateInfo tci{ vk::SemaphoreType::eTimeline, 0 };
15715+
vk::SemaphoreCreateInfo ci{};
15716+
ci.setPNext(&tci);
15717+
vkev->tl_semaphore = { device->device.createSemaphore(ci), 0 };
1568315718

1568415719
return new ggml_backend_event {
1568515720
/* .device = */ dev,
@@ -15693,8 +15728,16 @@ static void ggml_backend_vk_device_event_free(ggml_backend_dev_t dev, ggml_backe
1569315728

1569415729
vk_event *vkev = (vk_event *)event->context;
1569515730

15696-
device->device.destroyFence(vkev->fence);
15697-
device->device.destroyEvent(vkev->event);
15731+
device->device.destroySemaphore(vkev->tl_semaphore.s);
15732+
for (auto& event : vkev->events_free) {
15733+
device->device.destroyEvent(event);
15734+
}
15735+
for (auto& event : vkev->events_submitted) {
15736+
device->device.destroyEvent(event);
15737+
}
15738+
if (vkev->has_event) {
15739+
device->device.destroyEvent(vkev->event);
15740+
}
1569815741
delete vkev;
1569915742
delete event;
1570015743
}
@@ -15705,10 +15748,29 @@ static void ggml_backend_vk_device_event_synchronize(ggml_backend_dev_t dev, ggm
1570515748
auto device = ggml_vk_get_device(ctx->device);
1570615749
vk_event *vkev = (vk_event *)event->context;
1570715750

15708-
VK_CHECK(device->device.waitForFences({ vkev->fence }, true, UINT64_MAX), "event_synchronize");
15709-
// Finished using current command buffer so we flag for reuse
15710-
if (vkev->cmd_buffer) {
15711-
vkev->cmd_buffer->in_use = false;
15751+
// Only do something if the event has actually been used
15752+
if (vkev->has_event) {
15753+
vk::Semaphore sem = vkev->tl_semaphore.s;
15754+
uint64_t val = vkev->tl_semaphore.value;
15755+
vk::SemaphoreWaitInfo swi{vk::SemaphoreWaitFlags{}, sem, val};
15756+
VK_CHECK(device->device.waitSemaphores(swi, UINT64_MAX), "event_synchronize");
15757+
15758+
// Reset and move submitted events
15759+
for (auto& event : vkev->events_submitted) {
15760+
device->device.resetEvent(event);
15761+
}
15762+
vkev->events_free.insert(vkev->events_free.end(), vkev->events_submitted.begin(), vkev->events_submitted.end());
15763+
vkev->events_submitted.clear();
15764+
15765+
// Finished using current command buffer so we flag for reuse
15766+
if (vkev->cmd_buffer) {
15767+
// Only flag for reuse if it hasn't been reused already
15768+
if (vkev->cmd_buffer_use_counter == vkev->cmd_buffer->use_counter) {
15769+
vkev->cmd_buffer->in_use = false;
15770+
vkev->cmd_buffer->buf.reset();
15771+
}
15772+
vkev->cmd_buffer = nullptr;
15773+
}
1571215774
}
1571315775
}
1571415776

0 commit comments

Comments
 (0)