Files
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1252 lines
51 KiB
C++

// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include "HipdnnBackendFlatbufferData.h"
#include "TestUtil.hpp"
#include "descriptors/ScopedDescriptor.hpp"
#include "hipdnn_backend.h"
#include <array>
#include <cstring>
#include <gtest/gtest.h>
#include <hipdnn_flatbuffers_sdk/data_objects/engine_config_generated.h>
#include <hipdnn_flatbuffers_sdk/data_objects/knob_value_generated.h>
#include <hipdnn_plugin_sdk/KnobSettingFactory.hpp>
#include <test_plugins/TestPluginConstants.hpp>
#include <unordered_set>
#include <vector>
using hipdnn_flatbuffers_sdk::data_objects::Knob;
using hipdnn_flatbuffers_sdk::data_objects::KnobConstraint;
using hipdnn_flatbuffers_sdk::data_objects::KnobValue;
using hipdnn_plugin_sdk::KnobSettingFactory;
class IntegrationKnobsApi : public ::testing::Test
{
protected:
hipdnnBackendDescriptor_t _engine = nullptr;
hipdnnBackendDescriptor_t _engineConfig = nullptr;
hipdnnBackendDescriptor_t _graph = nullptr;
hipdnnHandle_t _handle = nullptr;
void SetUp() override
{
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testKnobsPluginPath().c_str()};
ASSERT_EQ(hipdnnSetEnginePluginPaths_ext(
paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(hipdnnCreate(&_handle), HIPDNN_STATUS_SUCCESS);
}
void TearDown() override
{
if(_engineConfig != nullptr)
{
EXPECT_EQ(hipdnnBackendDestroyDescriptor(_engineConfig), HIPDNN_STATUS_SUCCESS);
}
if(_engine != nullptr)
{
EXPECT_EQ(hipdnnBackendDestroyDescriptor(_engine), HIPDNN_STATUS_SUCCESS);
}
if(_graph != nullptr)
{
EXPECT_EQ(hipdnnBackendDestroyDescriptor(_graph), HIPDNN_STATUS_SUCCESS);
}
if(_handle != nullptr)
{
EXPECT_EQ(hipdnnDestroy(_handle), HIPDNN_STATUS_SUCCESS);
_handle = nullptr;
}
}
void createFinalizedEngine()
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
test_util::createTestEngine(&_engine, &_graph, _handle, gidx, true);
}
void createFinalizedEngineConfig()
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(
hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::populateTestEngineConfig(&_engineConfig, &_engine, &_graph, _handle, gidx, true);
}
/// Retrieves knob info descriptors from _engine, wrapped in RAII.
std::vector<hipdnn_backend::ScopedDescriptor> getKnobDescriptors(int64_t expectedCount)
{
int64_t knobCount = 0;
auto status = hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_ENGINE_KNOB_INFO,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
0,
&knobCount,
nullptr);
EXPECT_EQ(status, HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(knobCount, expectedCount);
if(status != HIPDNN_STATUS_SUCCESS || knobCount != expectedCount)
{
return {};
}
std::vector<hipdnnBackendDescriptor_t> rawDescs(static_cast<size_t>(knobCount));
int64_t returnedCount = 0;
status = hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_ENGINE_KNOB_INFO,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
knobCount,
&returnedCount,
static_cast<void*>(rawDescs.data()));
EXPECT_EQ(status, HIPDNN_STATUS_SUCCESS);
if(status != HIPDNN_STATUS_SUCCESS)
{
// Clean up any descriptors that were returned before returning empty
for(auto raw : rawDescs)
{
delete raw;
}
return {};
}
// Immediately wrap in RAII
std::vector<hipdnn_backend::ScopedDescriptor> result;
result.reserve(rawDescs.size());
for(auto raw : rawDescs)
{
result.emplace_back(raw);
}
return result;
}
/// Finds a knob descriptor by its knob ID string.
static hipdnnBackendDescriptor_t
findKnobDescriptorById(const std::vector<hipdnn_backend::ScopedDescriptor>& knobDescs,
const std::string& targetId)
{
for(const auto& desc : knobDescs)
{
if(desc.get() == nullptr)
{
continue;
}
std::array<char, 256> knobId = {};
int64_t idLen = 0;
auto status = hipdnnBackendGetAttribute(desc.get(),
HIPDNN_ATTR_KNOB_INFO_TYPE,
HIPDNN_TYPE_CHAR,
static_cast<int64_t>(knobId.size()),
&idLen,
knobId.data());
if(status == HIPDNN_STATUS_SUCCESS && std::string(knobId.data()) == targetId)
{
return desc.get();
}
}
return nullptr;
}
};
// =============================================================================
// Engine Knob Info Tests (via HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE)
// =============================================================================
TEST_F(IntegrationKnobsApi, GetKnobInfoCountFromEngine)
{
createFinalizedEngine();
int64_t knobCount = -1;
EXPECT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
// The test plugin exposes 5 knobs: int_knob, float_knob, string_knob, deprecated_knob, shared.deterministic
EXPECT_EQ(knobCount, 5);
}
TEST_F(IntegrationKnobsApi, GetKnobInfoDataFromEngine)
{
createFinalizedEngine();
// First get the count
int64_t knobCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(knobCount, 5);
// Now get the actual knob data
std::vector<hipdnnBackendFlatbufferData_t> knobData(static_cast<size_t>(knobCount));
int64_t returnedCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
knobCount,
&returnedCount,
knobData.data()),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(returnedCount, 5);
// Verify each knob data is valid
for(size_t i = 0; i < static_cast<size_t>(returnedCount); ++i)
{
EXPECT_NE(knobData[i].ptr, nullptr);
EXPECT_GT(knobData[i].size, 0UL);
}
}
TEST_F(IntegrationKnobsApi, GetKnobInfoValidateIntKnob)
{
createFinalizedEngine();
int64_t knobCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
ASSERT_GT(knobCount, 0);
std::vector<hipdnnBackendFlatbufferData_t> knobData(static_cast<size_t>(knobCount));
int64_t returnedCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
knobCount,
&returnedCount,
knobData.data()),
HIPDNN_STATUS_SUCCESS);
// Find and validate the integer knob
bool foundIntKnob = false;
for(size_t i = 0; i < static_cast<size_t>(returnedCount); ++i)
{
flatbuffers::Verifier verifier( // NOLINT(misc-const-correctness)
static_cast<const uint8_t*>(knobData[i].ptr),
knobData[i].size);
ASSERT_TRUE(verifier.VerifyBuffer<Knob>());
auto knob = flatbuffers::GetRoot<Knob>(knobData[i].ptr);
if(knob->knob_id()->str() == "test.int_knob")
{
foundIntKnob = true;
// Verify description
EXPECT_STREQ(knob->description()->c_str(), "Test integer knob with range 0-100");
// Verify default value type and value
EXPECT_EQ(knob->default_value_type(), KnobValue::IntValue);
auto intValue = knob->default_value_as_IntValue();
ASSERT_NE(intValue, nullptr);
EXPECT_EQ(intValue->value(), 50);
// Verify constraint
EXPECT_EQ(knob->constraint_type(), KnobConstraint::IntConstraint);
auto constraint = knob->constraint_as_IntConstraint();
ASSERT_NE(constraint, nullptr);
EXPECT_EQ(constraint->min_value(), 0);
EXPECT_EQ(constraint->max_value(), 100);
EXPECT_EQ(constraint->step(), 10);
// Verify not deprecated
EXPECT_FALSE(knob->deprecated());
break;
}
}
EXPECT_TRUE(foundIntKnob) << "Integer knob 'test.int_knob' not found";
}
TEST_F(IntegrationKnobsApi, GetKnobInfoValidateFloatKnob)
{
createFinalizedEngine();
int64_t knobCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
std::vector<hipdnnBackendFlatbufferData_t> knobData(static_cast<size_t>(knobCount));
int64_t returnedCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
knobCount,
&returnedCount,
knobData.data()),
HIPDNN_STATUS_SUCCESS);
// Find and validate the float knob
bool foundFloatKnob = false;
for(size_t i = 0; i < static_cast<size_t>(returnedCount); ++i)
{
auto knob = flatbuffers::GetRoot<Knob>(knobData[i].ptr);
if(knob->knob_id()->str() == "test.float_knob")
{
foundFloatKnob = true;
// Verify description
EXPECT_STREQ(knob->description()->c_str(), "Test float knob with range 0.0-1.0");
// Verify default value type and value
EXPECT_EQ(knob->default_value_type(), KnobValue::FloatValue);
auto floatValue = knob->default_value_as_FloatValue();
ASSERT_NE(floatValue, nullptr);
EXPECT_DOUBLE_EQ(floatValue->value(), 0.5);
// Verify constraint
EXPECT_EQ(knob->constraint_type(), KnobConstraint::FloatConstraint);
auto constraint = knob->constraint_as_FloatConstraint();
ASSERT_NE(constraint, nullptr);
EXPECT_DOUBLE_EQ(constraint->min_value(), 0.0);
EXPECT_DOUBLE_EQ(constraint->max_value(), 1.0);
// Verify not deprecated
EXPECT_FALSE(knob->deprecated());
break;
}
}
EXPECT_TRUE(foundFloatKnob) << "Float knob 'test.float_knob' not found";
}
TEST_F(IntegrationKnobsApi, GetKnobInfoValidateStringKnob)
{
createFinalizedEngine();
int64_t knobCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
std::vector<hipdnnBackendFlatbufferData_t> knobData(static_cast<size_t>(knobCount));
int64_t returnedCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
knobCount,
&returnedCount,
knobData.data()),
HIPDNN_STATUS_SUCCESS);
// Find and validate the string knob
bool foundStringKnob = false;
for(size_t i = 0; i < static_cast<size_t>(returnedCount); ++i)
{
auto knob = flatbuffers::GetRoot<Knob>(knobData[i].ptr);
if(knob->knob_id()->str() == "test.string_knob")
{
foundStringKnob = true;
// Verify description
EXPECT_STREQ(knob->description()->c_str(), "Test string knob with enum values");
// Verify default value type and value
EXPECT_EQ(knob->default_value_type(), KnobValue::StringValue);
auto stringValue = knob->default_value_as_StringValue();
ASSERT_NE(stringValue, nullptr);
EXPECT_STREQ(stringValue->value()->c_str(), "fast");
// Verify constraint
EXPECT_EQ(knob->constraint_type(), KnobConstraint::StringConstraint);
auto constraint = knob->constraint_as_StringConstraint();
ASSERT_NE(constraint, nullptr);
// Note: KnobFactory uses max_length=0 (no length limit) for string knobs
EXPECT_EQ(constraint->max_length(), 0);
ASSERT_NE(constraint->valid_values(), nullptr);
EXPECT_EQ(constraint->valid_values()->size(), 3UL);
// Check valid values
std::vector<std::string> expectedValues = {"fast", "accurate", "balanced"};
for(size_t j = 0; j < constraint->valid_values()->size(); ++j)
{
EXPECT_EQ(constraint->valid_values()->Get(static_cast<unsigned int>(j))->str(),
expectedValues[j]);
}
// Verify not deprecated
EXPECT_FALSE(knob->deprecated());
break;
}
}
EXPECT_TRUE(foundStringKnob) << "String knob 'test.string_knob' not found";
}
TEST_F(IntegrationKnobsApi, GetKnobInfoValidateDeprecatedKnob)
{
createFinalizedEngine();
int64_t knobCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
std::vector<hipdnnBackendFlatbufferData_t> knobData(static_cast<size_t>(knobCount));
int64_t returnedCount = 0;
ASSERT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
knobCount,
&returnedCount,
knobData.data()),
HIPDNN_STATUS_SUCCESS);
// Find and validate the deprecated knob
bool foundDeprecatedKnob = false;
for(size_t i = 0; i < static_cast<size_t>(returnedCount); ++i)
{
auto knob = flatbuffers::GetRoot<Knob>(knobData[i].ptr);
if(knob->knob_id()->str() == "test.deprecated_knob")
{
foundDeprecatedKnob = true;
// Verify deprecated flag is set
EXPECT_TRUE(knob->deprecated());
break;
}
}
EXPECT_TRUE(foundDeprecatedKnob) << "Deprecated knob 'test.deprecated_knob' not found";
}
TEST_F(IntegrationKnobsApi, GetKnobInfoNotFinalizedEngine)
{
int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
// Create engine but don't finalize
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINE_DESCRIPTOR, &_engine),
HIPDNN_STATUS_SUCCESS);
test_util::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);
// Not finalizing!
int64_t knobCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_KNOB_INFO_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_NOT_INITIALIZED);
}
// =============================================================================
// EngineConfig Knob Choice Tests (via HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE)
// =============================================================================
TEST_F(IntegrationKnobsApi, SetKnobChoiceIntValue)
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
// Set an integer knob value
auto knobBuffer = KnobSettingFactory::createIntKnobSetting("test.int_knob", 70);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
// Finalize should succeed
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
}
TEST_F(IntegrationKnobsApi, SetKnobChoiceFloatValue)
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
// Set a float knob value
auto knobBuffer = KnobSettingFactory::createFloatKnobSetting("test.float_knob", 0.75);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
// Finalize should succeed
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
}
TEST_F(IntegrationKnobsApi, SetKnobChoiceStringValue)
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
// Set a string knob value
auto knobBuffer = KnobSettingFactory::createStringKnobSetting("test.string_knob", "accurate");
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
// Finalize should succeed
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
}
TEST_F(IntegrationKnobsApi, SetKnobChoiceMultipleKnobs)
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
// Create multiple knob settings
auto intKnobBuffer = KnobSettingFactory::createIntKnobSetting("test.int_knob", 80);
auto floatKnobBuffer = KnobSettingFactory::createFloatKnobSetting("test.float_knob", 0.25);
auto stringKnobBuffer
= KnobSettingFactory::createStringKnobSetting("test.string_knob", "balanced");
std::vector<hipdnnBackendFlatbufferData_t> knobDataArray
= {{intKnobBuffer.data(), intKnobBuffer.size()},
{floatKnobBuffer.data(), floatKnobBuffer.size()},
{stringKnobBuffer.data(), stringKnobBuffer.size()}};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
3,
knobDataArray.data()),
HIPDNN_STATUS_SUCCESS);
// Finalize should succeed
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
}
TEST_F(IntegrationKnobsApi, FinalizeEngineConfigWithKnobs)
{
createFinalizedEngineConfig();
// Just verify we can finalize successfully - the fixture already does this
// This test documents that the basic flow works
SUCCEED();
}
TEST_F(IntegrationKnobsApi, SetKnobChoiceNullPointer)
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
nullptr),
HIPDNN_STATUS_BAD_PARAM_NULL_POINTER);
}
TEST_F(IntegrationKnobsApi, SetKnobChoiceInvalidType)
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
auto knobBuffer = KnobSettingFactory::createIntKnobSetting("test.int_knob", 50);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
// Wrong attribute type
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_INT64,
1,
&knobData),
HIPDNN_STATUS_BAD_PARAM);
}
TEST_F(IntegrationKnobsApi, SetKnobChoiceOnFinalizedConfig)
{
createFinalizedEngineConfig();
auto knobBuffer = KnobSettingFactory::createIntKnobSetting("test.int_knob", 50);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
// Cannot set knob choice on already finalized engine config
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_NOT_INITIALIZED);
}
TEST_F(IntegrationKnobsApi, GetMaxWorkspaceSizeWithKnobs)
{
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPlugin>();
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
// Set a knob before finalizing
auto knobBuffer = KnobSettingFactory::createIntKnobSetting("test.int_knob", 60);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
ASSERT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
// Verify we can still get workspace size after setting knobs
int64_t workspaceSize = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(_engineConfig,
HIPDNN_ATTR_ENGINECFG_WORKSPACE_SIZE,
HIPDNN_TYPE_INT64,
1,
nullptr,
&workspaceSize),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(workspaceSize, 1024); // Test plugin returns 1024
}
// =============================================================================
// Constraint Type Validation Tests
// =============================================================================
class IntegrationConstraintValidationApi : public ::testing::Test
{
protected:
hipdnnBackendDescriptor_t _engine = nullptr;
hipdnnBackendDescriptor_t _engineConfig = nullptr;
hipdnnBackendDescriptor_t _graph = nullptr;
hipdnnHandle_t _handle = nullptr;
void SetUp() override
{
const std::array<const char*, 1> paths
= {hipdnn_tests::plugin_constants::testKnobConstraintValidationPluginPath().c_str()};
ASSERT_EQ(hipdnnSetEnginePluginPaths_ext(
paths.size(), paths.data(), HIPDNN_PLUGIN_LOADING_ABSOLUTE),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(hipdnnCreate(&_handle), HIPDNN_STATUS_SUCCESS);
}
void TearDown() override
{
if(_engineConfig != nullptr)
{
EXPECT_EQ(hipdnnBackendDestroyDescriptor(_engineConfig), HIPDNN_STATUS_SUCCESS);
}
if(_engine != nullptr)
{
EXPECT_EQ(hipdnnBackendDestroyDescriptor(_engine), HIPDNN_STATUS_SUCCESS);
}
if(_graph != nullptr)
{
EXPECT_EQ(hipdnnBackendDestroyDescriptor(_graph), HIPDNN_STATUS_SUCCESS);
}
if(_handle != nullptr)
{
EXPECT_EQ(hipdnnDestroy(_handle), HIPDNN_STATUS_SUCCESS);
_handle = nullptr;
}
}
void setupEngineConfig()
{
const int64_t gidx
= hipdnn_tests::plugin_constants::engineId<KnobConstraintValidationPlugin>();
ASSERT_EQ(
hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_ENGINECFG_DESCRIPTOR, &_engineConfig),
HIPDNN_STATUS_SUCCESS);
test_util::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);
}
/// Creates an execution plan with the fixture's handle and engine config,
/// then asserts finalize returns the expected status.
void expectExecutionPlanFinalize(hipdnnStatus_t expectedStatus)
{
hipdnn_backend::ScopedDescriptor executionPlan;
ASSERT_EQ(hipdnnBackendCreateDescriptor(HIPDNN_BACKEND_EXECUTION_PLAN_DESCRIPTOR,
executionPlan.getPtr()),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(hipdnnBackendSetAttribute(executionPlan.get(),
HIPDNN_ATTR_EXECUTION_PLAN_HANDLE,
HIPDNN_TYPE_HANDLE,
1,
static_cast<const void*>(&_handle)),
HIPDNN_STATUS_SUCCESS);
ASSERT_EQ(hipdnnBackendSetAttribute(executionPlan.get(),
HIPDNN_ATTR_EXECUTION_PLAN_ENGINE_CONFIG,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
1,
static_cast<const void*>(&_engineConfig)),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(executionPlan.get()), expectedStatus);
}
};
TEST_F(IntegrationConstraintValidationApi, IntValueToFloatConstraint)
{
setupEngineConfig();
auto knobBuffer = KnobSettingFactory::createIntKnobSetting("constraint.float_knob", 5);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_PLUGIN_ERROR);
}
TEST_F(IntegrationConstraintValidationApi, FloatValueToIntConstraint)
{
setupEngineConfig();
auto knobBuffer = KnobSettingFactory::createFloatKnobSetting("constraint.int_knob", 50.0);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_PLUGIN_ERROR);
}
TEST_F(IntegrationConstraintValidationApi, StringValueToIntConstraint)
{
setupEngineConfig();
auto knobBuffer = KnobSettingFactory::createStringKnobSetting("constraint.int_knob", "50");
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_PLUGIN_ERROR);
}
TEST_F(IntegrationConstraintValidationApi, IntValueToStringConstraint)
{
setupEngineConfig();
auto knobBuffer = KnobSettingFactory::createIntKnobSetting("constraint.string_knob", 0);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_PLUGIN_ERROR);
}
TEST_F(IntegrationConstraintValidationApi, FloatValueToStringConstraint)
{
setupEngineConfig();
auto knobBuffer = KnobSettingFactory::createFloatKnobSetting("constraint.string_knob", 1.5);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_PLUGIN_ERROR);
}
TEST_F(IntegrationConstraintValidationApi, StringValueToFloatConstraint)
{
setupEngineConfig();
auto knobBuffer = KnobSettingFactory::createStringKnobSetting("constraint.float_knob", "5.0");
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_PLUGIN_ERROR);
}
TEST_F(IntegrationConstraintValidationApi, CorrectTypesSucceed)
{
setupEngineConfig();
auto intKnobBuffer = KnobSettingFactory::createIntKnobSetting("constraint.int_knob", 50);
auto floatKnobBuffer = KnobSettingFactory::createFloatKnobSetting("constraint.float_knob", 5.0);
auto stringKnobBuffer
= KnobSettingFactory::createStringKnobSetting("constraint.string_knob", "beta");
std::vector<hipdnnBackendFlatbufferData_t> knobDataArray
= {{intKnobBuffer.data(), intKnobBuffer.size()},
{floatKnobBuffer.data(), floatKnobBuffer.size()},
{stringKnobBuffer.data(), stringKnobBuffer.size()}};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
3,
knobDataArray.data()),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_SUCCESS);
}
TEST_F(IntegrationConstraintValidationApi, UnknownKnobIsIgnored)
{
setupEngineConfig();
auto knobBuffer = KnobSettingFactory::createIntKnobSetting("unknown.knob", 123);
hipdnnBackendFlatbufferData_t knobData = {knobBuffer.data(), knobBuffer.size()};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
1,
&knobData),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_SUCCESS);
}
TEST_F(IntegrationConstraintValidationApi, MixedValidAndInvalidKnobs)
{
setupEngineConfig();
auto validKnobBuffer = KnobSettingFactory::createIntKnobSetting("constraint.int_knob", 50);
auto invalidKnobBuffer = KnobSettingFactory::createIntKnobSetting("constraint.float_knob", 5);
std::vector<hipdnnBackendFlatbufferData_t> knobDataArray
= {{validKnobBuffer.data(), validKnobBuffer.size()},
{invalidKnobBuffer.data(), invalidKnobBuffer.size()}};
EXPECT_EQ(hipdnnBackendSetAttribute(_engineConfig,
HIPDNN_ATTR_KNOB_CHOICE_SERIALIZED_VALUE,
HIPDNN_TYPE_FLATBUFFER_DATA_STRUCT_EXT,
2,
knobDataArray.data()),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(hipdnnBackendFinalize(_engineConfig), HIPDNN_STATUS_SUCCESS);
expectExecutionPlanFinalize(HIPDNN_STATUS_PLUGIN_ERROR);
}
// =============================================================================
// Engine Knob Info via Descriptor API (HIPDNN_ATTR_ENGINE_KNOB_INFO)
// =============================================================================
TEST_F(IntegrationKnobsApi, GetKnobInfoDescriptorCount)
{
createFinalizedEngine();
int64_t knobCount = -1;
EXPECT_EQ(hipdnnBackendGetAttribute(_engine,
HIPDNN_ATTR_ENGINE_KNOB_INFO,
HIPDNN_TYPE_BACKEND_DESCRIPTOR,
0,
&knobCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(knobCount, 5);
}
TEST_F(IntegrationKnobsApi, GetKnobInfoDescriptorsAndValidateIntKnob)
{
createFinalizedEngine();
auto knobDescs = getKnobDescriptors(5);
ASSERT_FALSE(knobDescs.empty());
auto* intKnob = findKnobDescriptorById(knobDescs, "test.int_knob");
ASSERT_NE(intKnob, nullptr) << "Integer knob 'test.int_knob' not found via descriptor API";
// Verify default value type
int64_t valueType = 0;
int64_t vtCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(intKnob,
HIPDNN_ATTR_KNOB_INFO_DEFAULT_VALUE_TYPE,
HIPDNN_TYPE_INT64,
1,
&vtCount,
&valueType),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(valueType, static_cast<int64_t>(HIPDNN_TYPE_INT64));
// Verify default value
int64_t defaultVal = 0;
int64_t dvCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(intKnob,
HIPDNN_ATTR_KNOB_INFO_DEFAULT_VALUE,
HIPDNN_TYPE_INT64,
1,
&dvCount,
&defaultVal),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(defaultVal, 50);
// Verify min/max
int64_t minVal = 0;
int64_t minCount = 0;
EXPECT_EQ(
hipdnnBackendGetAttribute(
intKnob, HIPDNN_ATTR_KNOB_INFO_MINIMUM_VALUE, HIPDNN_TYPE_INT64, 1, &minCount, &minVal),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(minVal, 0);
int64_t maxVal = 0;
int64_t maxCount = 0;
EXPECT_EQ(
hipdnnBackendGetAttribute(
intKnob, HIPDNN_ATTR_KNOB_INFO_MAXIMUM_VALUE, HIPDNN_TYPE_INT64, 1, &maxCount, &maxVal),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(maxVal, 100);
// Verify stride
int64_t stride = 0;
int64_t strideCount = 0;
EXPECT_EQ(
hipdnnBackendGetAttribute(
intKnob, HIPDNN_ATTR_KNOB_INFO_STRIDE, HIPDNN_TYPE_INT64, 1, &strideCount, &stride),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(stride, 10);
}
TEST_F(IntegrationKnobsApi, GetKnobInfoDescriptorsAndValidateFloatKnob)
{
createFinalizedEngine();
auto knobDescs = getKnobDescriptors(5);
ASSERT_FALSE(knobDescs.empty());
auto* floatKnob = findKnobDescriptorById(knobDescs, "test.float_knob");
ASSERT_NE(floatKnob, nullptr) << "Float knob 'test.float_knob' not found via descriptor API";
// Verify default value type is DOUBLE
int64_t valueType = 0;
int64_t vtCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(floatKnob,
HIPDNN_ATTR_KNOB_INFO_DEFAULT_VALUE_TYPE,
HIPDNN_TYPE_INT64,
1,
&vtCount,
&valueType),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(valueType, static_cast<int64_t>(HIPDNN_TYPE_DOUBLE));
// Verify default value = 0.5
double defaultVal = 0.0;
int64_t dvCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(floatKnob,
HIPDNN_ATTR_KNOB_INFO_DEFAULT_VALUE,
HIPDNN_TYPE_DOUBLE,
1,
&dvCount,
&defaultVal),
HIPDNN_STATUS_SUCCESS);
EXPECT_DOUBLE_EQ(defaultVal, 0.5);
// Verify min = 0.0
double minVal = -1.0;
int64_t minCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(floatKnob,
HIPDNN_ATTR_KNOB_INFO_MINIMUM_VALUE,
HIPDNN_TYPE_DOUBLE,
1,
&minCount,
&minVal),
HIPDNN_STATUS_SUCCESS);
EXPECT_DOUBLE_EQ(minVal, 0.0);
// Verify max = 1.0
double maxVal = -1.0;
int64_t maxCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(floatKnob,
HIPDNN_ATTR_KNOB_INFO_MAXIMUM_VALUE,
HIPDNN_TYPE_DOUBLE,
1,
&maxCount,
&maxVal),
HIPDNN_STATUS_SUCCESS);
EXPECT_DOUBLE_EQ(maxVal, 1.0);
}
TEST_F(IntegrationKnobsApi, GetKnobInfoDescriptorsAndValidateStringKnob)
{
createFinalizedEngine();
auto knobDescs = getKnobDescriptors(5);
ASSERT_FALSE(knobDescs.empty());
auto* stringKnob = findKnobDescriptorById(knobDescs, "test.string_knob");
ASSERT_NE(stringKnob, nullptr) << "String knob 'test.string_knob' not found via descriptor API";
// Verify default value type is CHAR
int64_t valueType = 0;
int64_t vtCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(stringKnob,
HIPDNN_ATTR_KNOB_INFO_DEFAULT_VALUE_TYPE,
HIPDNN_TYPE_INT64,
1,
&vtCount,
&valueType),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(valueType, static_cast<int64_t>(HIPDNN_TYPE_CHAR));
// Verify default value = "fast"
std::array<char, 64> defaultVal = {};
int64_t dvCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(stringKnob,
HIPDNN_ATTR_KNOB_INFO_DEFAULT_VALUE,
HIPDNN_TYPE_CHAR,
static_cast<int64_t>(defaultVal.size()),
&dvCount,
defaultVal.data()),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(std::string(defaultVal.data()), "fast");
// Verify valid values buffer contains the 3 choices
int64_t validBufLen = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(stringKnob,
HIPDNN_ATTR_KNOB_INFO_VALID_VALUES_STRING,
HIPDNN_TYPE_CHAR,
0,
&validBufLen,
nullptr),
HIPDNN_STATUS_SUCCESS);
EXPECT_GT(validBufLen, 0) << "Valid values buffer should be non-empty";
std::vector<char> validBuf(static_cast<size_t>(validBufLen));
int64_t actualLen = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(stringKnob,
HIPDNN_ATTR_KNOB_INFO_VALID_VALUES_STRING,
HIPDNN_TYPE_CHAR,
validBufLen,
&actualLen,
validBuf.data()),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(actualLen, validBufLen);
// Parse the null-separated buffer and verify the 3 choices are present
std::unordered_set<std::string> validValues;
const char* p = validBuf.data();
const char* end = p + actualLen;
while(p < end)
{
const std::string s(p);
if(!s.empty())
{
validValues.insert(s);
}
p += s.size() + 1;
}
EXPECT_EQ(validValues.count("fast"), 1u);
EXPECT_EQ(validValues.count("accurate"), 1u);
EXPECT_EQ(validValues.count("balanced"), 1u);
}
TEST_F(IntegrationKnobsApi, GetKnobInfoDescriptorsAndValidateDeprecatedKnob)
{
createFinalizedEngine();
auto knobDescs = getKnobDescriptors(5);
ASSERT_FALSE(knobDescs.empty());
auto* deprecatedKnob = findKnobDescriptorById(knobDescs, "test.deprecated_knob");
ASSERT_NE(deprecatedKnob, nullptr)
<< "Deprecated knob 'test.deprecated_knob' not found via descriptor API";
// Verify deprecated flag is true
bool isDeprecated = false;
int64_t depCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(deprecatedKnob,
HIPDNN_ATTR_KNOB_INFO_DEPRECATED,
HIPDNN_TYPE_BOOLEAN,
1,
&depCount,
&isDeprecated),
HIPDNN_STATUS_SUCCESS);
EXPECT_TRUE(isDeprecated) << "test.deprecated_knob should have deprecated flag set to true";
}
TEST_F(IntegrationKnobsApi, GetKnobInfoDescriptorsAndValidateValidValuesIntKnob)
{
// Use Engine B which has test.engine_b.block_size with valid values {8, 16, 32, 64}
const int64_t gidx = hipdnn_tests::plugin_constants::engineId<KnobsPluginEngineB>();
test_util::createTestEngine(&_engine, &_graph, _handle, gidx, true);
auto knobDescs = getKnobDescriptors(3);
ASSERT_FALSE(knobDescs.empty());
auto* blockSizeKnob = findKnobDescriptorById(knobDescs, "test.engine_b.block_size");
ASSERT_NE(blockSizeKnob, nullptr)
<< "Knob 'test.engine_b.block_size' not found via descriptor API";
// Verify valid values count = 4
int64_t validCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(blockSizeKnob,
HIPDNN_ATTR_KNOB_INFO_VALID_VALUES_INT,
HIPDNN_TYPE_INT64,
0,
&validCount,
nullptr),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(validCount, 4);
// Fetch and verify the actual valid values {8, 16, 32, 64}
std::vector<int64_t> validValues(static_cast<size_t>(validCount));
int64_t actualCount = 0;
EXPECT_EQ(hipdnnBackendGetAttribute(blockSizeKnob,
HIPDNN_ATTR_KNOB_INFO_VALID_VALUES_INT,
HIPDNN_TYPE_INT64,
validCount,
&actualCount,
validValues.data()),
HIPDNN_STATUS_SUCCESS);
EXPECT_EQ(actualCount, 4);
const std::unordered_set<int64_t> expected = {8, 16, 32, 64};
const std::unordered_set<int64_t> actual(validValues.begin(), validValues.end());
EXPECT_EQ(actual, expected);
}