Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions projects/hipdnn/docs/KNOWN_DIVERGENCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ The shim targets the cuDNN frontend v9 graph API surface exposed by
`<hipdnn_compatibility/cudnn/cudnn_frontend.h>`. The v0.x / v8 builder and backend
descriptor API surface is out of scope.

## hipDNN extensions

The shim exposes one method with no cuDNN frontend equivalent:

`Graph::is_supported_ext(cudnnHandle_t, std::vector<HeurMode_t> = {FALLBACK})` is a
lightweight applicability probe forwarding to the native hipDNN method of the same
name. It returns `OK` when at least one engine can run the graph and
`GRAPH_NOT_SUPPORTED` when none can. Like the native method it auto-builds the
operation graph when needed; it neither creates nor invalidates execution plans, so
it may be called before or after `create_execution_plans`. A node-less graph has
nothing to probe and is reported supported.

Because it has no cuDNN counterpart, source that must also compile against real
cuDNN cannot use it.

## Heuristic modes

The shim accepts every cuDNN frontend `HeurMode_t` value, including `A`, `B`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,7 @@ class Graph : public ErrorRecorder<Graph>

error_t create_execution_plans(const std::vector<HeurMode_t>& modes = {HeurMode_t::FALLBACK})
{
for(const auto mode : modes)
{
if(mode != HeurMode_t::FALLBACK)
{
HIPDNN_FE_LOG_WARN("[cudnn_frontend] cuDNN heuristic mode "
<< hipdnn_frontend::to_string(mode)
<< " is accepted but not honored; hipDNN uses fallback "
"selection. Plan choice may differ from cuDNN.");
}
}
warnUnhonoredHeuristicModes(modes);

HIPDNN_CUDNN_SHIM_RETURN_RECORDED_ERROR();

Expand Down Expand Up @@ -179,6 +170,31 @@ class Graph : public ErrorRecorder<Graph>
return check_support();
}

// hipDNN extension, no upstream cuDNN frontend equivalent: probes whether any
// engine can run the graph, without creating or building plans. Mirrors the
// native method, which auto-builds the operation graph, so a successful probe
// leaves the graph built. Never regresses a later stage; the probe neither
// creates nor invalidates plans.
error_t is_supported_ext(cudnnHandle_t handle,
const std::vector<HeurMode_t>& modes = {HeurMode_t::FALLBACK})
{
warnUnhonoredHeuristicModes(modes);

HIPDNN_CUDNN_SHIM_RETURN_RECORDED_ERROR();

CHECK_CUDNN_FRONTEND_ERROR(validateOwnedTensors());
if(hasOperationGraphState())
{
CHECK_CUDNN_FRONTEND_ERROR(_graph->is_supported_ext(handle, modes));
}

if(!stageAtLeast(Stage::OpGraphBuilt))
{
_stage = Stage::OpGraphBuilt;
}
return {};
}

error_t build_plans(BuildPlanPolicy_t policy = BuildPlanPolicy_t::HEURISTICS_CHOICE,
bool doMultithreadedBuilds = false)
{
Expand Down Expand Up @@ -1810,6 +1826,20 @@ class Graph : public ErrorRecorder<Graph>
return {};
}

static void warnUnhonoredHeuristicModes(const std::vector<HeurMode_t>& modes)
{
for(const auto mode : modes)
{
if(mode != HeurMode_t::FALLBACK)
{
HIPDNN_FE_LOG_WARN("[cudnn_frontend] cuDNN heuristic mode "
<< hipdnn_frontend::to_string(mode)
<< " is accepted but not honored; hipDNN uses fallback "
"selection. Plan choice may differ from cuDNN.");
}
}
}

