-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
add_custom_target(LibomptUnitTests) | ||
set_target_properties(LibomptUnitTests PROPERTIES FOLDER "Tests/UnitTests") | ||
|
||
function(add_libompt_unittest test_dirname) | ||
add_unittest(LibomptUnitTests ${test_dirname} ${ARGN}) | ||
endfunction() | ||
|
||
add_subdirectory(Plugins) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
set(PLUGINS_TEST_COMMON omptarget OMPT omptarget.devicertl) | ||
set(PLUGINS_TEST_SOURCES NextgenPluginsTest.cpp) | ||
set(PLUGINS_TEST_INCLUDE ${LIBOMPTARGET_INCLUDE_DIR}) | ||
|
||
foreach(PLUGIN IN LISTS LIBOMPTARGET_TESTED_PLUGINS) | ||
libomptarget_say("Building plugin unit tests for ${PLUGIN}") | ||
add_libompt_unittest("${PLUGIN}.unittests" ${PLUGINS_TEST_SOURCES}) | ||
add_dependencies("${PLUGIN}.unittests" ${PLUGINS_TEST_COMMON} ${PLUGIN}) | ||
target_link_libraries("${PLUGIN}.unittests" PRIVATE ${PLUGINS_TEST_COMMON} ${PLUGIN}) | ||
target_include_directories("${PLUGIN}.unittests" PRIVATE ${PLUGINS_TEST_INCLUDE}) | ||
endforeach() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
//===------- unittests/Plugins/NextgenPluginsTest.cpp - Plugin tests ------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "Shared/PluginAPI.h" | ||
jdoerfert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#include "omptarget.h" | ||
#include "gtest/gtest.h" | ||
|
||
#include <unordered_set> | ||
|
||
const int DEVICE_ID = 0; | ||
std::unordered_set<int> setup_map; | ||
|
||
int init_test_device(int ID) { | ||
if (setup_map.find(ID) != setup_map.end()) { | ||
return OFFLOAD_SUCCESS; | ||
} | ||
if (__tgt_rtl_init_plugin() == OFFLOAD_FAIL || | ||
__tgt_rtl_init_device(ID) == OFFLOAD_FAIL) { | ||
return OFFLOAD_FAIL; | ||
} | ||
setup_map.insert(ID); | ||
return OFFLOAD_SUCCESS; | ||
} | ||
|
||
// Test plugin initialization | ||
TEST(NextgenPluginsTest, PluginInit) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense, thanks for explaining. |
||
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)); | ||
|
||
// Check if device supports async | ||
// Platforms like x86_64 don't support it | ||
if (__tgt_rtl_init_async_info(DEVICE_ID, &info) == OFFLOAD_SUCCESS) { | ||
// 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); | ||
|
||
// Look for compatible device | ||
int DEVICE_TWO = -1; | ||
for (int i = 1; i < __tgt_rtl_number_of_devices(); i++) { | ||
if (__tgt_rtl_is_data_exchangable(DEVICE_ID, i)) { | ||
DEVICE_TWO = i; | ||
break; | ||
} | ||
} | ||
|
||
// Only run test if we have multiple GPUs to test | ||
// GPUs must be compatible for test to work | ||
if (DEVICE_TWO >= 1) { | ||
// 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.
lld just does this:
llvm_add_unittests(LLD_UNITTESTS_ADDED)
See
llvm-project/lld/CMakeLists.txt
Line 195 in 3942749
and
https://github.com/llvm/llvm-project/blob/394274965a119973612c25e0eaf299c8954cce94/llvm/cmake/modules/AddLLVM.cmake#L2497C2-L2497C2
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.