CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
440 lines
15 KiB
C++
440 lines
15 KiB
C++
// Copyright © Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "TestUtil.hpp"
|
|
#include "hipdnn_backend.h"
|
|
#include <filesystem>
|
|
#include <gtest/gtest.h>
|
|
#include <hipdnn_data_sdk/logging/Logger.hpp>
|
|
#include <hipdnn_data_sdk/utilities/PlatformUtils.hpp>
|
|
#include <hipdnn_flatbuffers_sdk/data_objects/graph_generated.h>
|
|
#include <hipdnn_test_sdk/utilities/FlatbufferGraphTestUtils.hpp>
|
|
#include <span>
|
|
#include <stdexcept>
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace test_util
|
|
{
|
|
|
|
void createTestHandle(hipdnnHandle_t* handle)
|
|
{
|
|
ASSERT_EQ(hipdnnCreate(handle), HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
|
|
void createTestGraph(hipdnnBackendDescriptor_t* descriptor, hipdnnHandle_t handle)
|
|
{
|
|
// Any valid graph with at least one node — the specific operation type doesn't matter
|
|
auto builder = hipdnn_test_sdk::utilities::createValidReductionGraph();
|
|
flatbuffers::DetachedBuffer serializedGraph = builder.Release();
|
|
|
|
ASSERT_EQ(hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
descriptor, serializedGraph.data(), serializedGraph.size()),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(*descriptor,
|
|
HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
|
|
void populateTestEngine(hipdnnBackendDescriptor_t engine,
|
|
hipdnnBackendDescriptor_t* graph,
|
|
hipdnnHandle_t handle,
|
|
int64_t gidx,
|
|
bool finalize)
|
|
{
|
|
if(*graph == nullptr)
|
|
{
|
|
createTestGraph(graph, handle);
|
|
}
|
|
|
|
ASSERT_EQ(hipdnnBackendFinalize(*graph), HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(engine,
|
|
HIPDNN_ATTR_ENGINE_OPERATION_GRAPH,
|
|
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
|
|
1,
|
|
static_cast<const void*>(graph)),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(
|
|
engine, HIPDNN_ATTR_ENGINE_GLOBAL_INDEX, HIPDNN_TYPE_INT64, 1, &gidx),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
|
|
if(finalize)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendFinalize(engine), HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void createTestEngine(hipdnnBackendDescriptor_t* engine,
|
|
hipdnnBackendDescriptor_t* graph,
|
|
hipdnnHandle_t handle,
|
|
int64_t gidx,
|
|
bool finalize)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINE_DESCRIPTOR, engine),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
populateTestEngine(*engine, graph, handle, gidx, finalize);
|
|
}
|
|
|
|
void populateTestEngineConfig(hipdnnBackendDescriptor_t* engineConfig,
|
|
hipdnnBackendDescriptor_t* engine,
|
|
hipdnnBackendDescriptor_t* graph,
|
|
hipdnnHandle_t handle,
|
|
int64_t gidx,
|
|
bool finalize)
|
|
{
|
|
if(*engine == nullptr)
|
|
{
|
|
createTestEngine(engine, graph, handle, gidx, true);
|
|
}
|
|
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(*engineConfig,
|
|
HIPDNN_ATTR_ENGINECFG_ENGINE,
|
|
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
|
|
1,
|
|
static_cast<const void*>(engine)),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
|
|
if(finalize)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendFinalize(*engineConfig), HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void createTestEngineConfig(hipdnnBackendDescriptor_t* engineConfig,
|
|
hipdnnBackendDescriptor_t* engine,
|
|
hipdnnBackendDescriptor_t* graph,
|
|
hipdnnHandle_t handle,
|
|
int64_t gidx,
|
|
bool finalize)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, engineConfig),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
populateTestEngineConfig(engineConfig, engine, graph, handle, gidx, finalize);
|
|
}
|
|
|
|
void populateTestExecutionPlan(hipdnnBackendDescriptor_t* executionPlan,
|
|
hipdnnBackendDescriptor_t* engineConfig,
|
|
hipdnnBackendDescriptor_t* engine,
|
|
hipdnnBackendDescriptor_t* graph,
|
|
hipdnnHandle_t handle,
|
|
int64_t gidx,
|
|
bool finalize)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(*executionPlan,
|
|
HIPDNN_ATTR_EXECUTION_PLAN_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
|
|
if(*engineConfig == nullptr)
|
|
{
|
|
createTestEngineConfig(engineConfig, engine, graph, handle, gidx, true);
|
|
}
|
|
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(*executionPlan,
|
|
HIPDNN_ATTR_EXECUTION_PLAN_ENGINE_CONFIG,
|
|
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
|
|
1,
|
|
static_cast<const void*>(engineConfig)),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
|
|
if(finalize)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendFinalize(*executionPlan), HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void* allocateTensorMemory([[maybe_unused]] const int64_t* dims,
|
|
[[maybe_unused]] size_t dimsCount,
|
|
[[maybe_unused]] hipdnnBackendAttributeType_t dataType,
|
|
[[maybe_unused]] bool initialize)
|
|
{
|
|
// TODO: Implement memory allocation logic based on the data type and dimensions
|
|
// For now, just return a dummy pointer
|
|
void* memory = malloc(0);
|
|
return memory;
|
|
}
|
|
|
|
void freeTensorMemory(void* dataPtr)
|
|
{
|
|
if(dataPtr != nullptr)
|
|
{
|
|
free(dataPtr);
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void setTensorMappingsInVariantPack(hipdnnBackendDescriptor_t variantPack,
|
|
const std::vector<int64_t>& tensorIds,
|
|
const std::vector<void*>& dataPtrs)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(variantPack,
|
|
HIPDNN_ATTR_VARIANT_PACK_UNIQUE_IDS,
|
|
HIPDNN_TYPE_INT64,
|
|
static_cast<int64_t>(tensorIds.size()),
|
|
tensorIds.data()),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(variantPack,
|
|
HIPDNN_ATTR_VARIANT_PACK_DATA_POINTERS,
|
|
HIPDNN_TYPE_VOID_PTR,
|
|
static_cast<int64_t>(dataPtrs.size()),
|
|
static_cast<const void*>(dataPtrs.data())),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
|
|
void setWorkspaceInVariantPack(hipdnnBackendDescriptor_t variantPack, void* workspace)
|
|
{
|
|
if(workspace != nullptr)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(variantPack,
|
|
HIPDNN_ATTR_VARIANT_PACK_WORKSPACE,
|
|
HIPDNN_TYPE_VOID_PTR,
|
|
1,
|
|
static_cast<const void*>(&workspace)),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
void finalizeVariantPack(hipdnnBackendDescriptor_t variantPack)
|
|
{
|
|
ASSERT_EQ(hipdnnBackendFinalize(variantPack), HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
|
|
void extractTensorMappings(const std::unordered_map<int64_t, void*>& dataPtrMappings,
|
|
std::vector<int64_t>& tensorIds,
|
|
std::vector<void*>& dataPtrs)
|
|
{
|
|
for(const auto& [id, dataPtr] : dataPtrMappings)
|
|
{
|
|
ASSERT_NE(dataPtr, nullptr);
|
|
tensorIds.push_back(id);
|
|
dataPtrs.push_back(dataPtr);
|
|
}
|
|
ASSERT_EQ(tensorIds.size(), dataPtrs.size());
|
|
ASSERT_FALSE(tensorIds.empty());
|
|
ASSERT_FALSE(dataPtrs.empty());
|
|
}
|
|
} // namespace
|
|
|
|
void populateVariantPackWithMappings(hipdnnBackendDescriptor_t variantPack,
|
|
const std::unordered_map<int64_t, void*>& dataPtrMappings,
|
|
void* workspace)
|
|
{
|
|
std::vector<int64_t> tensorIds;
|
|
std::vector<void*> dataPtrs;
|
|
|
|
extractTensorMappings(dataPtrMappings, tensorIds, dataPtrs);
|
|
setTensorMappingsInVariantPack(variantPack, tensorIds, dataPtrs);
|
|
setWorkspaceInVariantPack(variantPack, workspace);
|
|
finalizeVariantPack(variantPack);
|
|
}
|
|
|
|
void createAndInitializeBackendDescriptor(hipdnnBackendDescriptor_t* backendDescriptor,
|
|
const flatbuffers::DetachedBuffer& serializedGraph,
|
|
hipdnnHandle_t handle)
|
|
{
|
|
ASSERT_EQ(*backendDescriptor, nullptr);
|
|
|
|
auto status = hipdnnBackendCreateAndDeserializeGraph_ext(
|
|
backendDescriptor, serializedGraph.data(), serializedGraph.size());
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
|
|
ASSERT_EQ(hipdnnBackendSetAttribute(*backendDescriptor,
|
|
HIPDNN_ATTR_OPERATIONGRAPH_HANDLE,
|
|
HIPDNN_TYPE_HANDLE,
|
|
1,
|
|
static_cast<const void*>(&handle)),
|
|
HIPDNN_STATUS_SUCCESS);
|
|
|
|
status = hipdnnBackendFinalize(*backendDescriptor);
|
|
ASSERT_EQ(status, HIPDNN_STATUS_SUCCESS);
|
|
}
|
|
|
|
flatbuffers::FlatBufferBuilder createAndPopulateBatchnormNode()
|
|
{
|
|
return hipdnn_test_sdk::utilities::createValidBatchnormInferenceGraph();
|
|
}
|
|
|
|
void extractTensorInfoFromGraph(const flatbuffers::DetachedBuffer& serializedGraph,
|
|
std::unordered_map<int64_t, std::string>& uidToNameMap,
|
|
std::unordered_map<std::string, int64_t>& nameToUidMap,
|
|
std::unordered_map<int64_t, std::vector<int64_t>>& uidToDimsMap)
|
|
{
|
|
uidToNameMap.clear();
|
|
nameToUidMap.clear();
|
|
uidToDimsMap.clear();
|
|
|
|
auto deserializedGraph
|
|
= hipdnn_flatbuffers_sdk::data_objects::UnPackGraph(serializedGraph.data());
|
|
ASSERT_NE(deserializedGraph, nullptr);
|
|
|
|
// Extract all tensor information from the deserialized graph
|
|
for(const auto& tensor : deserializedGraph->tensors)
|
|
{
|
|
const int64_t uid = tensor->uid;
|
|
const std::string name = tensor->name;
|
|
|
|
uidToNameMap[uid] = name;
|
|
nameToUidMap[name] = uid;
|
|
|
|
if(!tensor->dims.empty())
|
|
{
|
|
uidToDimsMap[uid] = tensor->dims;
|
|
}
|
|
}
|
|
|
|
ASSERT_FALSE(uidToNameMap.empty());
|
|
}
|
|
|
|
std::vector<std::string> getLoadedPlugins(hipdnnHandle_t handle)
|
|
{
|
|
size_t numPlugins = 0;
|
|
size_t maxPathLength = 0;
|
|
auto status
|
|
= hipdnnGetLoadedEnginePluginPaths_ext(handle, &numPlugins, nullptr, &maxPathLength);
|
|
|
|
if(status != HIPDNN_STATUS_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to get loaded plugin paths");
|
|
}
|
|
|
|
if(numPlugins == 0)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
std::vector<std::vector<char>> pathBuffers(numPlugins, std::vector<char>(maxPathLength));
|
|
std::vector<char*> pluginPathsC(numPlugins);
|
|
for(size_t i = 0; i < numPlugins; ++i)
|
|
{
|
|
pluginPathsC[i] = pathBuffers[i].data();
|
|
}
|
|
|
|
status = hipdnnGetLoadedEnginePluginPaths_ext(
|
|
handle, &numPlugins, pluginPathsC.data(), &maxPathLength);
|
|
if(status != HIPDNN_STATUS_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to get loaded plugin paths");
|
|
}
|
|
|
|
std::vector<std::string> pluginPaths;
|
|
pluginPaths.reserve(numPlugins);
|
|
for(size_t i = 0; i < numPlugins; ++i)
|
|
{
|
|
pluginPaths.emplace_back(pluginPathsC[i]);
|
|
}
|
|
return pluginPaths;
|
|
}
|
|
|
|
static inline bool stemEq(const fs::path& pathFileName, const fs::path& suffixFileName)
|
|
{
|
|
using hipdnn_data_sdk::utilities::pathCompEq;
|
|
if(pathCompEq(pathFileName, suffixFileName))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// The full path should have an extension, but the suffix may not
|
|
return pathFileName.has_extension() && !suffixFileName.has_extension()
|
|
&& pathCompEq(pathFileName.stem(), suffixFileName);
|
|
}
|
|
|
|
static bool isPluginLoadedByRelativePathInternal(const fs::path& fullPath, const fs::path& suffix)
|
|
{
|
|
using hipdnn_data_sdk::utilities::pathCompEq;
|
|
|
|
const fs::path suffixNorm = suffix.lexically_normal();
|
|
const fs::path fullPathNorm = fullPath.lexically_normal();
|
|
|
|
if(suffixNorm.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for(const auto& c : suffixNorm)
|
|
{
|
|
if(c == "..")
|
|
{
|
|
return false; // Is unresolvable
|
|
}
|
|
}
|
|
|
|
std::vector<fs::path> suffixComps;
|
|
std::vector<fs::path> fullComps;
|
|
for(const auto& c : suffixNorm)
|
|
{
|
|
suffixComps.push_back(c);
|
|
}
|
|
for(const auto& c : fullPathNorm)
|
|
{
|
|
fullComps.push_back(c);
|
|
}
|
|
if(suffixComps.size() > fullComps.size())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto si = suffixComps.rbegin();
|
|
auto fi = fullComps.rbegin();
|
|
|
|
if(!stemEq(*si, *fi))
|
|
{
|
|
return false;
|
|
}
|
|
++si;
|
|
++fi;
|
|
|
|
for(; si != suffixComps.rend(); ++si, ++fi)
|
|
{
|
|
if(!pathCompEq(*si, *fi))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool isPluginLoaded(const std::vector<std::string>& loadedPlugins, const std::string& pluginName)
|
|
{
|
|
return std::any_of(
|
|
loadedPlugins.begin(), loadedPlugins.end(), [&](const std::string& loadedPathStr) {
|
|
try
|
|
{
|
|
return fs::canonical(fs::path(loadedPathStr))
|
|
== fs::canonical(fs::path(pluginName));
|
|
}
|
|
catch(const fs::filesystem_error&)
|
|
{
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
bool isPluginLoadedByRelativePath(const std::vector<std::string>& loadedPlugins,
|
|
const std::string& relativePath)
|
|
{
|
|
// We cannot resolve a relative path to the hipdnn_backend module externally
|
|
// of the backend. We can retrieve the absolute loaded path, but our
|
|
// comparison to the originally specified path is limited. In particular, we
|
|
// can check that the loaded path is suffixed with the resolved portion of
|
|
// the relative path.
|
|
const fs::path suffix{relativePath};
|
|
for(const auto& s : loadedPlugins)
|
|
{
|
|
if(isPluginLoadedByRelativePathInternal(fs::path{s}, suffix))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace test_util
|