static error_t unsupportedDevicelessBuildError()
{
return {error_code_t::INVALID_VALUE,
Expand Down
1 change: 1 addition & 0 deletions projects/hipdnn/frontend/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ add_executable(
$<$<BOOL:${HIPDNN_ENABLE_CUDNN_COMPATIBILITY}>:TestCudnnShimV9Tu.cpp>
$<$<BOOL:${HIPDNN_ENABLE_CUDNN_COMPATIBILITY}>:TestCudnnShimGraph.cpp>
$<$<BOOL:${HIPDNN_ENABLE_CUDNN_COMPATIBILITY}>:TestCudnnShimGraphNodes.cpp>
$<$<BOOL:${HIPDNN_ENABLE_CUDNN_COMPATIBILITY}>:TestCudnnShimGraphSupportCheck.cpp>
$<$<BOOL:${HIPDNN_ENABLE_CUDNN_COMPATIBILITY}>:TestCudnnShimKnobWrapper.cpp>
$<$<BOOL:${HIPDNN_ENABLE_CUDNN_COMPATIBILITY}>:TestCudnnShimNoteTriage.cpp>
$<$<BOOL:${HIPDNN_ENABLE_CUDNN_COMPATIBILITY}>:TestCudnnShimPlanSurface.cpp>
Expand Down
134 changes: 134 additions & 0 deletions projects/hipdnn/frontend/tests/TestCudnnShimGraphSupportCheck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT

// Coverage for the shim's is_supported_ext() probe: a hipDNN extension with no
// upstream cuDNN frontend equivalent, so the contract asserted here is hipDNN's.
#include "CudnnShimTestSupport.hpp"
#include "fake_backend/MockBackendFixture.hpp"

#include <hipdnn_compatibility/cudnn/cudnn_frontend.h>

#include <cstdint>
#include <gtest/gtest.h>

#include <vector>

namespace
{
namespace fe = hipdnn_frontend::compatibility::cudnn_frontend;

using ::testing::_;

void addPointwiseGraph(fe::graph::Graph& graph)
{
const int64_t n = 4;
graph.set_io_data_type(fe::DataType_t::FLOAT).set_compute_data_type(fe::DataType_t::FLOAT);

auto a = hipdnn_shim_test::makeTensor(graph, {n, n, n, n}, {n * n * n, n * n, n, 1}, 1);
auto b = hipdnn_shim_test::makeTensor(graph, {n, n, n, n}, {n * n * n, n * n, n, 1}, 2);
auto c = graph.pointwise(
a, b, fe::graph::Pointwise_attributes{}.set_mode(fe::PointwiseMode_t::ADD));
ASSERT_NE(c, nullptr);
c->set_output(true).set_uid(3);
}

class TestCudnnShimGraphSupportCheck : public hipdnn_shim_test::ShimMockBackendFixture
{
protected:
int64_t _availableEngineCount = 1;

// The probe's only backend query beyond the operation-graph lowering the base
// fixture already answers: how many engine configs the heuristic returned.
void installEngineHeuristicResults()
{
ON_CALL(*_mockBackend, backendGetAttribute(_, HIPDNN_ATTR_ENGINEHEUR_RESULTS, _, _, _, _))
.WillByDefault([this](hipdnnBackendDescriptor_t,
hipdnnBackendAttributeName_t,
hipdnnBackendAttributeType_t,
int64_t,
int64_t* elementCount,
void*) {
if(elementCount != nullptr)
{
*elementCount = _availableEngineCount;
}
return HIPDNN_STATUS_SUCCESS;
});
}
};

TEST_F(TestCudnnShimGraphSupportCheck, ReportsSupportedWhenEnginesAvailable)
{
installEngineHeuristicResults();

fe::graph::Graph graph;
addPointwiseGraph(graph);

auto error = graph.is_supported_ext(_handle);
EXPECT_TRUE(error.is_good()) << error.get_message();
}

TEST_F(TestCudnnShimGraphSupportCheck, ReportsNotSupportedWhenNoEngineIsApplicable)
{
_availableEngineCount = 0;
installEngineHeuristicResults();

fe::graph::Graph graph;
addPointwiseGraph(graph);

auto error = graph.is_supported_ext(_handle);
ASSERT_TRUE(error.is_bad());
EXPECT_EQ(error.get_code(), fe::error_code_t::GRAPH_NOT_SUPPORTED);
}

TEST_F(TestCudnnShimGraphSupportCheck, AcceptsNonFallbackHeuristicModes)
{
installEngineHeuristicResults();

fe::graph::Graph graph;
addPointwiseGraph(graph);

auto error = graph.is_supported_ext(_handle, {fe::HeurMode_t::A, fe::HeurMode_t::FALLBACK});
EXPECT_TRUE(error.is_good()) << error.get_message();
}

TEST_F(TestCudnnShimGraphSupportCheck, InvalidOwnedTensorFailsBeforeBackendCall)
{
fe::graph::Graph graph;
graph.tensor(fe::graph::Tensor_attributes{}
.set_dim({1})
.set_data_type(fe::DataType_t::FLOAT)
.set_uid(1));

auto error = graph.is_supported_ext(_handle);
ASSERT_TRUE(error.is_bad());
EXPECT_EQ(error.get_code(), fe::error_code_t::INVALID_VALUE);
}

TEST_F(TestCudnnShimGraphSupportCheck, RecordedSetterErrorSurfaces)
{
fe::graph::Graph graph;
graph.set_sm_count(1);

auto error = graph.is_supported_ext(_handle);
ASSERT_TRUE(error.is_bad());
EXPECT_EQ(error.get_code(), fe::error_code_t::INVALID_VALUE);
}

// A node-less graph has nothing to probe, so it is trivially supported, and the
// probe leaves it built the way the native call would (workspace query answers 0
// only once the operation graph is built).
TEST_F(TestCudnnShimGraphSupportCheck, EmptyGraphIsSupportedAndBuilt)
{
fe::graph::Graph graph;

int64_t workspaceSize = -1;
ASSERT_TRUE(graph.get_workspace_size(workspaceSize).is_bad());

EXPECT_TRUE(graph.is_supported_ext(nullptr).is_good());

ASSERT_TRUE(graph.get_workspace_size(workspaceSize).is_good());
EXPECT_EQ(workspaceSize, 0);
}

} // namespace
Loading