- Overview
- Plugin Types
- hipDNN-SDK Library
- Plugin API
- Creating a Kernel Engine Plugin
- Plugin Architecture
- Example: MIOpen Legacy Plugin
hipDNN supports a plugin architecture that allows for modular extensions to the framework. Plugins are designed to be separate projects that extend hipDNN's capabilities without being part of the core repository. The backend discovers and manages these plugins, leveraging them for different aspects of deep learning routines. This architecture provides flexibility in implementation choices and enables optimizations for specific hardware or use cases.
hipDNN will support three types of plugins, each serving a specific purpose:
These plugins help determine the best execution strategy for a given operation or graph. They analyze the computation requirements and available resources to select optimal implementations.
These plugins focus on performance optimization by benchmarking different implementations and tuning parameters for specific hardware configurations.
These plugins provide the actual kernel implementations for operations. They contain the compute kernels that execute on the target hardware (GPUs, accelerators, etc.).
Important
π Current Status: Only kernel engine plugins are presently supported in hipDNN. Support for engine heuristic/selection and benchmarking/tuning plugins will be added in future releases. See the Roadmap for future development plans.
The hipDNN-SDK API is a Header-Only C++ library which provides the requirements needed to create a plugin that hipDNN can consume. It includes:
- Plugin interface definitions
- Data structures for graph representation
- Utilities for serialization/deserialization
- Base classes for engine implementation
For adding new operations to the SDK (schemas, nodes, attributes), see the How-To Guide.
The plugin API defines how kernel engine plugins interact with hipDNN:
- Graph Processing: Graphs are passed in a serialized format to plugins using FlatBuffers
- SDK Data Objects: Plugins use SDK data objects to deserialize and process graphs
- Capability Reporting: Plugins analyze graphs and report whether they can execute them
- Execution Interface: Plugins provide execution methods for supported operations
This section focuses on developing kernel engine plugins; currently the only supported plugin type.
Before creating a plugin, ensure you have built and installed hipDNN. Plugins depend on the hipDNN SDK headers and libraries. See the Quick Start Guide for build and installation instructions.
-
Create Plugin Structure
- Create a new project/repository for your plugin
- Implement the plugin interface defined in
sdk/include/hipdnn_sdk/plugin/EnginePluginApi.h - See MIOpen Legacy Plugin as a reference implementation (currently included but will become a separate project)
-
Implement Plugin API Functions
The underlying implementation below the plugin API level is entirely at the developer's discretion. While the following architectural components are recommended for code organization and maintainability; the only true requirement is to implement the exported API functions defined in
engine_plugin_api.h. However, the common architectural pattern consists of:- Engine Manager: Manages available engines and their capabilities
- Engine: Implements graph execution for specific operations (each engine must have a globally unique
int64_tID) - Execution Plans: Define how operations are executed
-
Build and Deploy Plugin
- Configure CMake to build the plugin as a shared library
- Install to the appropriate plugin directory where hipDNN can discover it at runtime
The Engine Manager is responsible for:
- Creating and managing engine instances
- Reporting supported operations
- Handling resource allocation
- Managing device-specific contexts
For Engine Implementations:
- Each engine must have a unique inter-plugin
int64_tidentifier - Implement the
execute()method for graph execution - Provide
get_supported_operations()to report capabilities - Handle operation-specific kernel launches
- Manage memory transfers and synchronization
Tip
π‘ An engine ID is an integer unique to all loaded plugins. These IDs are used by the backend to identify and select specific engines for execution. You may want to reference other loaded plugins to accrue a set of unused engine IDs.
Execution plans for kernel engines:
- Map hipDNN operations to backend-specific kernel implementations
- Define memory layouts and data transformations
- Specify kernel launch configurations
- Handle device-specific optimizations
In general, the best practices consist of:
- Organizing kernels by operation type
- Efficiently manage device memory allocations and transfers
- Validate inputs and provide meaningful error messages and logs via the sdk
- Properly manage compute streams for asynchronous execution
- Profile kernels and optimize for target hardware
- Validate and document supported operations, hardware requirements, and limitations
- Include unit tests and integration tests
- Plugin API Interface:
sdk/include/hipdnn_sdk/plugin/EnginePluginApi.h - Example Plugin Implementation:
plugins/miopen_legacy_plugin/MiopenLegacyPlugin.cpp - Example Engine Manager:
plugins/miopen_legacy_plugin/EngineManager.hpp - Example Engine Implementation:
plugins/miopen_legacy_plugin/engines/MiopenEngine.cpp
Your plugin should be structured as an independent project:
your_kernel_plugin_project/
βββ CMakeLists.txt
βββ YourPlugin.cpp # Main plugin entry point
βββ EngineManager.cpp # Engine management
βββ engines/
β βββ EngineInterface.hpp # Engine interface
β βββ YourEngine.cpp # Engine implementation
β βββ plans/ # Internal plans
βββ tests/ # Plugin-specific tests
βββ integration_tests/ # End-to-end integration tests
Your plugin's CMakeLists.txt should:
- Build as a shared library
- Link against hipDNN SDK
- Set appropriate install paths
- Link to required compute libraries (ie. HIP)
When building an external plugin, the hipDNN SDK provides CMake variables to help you install your plugin in the correct location:
find_package(hipdnn_sdk CONFIG REQUIRED)
# The SDK provides these variables:
# HIPDNN_PLUGIN_ENGINE_SUBDIR - Subdirectory path for engine plugins (e.g., "hipdnn_plugins/engines")
# HIPDNN_INSTALL_PLUGIN_ENGINE_DIR - Install directory relative to CMAKE_INSTALL_PREFIX
# HIPDNN_PLUGIN_ENGINE_INSTALL_PATH - Full install path for plugins
# Example: Configure your plugin to install to the correct location
install(
TARGETS your_plugin_name
LIBRARY DESTINATION ${HIPDNN_INSTALL_PLUGIN_ENGINE_DIR}
)This ensures your plugin will be installed to the same directory structure that hipDNN expects for plugin discovery.
The hipDNN build system maintains consistent directory structures for plugins:
Build directory structure:
build/lib/
βββ hipdnn_plugins/
βββ engines/
βββ your_plugin.so
Install directory structure:
/opt/rocm/lib/
βββ hipdnn_plugins/
βββ engines/
βββ your_plugin.so
External plugins should follow this same structure to ensure compatibility.
hipDNN supports dynamic plugin loading with configurable search paths.
By default, hipDNN loads plugins from:
./hipdnn_plugins/engines/
This path is relative to the backend shared library location, typically:
/opt/rocm/lib/
Default structure example:
/opt/rocm/lib/
βββ hipdnn_plugins/
βββ engines/
βββ miopen_legacy_plugin.so
βββ other_plugin.so
You can override the default plugin directory using the HIPDNN_PLUGIN_DIR environment variable. This is particularly useful for testing and development:
# Load plugins from a custom directory
export HIPDNN_PLUGIN_DIR=/path/to/test/plugins
# Example: Load test plugins during testing
export HIPDNN_PLUGIN_DIR=/home/user/hipDNN/build/lib/test_pluginsWhen HIPDNN_PLUGIN_DIR is set, hipDNN will only load plugins from the specified directory and supplementary custom paths, ignoring the default location. This allows complete control over which plugins are loaded, which is essential for:
- Running tests with test-specific plugins
- Development and debugging of new plugins
- Isolating production plugins from test plugins
Prior to creating a hipDNN handle, you can specify custom plugin paths using the hipdnnSetEnginePluginPaths_ext function:
hipdnnStatus_t hipdnnSetEnginePluginPaths_ext(
size_t num_paths,
const char* const* plugin_paths,
hipdnnPluginLoadingMode_ext_t loading_mode
);Custom paths can be:
- Relative paths: Resolved from the backend shared library location
- Absolute paths: Used as specified
| Mode | Description |
|---|---|
HIPDNN_PLUGIN_LOADING_ADDITIVE |
Adds new paths to the existing plugin search paths |
HIPDNN_PLUGIN_LOADING_ABSOLUTE |
Only loads from the specified paths |
// Add custom plugin directories
const char* custom_paths[] = {
"/home/user/my_plugins", // Absolute path
"./local_plugins", // Relative to backend shared library
"/opt/custom/hipdnn/plugins"
};
hipdnnSetEnginePluginPaths_ext(
3, // Number of paths
custom_paths, // Array of path strings
HIPDNN_PLUGIN_LOADING_ADDITIVE // Add to existing paths
);Plugins are loaded according to the selected path schema during hipDNN handle creation. Changing paths after handle creation has no effect until another handle is created.
After creating a hipDNN handle, you can query which engine plugins were successfully loaded using the hipdnnGetLoadedEnginePluginPaths_ext function:
hipdnnStatus_t hipdnnGetLoadedEnginePluginPaths_ext(
hipdnnHandle_t handle,
size_t* num_plugin_paths,
char** plugin_paths,
size_t* max_string_len
);This function uses a two-call pattern:
-
First call - Query the number of plugins and required buffer size:
size_t num_plugins = 0; size_t max_len = 0; hipdnnGetLoadedEnginePluginPaths_ext(handle, &num_plugins, nullptr, &max_len);
-
Second call - Retrieve the actual plugin paths:
hipdnnGetLoadedEnginePluginPaths_ext(handle, &num_plugins, nullptr, &max_len); std::vector<std::vector<char>> buffers(num_plugins, std::vector<char>(max_len)); std::vector<char*> ptrs; ptrs.reserve(num_plugins); for(size_t i = 0; i < num_plugins; ++i) ptrs.push_back(buffers[i].data()); hipdnnGetLoadedEnginePluginPaths_ext(handle, &num_plugins, ptrs.data(), &max_len); for(size_t i = 0; i < num_plugins; ++i) { std::cout << "Loaded plugin: " << buffers[i].data() << '\n'; }
Important
Testing is crucial for ensuring plugin reliability and correctness. Plugins should include both unit tests and integration tests to validate their functionality.
Following the Testing Strategy, plugins should organize tests as follows:
your_kernel_plugin_project/
βββ tests/ # Unit tests
β βββ TestEngine.cpp
β βββ TestKernels.cpp
β βββ TestUtilties.cpp
βββ integration_tests/ # End-to-end integration tests
βββ Operation1Test.cpp
βββ Operation2Test.cpp
Unit tests focus on the internal implementation of your plugin components:
- Location:
plugins/<plugin_name>/tests/ - Purpose: Test individual components in isolation (engines, utilities, kernel logic)
- Requirements:
- Must be fast-running
- GPU operations must be marked with
SKIP_IF_NO_DEVICE()macro - Use mocking/stubbing for dependencies where appropriate
- Should work on both Windows and Linux
Integration tests validate end-to-end functionality of your plugin:
- Location:
plugins/<plugin_name>/integration_tests/ - Purpose: Validate correctness of graph execution and accuracy of results
- Requirements:
- Test complete operation graphs
- Validate against reference implementations
- Test different data types, layouts, dimensions, and edge-cases for each
- Enable tests for all supported ASICs
- GPU typically required for meaningful validation
For a comprehensive example of an integration test, see: plugins/miopen_legacy_plugin/integration_tests/BatchnormFwdInferenceIntegrationTest.cpp
Moreover, see our general testing requirements.
Example: MIOpen Legacy Plugin
The MIOpen Legacy Plugin is a complete example of a kernel engine plugin. It demonstrates how a plugin integrates with hipDNN and delegates execution to a backend. Furthermore, it incorporates the recommended structure and best practices for kernel engine plugins.
At a high level, it:
- Initializes and manages the GPU context using MIOpen handles
- Translates hipDNN tensors into MIOpen tensor descriptors
- Dispatches MIOpen kernels to execute operations
- Coordinates streams and handles synchronization
- Main Plugin:
MiopenLegacyPlugin.cpp- Entry point and plugin registration - Engine Manager:
EngineManager.hpp- Manages MIOpen engines - MIOpen Engine:
MiopenEngine.cpp- Implements graph execution using MIOpen kernels