[llvm] [offload] add tests for event synchronization (PR #193927)
Kevin Sala Penades via llvm-commits
llvm-commits at lists.llvm.org
Mon May 4 10:02:46 PDT 2026
================
@@ -240,6 +248,115 @@ TEST_P(olLaunchKernelLocalMemStaticTest, Success) {
ASSERT_SUCCESS(olMemFree(Mem));
}
+// The test intends to verify the correctness of the current implementation of
+// the event synchronisation.
+TEST_P(olLaunchKernelSingleCounterSyncEventTest, SuccessSyncEvent) {
+ void* InitValuePassed;
+ void *ResNum;
+
+ size_t Size = sizeof(uint32_t);
+
+ ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &InitValuePassed));
+ ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &ResNum));
+
+ uint32_t HostInitVal = 0;
+ ASSERT_SUCCESS(olMemcpy(Queue, InitValuePassed, Device, &HostInitVal, Host, Size));
+ ASSERT_SUCCESS(olMemcpy(Queue, ResNum, Device, &HostInitVal, Host, Size));
+ ASSERT_SUCCESS(olSyncQueue(Queue));
+
+ // The execution time of the provided kernel should be high enough to ensure
+ // that the read value of the final result is not correct without explicit
+ // synchronization: explicit waiting for the event following the submitted
+ // operation. LoopRange is arbitrarily set with the goal of prolonging the
+ // kernel execution through a high number of loop iterations. In each
+ // iteration, NumberToAdd is added to the final sum, which is stored in
+ // ResNum.
+ int32_t LoopRange = 1000000;
+ int32_t NumberToAdd = 2;
+
+ ArgsSingleCounter Args{LoopRange, NumberToAdd, (uint32_t *)InitValuePassed,
+ (uint32_t *)ResNum};
+
+ ASSERT_SUCCESS(
+ olLaunchKernel(Queue, Device, Kernel, &Args, sizeof(Args), &LaunchArgs));
+
+ uint32_t FinalResVal = 0;
+ ASSERT_SUCCESS(olMemcpy(Queue, &FinalResVal, Host, ResNum, Device, Size));
+
+ ol_event_handle_t Event = nullptr;
+ ASSERT_SUCCESS(olCreateEvent(Queue, &Event));
+ ASSERT_SUCCESS(olSyncEvent(Event));
+
+ ASSERT_EQ(FinalResVal, NumberToAdd * LoopRange);
+
+ ASSERT_SUCCESS(olMemFree(InitValuePassed));
+ ASSERT_SUCCESS(olMemFree(ResNum));
+}
+
+// The test checks the correctness of the synchronization between queues using
+// events. Enqueueing the kernel on the queue `Q1` should produce `Result1`,
+// which is used as an initial value for the queue `Q2`. Therefore, before
+// executing any future work, `Q2` should wait for the event `Event1`, which is
+// created after submitting work on `Q1`. The required synchronization is
+// ensured by `olWaitEvents(Q2, &Event1, 1)`. If `Q2` uses the value passed as
+// `Result1` before the work on `Q1` has completed, the result of the kernel
+// enqueued on `Q2` would be incorrect.
+
+TEST_P(olLaunchKernelSingleCounterSyncEventTest, SuccessTwoQueues) {
+ void* InitValuePassed;
+ void *ResNum;
+ void *ResNum2;
+
+ size_t Size = sizeof(uint32_t);
+
+ ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &InitValuePassed));
+ ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &ResNum));
+ ASSERT_SUCCESS(olMemAlloc(Device, OL_ALLOC_TYPE_DEVICE, Size, &ResNum2));
+
+ uint32_t HostInitVal = 0;
+ ASSERT_SUCCESS(olMemcpy(Queue, InitValuePassed, Device, &HostInitVal, Host, Size));
+ ASSERT_SUCCESS(olMemcpy(Queue, ResNum, Device, &HostInitVal, Host, Size));
+ ASSERT_SUCCESS(olMemcpy(Queue, ResNum2, Device, &HostInitVal, Host, Size));
+ ASSERT_SUCCESS(olSyncQueue(Queue));
+
+ ol_queue_handle_t Queue2 = nullptr;
+ ASSERT_SUCCESS(olCreateQueue(Device, &Queue2));
+
+ // For the explanation of the reasoning behind particular values assigned to
+ // parameters, see the comment in the Success test from the same test suite
+ int32_t LoopRange = 1000000;
+ int32_t NumberToAdd = 2;
+
+ ArgsSingleCounter Args{LoopRange, NumberToAdd, (uint32_t *)InitValuePassed,
+ (uint32_t *)ResNum};
+ ASSERT_SUCCESS(
+ olLaunchKernel(Queue, Device, Kernel, &Args, sizeof(Args), &LaunchArgs));
+
+ ol_event_handle_t Event = nullptr;
+ ASSERT_SUCCESS(olCreateEvent(Queue, &Event));
+ ASSERT_SUCCESS(olWaitEvents(Queue2, &Event, 1));
+ ArgsSingleCounter Args2{LoopRange, NumberToAdd, (uint32_t *)ResNum,
+ (uint32_t *)ResNum2};
+
+ // At the beginning of the kernel, ResNum from the first queue is saved
+ // locally as the initial value. If operations enqueued before Event have not
+ // been completed by the time the kernel is executed using the second queue,
+ // the FinalResVal would be incorrect.
+ ASSERT_SUCCESS(olLaunchKernel(Queue2, Device, Kernel, &Args2, sizeof(Args2),
+ &LaunchArgs));
+
+ ASSERT_SUCCESS(olSyncQueue(Queue2));
+ uint32_t FinalResVal = 0;
+ ASSERT_SUCCESS(olMemcpy(Queue, &FinalResVal, Host, ResNum2, Device, Size));
+ ASSERT_SUCCESS(olSyncQueue(Queue2));
----------------
kevinsala wrote:
The queue for the memcpy and the sync are not the same.
https://github.com/llvm/llvm-project/pull/193927
More information about the llvm-commits
mailing list