Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
3 changes: 3 additions & 0 deletions projects/hipdnn/backend/src/PlatformUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#pragma once

#include <array>
#include <cstdint>
#include <filesystem>
#include <hipdnn_data_sdk/utilities/PlatformUtils.hpp>
#include <string>
Expand All @@ -24,5 +26,6 @@ PluginLibHandle openLibrary(const std::filesystem::path& libraryPath);
void closeLibrary(PluginLibHandle handle);
void* getSymbol(PluginLibHandle handle, const char* symbolName);
std::string getSystemInfo();
std::array<uint8_t, 16> generateUuidV4();

}
31 changes: 31 additions & 0 deletions projects/hipdnn/backend/src/PlatformUtils.linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
#if defined(__linux__)

#include "HipdnnException.hpp"
#include <cerrno>
#include <dlfcn.h>
#include <spdlog/fmt/fmt.h>
#include <sys/random.h>
#include <sys/utsname.h>

namespace hipdnn_backend::platform_utilities
Expand Down Expand Up @@ -79,6 +81,35 @@ std::string getSystemInfo()
buffer.machine);
}

std::array<uint8_t, 16> generateUuidV4()
{
std::array<uint8_t, 16> bytes{};
size_t filled = 0;
while(filled < bytes.size())
{
const auto result = getrandom(bytes.data() + filled, bytes.size() - filled, 0);
if(result < 0)
{
if(errno == EINTR)
{
continue;
}
throw HipdnnException(HIPDNN_STATUS_INTERNAL_ERROR,
"Failed to generate graph UUID using getrandom.");
}
if(result == 0)
{
throw HipdnnException(HIPDNN_STATUS_INTERNAL_ERROR,
"Failed to generate graph UUID: getrandom returned no data.");
}
filled += static_cast<size_t>(result);
}

bytes[6] = static_cast<uint8_t>((bytes[6] & 0x0fU) | 0x40U);
bytes[8] = static_cast<uint8_t>((bytes[8] & 0x3fU) | 0x80U);
return bytes;
}

}

#endif // defined(__linux__)
25 changes: 25 additions & 0 deletions projects/hipdnn/backend/src/PlatformUtils.windows.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
// Copyright © Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT

#ifdef _WIN32
#define _CRT_RAND_S
#endif

#include "PlatformUtils.hpp"

#ifdef _WIN32

#include "HipdnnException.hpp"
#include <array>
#include <cstdlib>
#include <cstring>
#include <spdlog/fmt/fmt.h>
#include <winternl.h>

Expand Down Expand Up @@ -142,6 +148,25 @@ std::string getSystemInfo()
architecture);
}

std::array<uint8_t, 16> generateUuidV4()
{
std::array<uint8_t, 16> bytes{};
for(size_t offset = 0; offset < bytes.size(); offset += sizeof(unsigned int))
{
unsigned int randomValue;
if(rand_s(&randomValue) != 0)
{
throw HipdnnException(HIPDNN_STATUS_INTERNAL_ERROR,
"Failed to generate graph UUID using rand_s.");
}
std::memcpy(bytes.data() + offset, &randomValue, sizeof(randomValue));
}

bytes[6] = static_cast<uint8_t>((bytes[6] & 0x0fU) | 0x40U);
bytes[8] = static_cast<uint8_t>((bytes[8] & 0x3fU) | 0x80U);
return bytes;
}

}

#endif // _WIN32
62 changes: 47 additions & 15 deletions projects/hipdnn/backend/src/descriptors/GraphDescriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include "HipdnnBackendDescriptorType.h"
#include "HipdnnException.hpp"
#include "NodeFactory.hpp"
#include "PlatformUtils.hpp"

#include <hipdnn_flatbuffers_sdk/utilities/FlatbufferUtils.hpp>
#include <hipdnn_flatbuffers_sdk/utilities/Uuid.hpp>
#include <hipdnn_flatbuffers_sdk/utilities/json/Graph.hpp>
#include <hipdnn_plugin_sdk/PluginVersionConstants.hpp>
#include <logging/GraphLogger.hpp>
Expand All @@ -33,8 +35,25 @@ void GraphDescriptor::finalize()
HIPDNN_STATUS_BAD_PARAM,
"GraphDescriptor::finalize: no operations set");

invalidateCache();
buildSerializedGraph();
const bool needsId = !_graphId.has_value();
if(needsId)
{
_graphId = platform_utilities::generateUuidV4();
}

try
{
invalidateCache();
buildSerializedGraph();
}
catch(...)
{
if(needsId)
{
_graphId.reset();
}
throw;
}

HipdnnBackendDescriptorImpl<GraphDescriptor>::finalize();

