-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[OpenMP] Add unit tests for nextgen plugins #74398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-openmp Author: Ethan Luis McDonough (EthanLuisMcDonough) ChangesThis patch add three GTest unit tests that test plugin read and write operations. Tests can be compiled with Full diff: https://github.com/llvm/llvm-project/pull/74398.diff 4 Files Affected:
diff --git a/openmp/libomptarget/CMakeLists.txt b/openmp/libomptarget/CMakeLists.txt
index 5f592f46c8ffa..6a474469ab87a 100644
--- a/openmp/libomptarget/CMakeLists.txt
+++ b/openmp/libomptarget/CMakeLists.txt
@@ -123,3 +123,9 @@ add_subdirectory(tools)
# Add tests.
add_subdirectory(test)
+
+# Add unit tests if GMock/GTest is present
+if (EXISTS ${LLVM_THIRD_PARTY_DIR}/unittest)
+ add_subdirectory(${LLVM_THIRD_PARTY_DIR}/unittest ${CMAKE_CURRENT_BINARY_DIR}/third-party/unittest)
+ add_subdirectory(unittests)
+endif()
diff --git a/openmp/libomptarget/unittests/CMakeLists.txt b/openmp/libomptarget/unittests/CMakeLists.txt
new file mode 100644
index 0000000000000..0a29afd68569a
--- /dev/null
+++ b/openmp/libomptarget/unittests/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_custom_target(LibomptUnitTests)
+set_target_properties(LibomptUnitTests PROPERTIES FOLDER "Libomptarget tests")
+
+function(add_libompt_unittest test_dirname)
+ add_unittest(LibomptUnitTests ${test_dirname} ${ARGN})
+endfunction()
+
+add_subdirectory(Plugins)
diff --git a/openmp/libomptarget/unittests/Plugins/CMakeLists.txt b/openmp/libomptarget/unittests/Plugins/CMakeLists.txt
new file mode 100644
index 0000000000000..234ba699c4834
--- /dev/null
+++ b/openmp/libomptarget/unittests/Plugins/CMakeLists.txt
@@ -0,0 +1,26 @@
+set(LIBOMPTARGET_BUILD_AMDGPU_PLUGIN TRUE CACHE BOOL
+ "Whether to build AMDGPU plugin")
+set(LIBOMPTARGET_BUILD_CUDA_PLUGIN TRUE CACHE BOOL
+ "Whether to build CUDA plugin")
+
+set(PLUGINS_TEST_COMMON omptarget OMPT omptarget.devicertl)
+set(PLUGINS_TEST_SOURCES NextgenPluginsTest.cpp)
+set(PLUGINS_TEST_INCLUDE ${LIBOMPTARGET_INCLUDE_DIR})
+
+if (LIBOMPTARGET_BUILD_AMDGPU_PLUGIN)
+ libomptarget_say("Building plugin unit tests for AMDGPU")
+ add_libompt_unittest(PluginsTestAmd ${PLUGINS_TEST_SOURCES})
+ add_dependencies(PluginsTestAmd ${PLUGINS_TEST_COMMON} omptarget.rtl.amdgpu)
+ target_link_libraries(PluginsTestAmd PRIVATE ${PLUGINS_TEST_COMMON} omptarget.rtl.amdgpu)
+ target_include_directories(PluginsTestAmd PRIVATE ${PLUGINS_TEST_INCLUDE})
+else()
+ libomptarget_say("Skipping AMDGPU plugin unit tests")
+endif()
+
+if (LIBOMPTARGET_BUILD_CUDA_PLUGIN)
+ libomptarget_say("Building plugin unit tests for Cuda")
+ add_libompt_unittest(PluginsTestCu ${PLUGINS_TEST_SOURCES})
+ add_dependencies(PluginsTestCu ${PLUGINS_TEST_COMMON} omptarget.rtl.cuda)
+ target_link_libraries(PluginsTestCu PRIVATE ${PLUGINS_TEST_COMMON} omptarget.rtl.cuda)
+ target_include_directories(PluginsTestCu PRIVATE ${PLUGINS_TEST_INCLUDE})
+endif()
diff --git a/openmp/libomptarget/unittests/Plugins/NextgenPluginsTest.cpp b/openmp/libomptarget/unittests/Plugins/NextgenPluginsTest.cpp
new file mode 100644
index 0000000000000..24311076e8acd
--- /dev/null
+++ b/openmp/libomptarget/unittests/Plugins/NextgenPluginsTest.cpp
@@ -0,0 +1,146 @@
+#include "Shared/PluginAPI.h"
+#include "omptarget.h"
+#include "gtest/gtest.h"
+
+const int DEVICE_ID = 0, DEVICE_TWO = 1;
+bool setup_map[DEVICE_TWO + 1];
+
+int init_test_device(int ID) {
+ if (setup_map[ID]) {
+ return OFFLOAD_SUCCESS;
+ }
+ if (__tgt_rtl_init_plugin() == OFFLOAD_FAIL ||
+ __tgt_rtl_init_device(ID) == OFFLOAD_FAIL) {
+ return OFFLOAD_FAIL;
+ }
+ setup_map[ID] = true;
+ return OFFLOAD_SUCCESS;
+}
+
+// Test plugin initialization
+TEST(NextgenPluginsTest, PluginInit) {
+ EXPECT_EQ(OFFLOAD_SUCCESS, init_test_device(DEVICE_ID));
+}
+
+// Test GPU allocation and R/W
+TEST(NextgenPluginsTest, PluginAlloc) {
+ int32_t test_value = 23;
+ int32_t host_value = -1;
+ int64_t var_size = sizeof(int32_t);
+
+ // Init plugin and device
+ EXPECT_EQ(OFFLOAD_SUCCESS, init_test_device(DEVICE_ID));
+
+ // Allocate memory
+ void *device_ptr =
+ __tgt_rtl_data_alloc(DEVICE_ID, var_size, nullptr, TARGET_ALLOC_DEFAULT);
+
+ // Check that the result is not null
+ EXPECT_NE(device_ptr, nullptr);
+
+ // Submit data to device
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_data_submit(DEVICE_ID, device_ptr,
+ &test_value, var_size));
+
+ // Read data from device
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_data_retrieve(DEVICE_ID, &host_value,
+ device_ptr, var_size));
+
+ // Compare values
+ EXPECT_EQ(host_value, test_value);
+
+ // Cleanup data
+ EXPECT_EQ(OFFLOAD_SUCCESS,
+ __tgt_rtl_data_delete(DEVICE_ID, device_ptr, TARGET_ALLOC_DEFAULT));
+}
+
+// Test async GPU allocation and R/W
+TEST(NextgenPluginsTest, PluginAsyncAlloc) {
+ int32_t test_value = 47;
+ int32_t host_value = -1;
+ int64_t var_size = sizeof(int32_t);
+ __tgt_async_info info;
+
+ // Init plugin and device
+ EXPECT_EQ(OFFLOAD_SUCCESS, init_test_device(DEVICE_ID));
+
+ // Allocate memory
+ void *device_ptr =
+ __tgt_rtl_data_alloc(DEVICE_ID, var_size, nullptr, TARGET_ALLOC_DEFAULT);
+
+ // Check that the result is not null
+ EXPECT_NE(device_ptr, nullptr);
+
+ // Submit data to device asynchronously
+ EXPECT_EQ(OFFLOAD_SUCCESS,
+ __tgt_rtl_data_submit_async(DEVICE_ID, device_ptr, &test_value,
+ var_size, &info));
+
+ // Wait for async request to process
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_synchronize(DEVICE_ID, &info));
+
+ // Read data from device
+ EXPECT_EQ(OFFLOAD_SUCCESS,
+ __tgt_rtl_data_retrieve_async(DEVICE_ID, &host_value, device_ptr,
+ var_size, &info));
+
+ // Wait for async request to process
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_synchronize(DEVICE_ID, &info));
+
+ // Compare values
+ EXPECT_EQ(host_value, test_value);
+
+ // Cleanup data
+ EXPECT_EQ(OFFLOAD_SUCCESS,
+ __tgt_rtl_data_delete(DEVICE_ID, device_ptr, TARGET_ALLOC_DEFAULT));
+}
+
+// Test GPU data exchange
+TEST(NextgenPluginsTest, PluginDataSwap) {
+ int32_t test_value = 23;
+ int32_t host_value = -1;
+ int64_t var_size = sizeof(int32_t);
+
+ // Only run test if we have multiple GPUs to test
+ // GPUs must be compatible for test to work
+ if (__tgt_rtl_number_of_devices() > 1 &&
+ __tgt_rtl_is_data_exchangable(DEVICE_ID, DEVICE_TWO)) {
+ // Init both GPUs
+ EXPECT_EQ(OFFLOAD_SUCCESS, init_test_device(DEVICE_ID));
+ EXPECT_EQ(OFFLOAD_SUCCESS, init_test_device(DEVICE_TWO));
+
+ // Allocate memory on both GPUs
+ // DEVICE_ID will be the source
+ // DEVICE_TWO will be the destination
+ void *source_ptr = __tgt_rtl_data_alloc(DEVICE_ID, var_size, nullptr,
+ TARGET_ALLOC_DEFAULT);
+ void *dest_ptr = __tgt_rtl_data_alloc(DEVICE_TWO, var_size, nullptr,
+ TARGET_ALLOC_DEFAULT);
+
+ // Check for success in allocation
+ EXPECT_NE(source_ptr, nullptr);
+ EXPECT_NE(dest_ptr, nullptr);
+
+ // Write data to source
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_data_submit(DEVICE_ID, source_ptr,
+ &test_value, var_size));
+
+ // Transfer data between devices
+ EXPECT_EQ(OFFLOAD_SUCCESS,
+ __tgt_rtl_data_exchange(DEVICE_ID, source_ptr, DEVICE_TWO,
+ dest_ptr, var_size));
+
+ // Read from destination device (DEVICE_TWO) memory
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_data_retrieve(DEVICE_TWO, &host_value,
+ dest_ptr, var_size));
+
+ // Ensure match
+ EXPECT_EQ(host_value, test_value);
+
+ // Cleanup
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_data_delete(DEVICE_ID, source_ptr,
+ TARGET_ALLOC_DEFAULT));
+ EXPECT_EQ(OFFLOAD_SUCCESS, __tgt_rtl_data_delete(DEVICE_TWO, dest_ptr,
+ TARGET_ALLOC_DEFAULT));
+ }
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I've been wanting the ability to test stuff without relying on OpenMP codegen for awhile.
@@ -0,0 +1,8 @@ | |||
add_custom_target(LibomptUnitTests) | |||
set_target_properties(LibomptUnitTests PROPERTIES FOLDER "Libomptarget tests") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set_target_properties(LibomptUnitTests PROPERTIES FOLDER "Libomptarget tests") | |
set_target_properties(LibomptUnitTests PROPERTIES FOLDER "Tests/UnitTests") |
This is a weird folder name.
set(PLUGINS_TEST_SOURCES NextgenPluginsTest.cpp) | ||
set(PLUGINS_TEST_INCLUDE ${LIBOMPTARGET_INCLUDE_DIR}) | ||
|
||
if (LIBOMPTARGET_BUILD_AMDGPU_PLUGIN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (LIBOMPTARGET_BUILD_AMDGPU_PLUGIN) | |
if(TARGET omptarget.rtl.amdgpu) |
We should be able to just check the existence of the target so long as we add the unit tests after the main source.
set(LIBOMPTARGET_BUILD_AMDGPU_PLUGIN TRUE CACHE BOOL | ||
"Whether to build AMDGPU plugin") | ||
set(LIBOMPTARGET_BUILD_CUDA_PLUGIN TRUE CACHE BOOL | ||
"Whether to build CUDA plugin") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set(LIBOMPTARGET_BUILD_AMDGPU_PLUGIN TRUE CACHE BOOL | |
"Whether to build AMDGPU plugin") | |
set(LIBOMPTARGET_BUILD_CUDA_PLUGIN TRUE CACHE BOOL | |
"Whether to build CUDA plugin") |
Aren't these already declared somewhere else?
add_dependencies(PluginsTestCu ${PLUGINS_TEST_COMMON} omptarget.rtl.cuda) | ||
target_link_libraries(PluginsTestCu PRIVATE ${PLUGINS_TEST_COMMON} omptarget.rtl.cuda) | ||
target_include_directories(PluginsTestCu PRIVATE ${PLUGINS_TEST_INCLUDE}) | ||
endif() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realistically this should be a loop over all the targets, just have the plugin append some CMake list of enabled targets, then we can just iterate it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I think after Joseph's comments are addressed and the licence is in, we can merge this.
#include "omptarget.h" | ||
#include "gtest/gtest.h" | ||
|
||
const int DEVICE_ID = 0, DEVICE_TWO = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a follow up we should iterate over all devices, not hardcode 2.
} | ||
|
||
// Test plugin initialization | ||
TEST(NextgenPluginsTest, PluginInit) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for not using a test fixture for this instead of the global variables?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The global variables serve to keep track of which devices have been initialized. Devices can't be re-initialized, so I think the initialized devices would have to either be tracked through static class members or exist outside the test fixture to persist across tests. I don't think there's a way to check if a device has been initialized through the functions defined in PluginAPI.h, but I definitely could be overlooking something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, thanks for explaining.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with getting this in and adapting it in-tree.
@jplehr concerns?
if (EXISTS ${LLVM_THIRD_PARTY_DIR}/unittest) | ||
add_subdirectory(${LLVM_THIRD_PARTY_DIR}/unittest ${CMAKE_CURRENT_BINARY_DIR}/third-party/unittest) | ||
add_subdirectory(unittests) | ||
endif() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lld just does this:
llvm_add_unittests(LLD_UNITTESTS_ADDED)
See
llvm-project/lld/CMakeLists.txt
Line 195 in 3942749
llvm_add_unittests(LLD_UNITTESTS_ADDED) |
and
We should check if we can do the same, reducing the logic we carry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure that's a name defined in the same way, we could use the same name if needed.
We also need these to run w/ check-openmp
or similar. Long term I'd like language tests to be all all in a different directory so we can test things like CUDA, HIP, SYCL, OpenACC, C, C++, Fortran, etc.
No, I think that makes sense. |
Hi @EthanLuisMcDonough, @jdoerfert, do you know what might be causing this buildbot failure https://lab.llvm.org/buildbot/#/builders/270/builds/5927/steps/6/logs/stdio? |
looks like these changes break the llvm configuration when
and
https://lab.llvm.org/buildbot/#/builders/270/builds/5927/steps/6/logs/stdio |
This patch addresses an issue introduced in pull request #74398. CMake will attempt to re-build gtest if openmp is enabled as a project (as opposed to being enabled as a runtime). This patch adds a check that prevents this from happening.
I think #76141 should resolve this. I've my patch out locally, and it seems to fix the issue. I hope you all don't mind me immediately merging it; I didn't want to keep the buildbot failing. |
Thank you @EthanLuisMcDonough |
This patch add three GTest unit tests that test plugin read and write operations. Tests can be compiled with
ninja -C runtimes/runtimes-bins LibomptUnitTests
.