Expand All @@ -58,6 +77,11 @@ std::unique_ptr<hipdnn_flatbuffers_sdk::data_objects::GraphT>
graph->preferred_engine_id = _preferredEngineId;
graph->is_override_shape_enabled = _isOverrideShapeEnabled;
graph->name = _name;
if(_graphId.has_value())
{
graph->id = std::make_unique<hipdnn_flatbuffers_sdk::data_objects::Uuid>(
hipdnn_flatbuffers_sdk::utilities::toFlatbufferUuid(*_graphId));
}

std::unordered_map<int64_t, std::shared_ptr<TensorDescriptor>> seenTensors;

Expand Down Expand Up @@ -379,20 +403,21 @@ void GraphDescriptor::setAttribute(hipdnnBackendAttributeName_t attributeName,
if(invalidate)
{
invalidateCache();
_graphId.reset();
}
}

void GraphDescriptor::deserializeGraph(const uint8_t* serializedGraph, size_t graphByteSize)
{
THROW_IF_TRUE(isFinalized(),
HIPDNN_STATUS_NOT_INITIALIZED,
"GraphDescriptor::deserializeGraph() failed: Already finalized.");
THROW_IF_NULL(serializedGraph,
HIPDNN_STATUS_BAD_PARAM_NULL_POINTER,
"GraphDescriptor::deserializeGraph: serializedGraph is null");
THROW_IF_TRUE(graphByteSize == 0,
HIPDNN_STATUS_BAD_PARAM,
"GraphDescriptor::deserializeGraph: graphByteSize is 0");

invalidateCache();

// Parse FlatBuffer and eagerly unpack into _operations
std::unique_ptr<hipdnn_flatbuffers_sdk::data_objects::GraphT> graph;
flatbuffer_utilities::convertSerializedGraphToGraph(serializedGraph, graphByteSize, graph);
Expand All @@ -405,7 +430,22 @@ void GraphDescriptor::deserializeGraph(const uint8_t* serializedGraph, size_t gr
HIPDNN_STATUS_NOT_SUPPORTED,
"Serialized graph requires a newer engine plugin API version than this build supports.");

// Extract graph-level attributes
std::optional<std::array<uint8_t, 16>> graphId;
if(graph->id)
{
graphId = hipdnn_flatbuffers_sdk::utilities::toUuidBytes(*graph->id);
}

auto tensorMap = NodeFactory::buildTensorMap(graph->tensors);
std::vector<std::shared_ptr<IBackendDescriptor>> unpacked;
unpacked.reserve(graph->nodes.size());
for(const auto& nodeT : graph->nodes)
{
unpacked.push_back(NodeFactory::createOperationFromNode(*nodeT, tensorMap));
}

// Commit only after every validation and conversion succeeds.
invalidateCache();
_computeDataType = graph->compute_data_type;
_intermediateDataType = graph->intermediate_data_type;
_ioDataType = graph->io_data_type;
Expand All @@ -416,15 +456,7 @@ void GraphDescriptor::deserializeGraph(const uint8_t* serializedGraph, size_t gr
_isRuntimePassByValueEnabled = hipdnn_flatbuffers_sdk::utilities::anyTensorIsRuntimePassByValue(
graph->tensors, [](const auto& tensor) { return tensor.get(); });
_name = graph->name;

// Populate _operations from the deserialized graph nodes
auto tensorMap = NodeFactory::buildTensorMap(graph->tensors);
std::vector<std::shared_ptr<IBackendDescriptor>> unpacked;
unpacked.reserve(graph->nodes.size());
for(const auto& nodeT : graph->nodes)
{
unpacked.push_back(NodeFactory::createOperationFromNode(*nodeT, tensorMap));
}
_graphId = graphId;
_operations = std::move(unpacked);
}

Expand Down
6 changes: 6 additions & 0 deletions projects/hipdnn/backend/src/descriptors/GraphDescriptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "BackendDescriptor.hpp"
#include "IGraphOperation.hpp"
#include <array>
#include <flatbuffers/detached_buffer.h>
#include <hipdnn_flatbuffers_sdk/data_objects/graph_generated.h>
#include <hipdnn_plugin_sdk/PluginApiDataTypes.h>
Expand Down Expand Up @@ -37,6 +38,11 @@ class GraphDescriptor : public HipdnnBackendDescriptorImpl<GraphDescriptor>
// Cleared by invalidateCache() when operations are mutated.
flatbuffers::DetachedBuffer _graphSerializedBuffer;

// Stable identity of a logical finalized graph across serialization round trips.
// Deserialized copies preserve it, so it is not a unique live-object token; use it only to
// key state derived from immutable graph content. Newly generated values use UUID v4.
std::optional<std::array<uint8_t, 16>> _graphId;

// Source of truth for the graph's operations. Populated via setOperations() (C-API flow)
// or eagerly from deserializeGraph() (FlatBuffer flow).
// Stored as IBackendDescriptor so getOperations() can pack them without cross-casting.
Expand Down
19 changes: 12 additions & 7 deletions projects/hipdnn/backend/src/logging/GraphLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,18 @@ std::filesystem::path GraphLogger::getOutputDirectory()
return cachedPath;
}

void GraphLogger::logGraph(const uint8_t* serializedGraph, size_t size)
void GraphLogger::logGraph(const uint8_t* serializedGraph, [[maybe_unused]] size_t size)
{
auto hash = hipdnn_data_sdk::utilities::fnv1aHash(serializedGraph, size);
auto* graph
= flatbuffers::GetRoot<hipdnn_flatbuffers_sdk::data_objects::Graph>(serializedGraph);
const nlohmann::json graphJson = *graph;
auto graphContent = graphJson;

// Graph IDs distinguish logical graph lifetimes for runtime caches, but graph dumps are
// content-addressed. Exclude the ID from the hash so rebuilding an equivalent graph does not
// create another log file; retain it in graphJson for diagnostics.
graphContent.erase("id");
const auto hash = hipdnn_data_sdk::utilities::fnv1aHash(graphContent.dump());
Comment thread
SamuelReeder marked this conversation as resolved.
Resolved

std::ostringstream oss;
oss << "graph_" << std::hex << std::setfill('0') << std::setw(16) << hash << ".json";
Expand All @@ -108,14 +117,10 @@ void GraphLogger::logGraph(const uint8_t* serializedGraph, size_t size)
return;
}

auto* graph
= flatbuffers::GetRoot<hipdnn_flatbuffers_sdk::data_objects::Graph>(serializedGraph);
const nlohmann::json j = *graph;

std::ofstream file(fullPath);
if(file.is_open())
{
file << j.dump(2);
file << graphJson.dump(2);
file.close();
HIPDNN_BACKEND_LOG_INFO("Graph logged to {}", fullPath.string());
}
Expand Down
4 changes: 2 additions & 2 deletions projects/hipdnn/backend/src/logging/GraphLogger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace hipdnn_backend::logging
class GraphLogger
{
public:
/// Logs the graph as JSON. Deduplicates by content hash — if the same
/// flatbuffer binary has already been logged, this is a no-op.
/// Logs the graph as JSON. Deduplicates by graph content while ignoring the graph ID, so
/// independently finalized equivalent graphs share one dump.
/// @param serializedGraph pointer to flatbuffer binary
/// @param size size of the flatbuffer binary
static void logGraph(const uint8_t* serializedGraph, size_t size);
Expand Down
17 changes: 15 additions & 2 deletions projects/hipdnn/backend/tests/TestGraphLogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,34 @@ TEST_F(TestGraphLogger, GraphLoggedWhenEnabled)
EXPECT_TRUE(j.contains("nodes"));
EXPECT_TRUE(j.contains("tensors"));
EXPECT_TRUE(j.contains("name"));
EXPECT_TRUE(j.contains("id"));
}

TEST_F(TestGraphLogger, DuplicateGraphNotLoggedTwice)
TEST_F(TestGraphLogger, EquivalentGraphsAreNotLoggedTwice)
{
hipdnn_data_sdk::utilities::setEnv("HIPDNN_LOG_GRAPH_DIR", _tempDirStr.c_str());
hipdnn_backend::logging::loggerShutdown();

// Finalize the same graph twice
// IDs differ, but graph dumps are deduplicated by graph content.
auto descriptor1 = createAndFinalizeGraph();
auto descriptor2 = createAndFinalizeGraph();

auto jsonFiles = getJsonFilesInDir(_tempDir);
EXPECT_EQ(jsonFiles.size(), 1u);
}

TEST_F(TestGraphLogger, ReLoggingSameFinalizedGraphIsDeduplicated)
{
hipdnn_data_sdk::utilities::setEnv("HIPDNN_LOG_GRAPH_DIR", _tempDirStr.c_str());
hipdnn_backend::logging::loggerShutdown();

auto descriptor = createAndFinalizeGraph();
const auto serialized = descriptor.getSerializedGraph();
logging::GraphLogger::logGraph(static_cast<const uint8_t*>(serialized.ptr), serialized.size);

EXPECT_EQ(getJsonFilesInDir(_tempDir).size(), 1u);
}

TEST_F(TestGraphLogger, DifferentGraphsLoggedSeparately)
{
hipdnn_data_sdk::utilities::setEnv("HIPDNN_LOG_GRAPH_DIR", _tempDirStr.c_str());
Expand Down
Loading
Loading