Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,24 @@ inline bool areDimensionsBroadcastCompatible(const std::vector<int64_t>& inputDi
return true;
}

// Sets a default stride ordering based off the provided stride order.
// Ex. dim = {1,2,3,4} stride_order = {3, 0, 2, 1} for NHWC
// returns {24, 1, 8, 2}
/**
* @brief Computes strides from dimensions and a stride ordering
*
* Generates strides for a tensor based on the provided dimensions and stride order.
* The stride order specifies the memory layout priority (lower values = tighter packing).
*
* @param dim Dimension sizes. The expected ordering depends on the operation type:
* convolution/batchnorm use (N, C, H, W) or (N, C, D, H, W),
* matmul uses (...batch, M, K) or (...batch, K, N).
* @param strideOrder Priority of each dimension in memory (lower = tighter packing).
* Use TensorLayout constants for common layouts.
* @return Strides corresponding to the requested memory layout
*
* @code{.cpp}
* // Example for convolution input: {N=1, C=2, H=3, W=4}
* auto nhwcStrides = generateStrides({1,2,3,4}, {3, 0, 2, 1}); // {24, 1, 8, 2}
* @endcode
*/
inline std::vector<int64_t> generateStrides(const std::vector<int64_t>& dim,
const std::vector<int64_t>& strideOrder)
{
Expand Down Expand Up @@ -309,8 +324,18 @@ static void iterateAlongDimensions(const std::vector<int64_t>& dims, F&& func)
}
}

// Constructs a full tensor indices vector from batch, channel, and spatial components. spatialOffset allows
// skipping initial elements in the spatialIndices vector for convenience.
/**
* @brief Constructs a full tensor indices vector from batch, channel, and spatial components
*
* Builds an index vector following NCHW ordering: batch at position 0, channel at position 1,
* followed by spatial dimensions (H, W for 4D; D, H, W for 5D).
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
*
* @param batchIdx Batch index (position 0 in result)
* @param channelIdx Channel index (position 1 in result)
* @param spatialIndices Spatial dimension indices (positions 2+ in result)
* @param spatialOffset Number of elements to skip at the start of spatialIndices
* @return Combined index vector in NCHW/NCDHW order
*/
static inline std::vector<int64_t> buildTensorIndices(int64_t batchIdx,
int64_t channelIdx,
const std::vector<int64_t>& spatialIndices,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,32 @@
namespace hipdnn_data_sdk::utilities
{

/**
* @brief Describes a tensor memory layout via stride ordering
*
* TensorLayout encodes how tensor dimensions map to memory. The `strideOrder` vector
* specifies the priority of each dimension in memory layout (lower values = tighter
* packing in memory).
*
* @note TensorLayout is primarily used with convolution and batch normalization tensors,
* which follow (N, C, H, W) / (N, C, D, H, W) dimension ordering. Other operations
* such as matmul and pointwise have their own dimension conventions. The TensorLayout
* controls how dimensions map to contiguous memory via strides computed by
* `generateStrides()`.
*
* For example, for a convolution input with dims = {1, 64, 28, 28} (N=1, C=64, H=28, W=28):
* - TensorLayout::NCHW produces strides {50176, 784, 28, 1} (channel-first)
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
* - TensorLayout::NHWC produces strides {50176, 1, 1792, 64} (channel-last)
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
*/
struct TensorLayout
{
std::string name;
std::vector<int64_t> strideOrder;
std::string name; ///< Human-readable layout name (e.g., "NCHW", "NHWC")
std::vector<int64_t> strideOrder; ///< Stride priority per dimension (lower = tighter packing)

static const TensorLayout NCHW;
static const TensorLayout NHWC;
static const TensorLayout NCDHW;
static const TensorLayout NDHWC;
static const TensorLayout NCHW; ///< 4D channel-first layout
static const TensorLayout NHWC; ///< 4D channel-last layout
static const TensorLayout NCDHW; ///< 5D channel-first layout
static const TensorLayout NDHWC; ///< 5D channel-last layout
};

inline const TensorLayout TensorLayout::NCHW{"NCHW", {3, 2, 1, 0}};
Expand Down
6 changes: 6 additions & 0 deletions projects/hipdnn/docs/OperationSupport-ReferenceImpl.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ The following table lists all operations currently supported in the CPU Referenc
- **NCDHW**: Batch, Channels, Depth, Height, Width (3D, channel-first)
- **NDHWC**: Batch, Depth, Height, Width, Channels (3D, channel-last)

> **Note:** The layout names (NCHW, NHWC, etc.) describe the **memory layout** controlled by
> strides. Dimension ordering is operation-specific: convolution and batch normalization use
> `(N, C, H, W)` / `(N, C, D, H, W)` ordering, matmul uses `(...batch, M, K)` ordering,
> and pointwise operations are dimension-agnostic. See the
> [Porting Guide](./PortingGuide.md#tensor-dimensions-and-layouts) for details.

### Implementation
- **CPU Reference**: CPU-based reference implementation for validation

Expand Down
73 changes: 73 additions & 0 deletions projects/hipdnn/docs/PortingGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,79 @@ ninja
| **Device Memory Utility** | Surface<type> | MigratableMemory<type> |
| **Device Memory Access** | Surface<type>::devPtr | MigratableMemory<type>::deviceData() |

### Tensor Dimensions and Layouts

Tensor dimension ordering in hipDNN is **operation-specific**, following the same conventions as
PyTorch and cuDNN. The **memory layout** (channel-first vs channel-last) is always controlled by
strides, not by the order of the dimension vector. Use the `TensorLayout` constants and
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
`generateStrides()` utility to compute strides for common layouts.

#### Convolution

| Tensor | Shape (4D) | Shape (5D) | Description |
|--------|-----------|------------|-------------|
| Input (x) | `(N, C, H, W)` | `(N, C, D, H, W)` | Batch, channels, spatial dims |
| Weights (w) | `(K, C/groups, R, S)` | `(K, C/groups, T, R, S)` | Output channels, input channels per group, kernel spatial dims |
| Output (y) | `(N, K, H_out, W_out)` | `(N, K, D_out, H_out, W_out)` | Batch, output channels, output spatial dims |

```cpp
// Convolution example: dims always follow (N, C, spatial...) ordering
auto x = graph.tensor(TensorAttributes()
.set_dim({1, 64, 28, 28}) // N=1, C=64, H=28, W=28
.set_stride(generateStrides({1, 64, 28, 28}, TensorLayout::NHWC.strideOrder)));

auto w = graph.tensor(TensorAttributes()
.set_dim({128, 64, 3, 3}) // K=128, C=64, R=3, S=3
.set_stride(generateStrides({128, 64, 3, 3}, TensorLayout::NHWC.strideOrder)));
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
```

#### Batch Normalization

| Tensor | Shape | Description |
|--------|-------|-------------|
| Input (x) | `(N, C, H, W)` or `(N, C, D, H, W)` | Same ordering as convolution |
| Scale, Bias, Mean, Variance | `(1, C, 1, 1)` or `(1, C, 1, 1, 1)` | Per-channel parameters |
| Output (y) | Same as input | Shape preserved |

Statistics are computed per-channel over the batch and spatial dimensions.

#### Layer Normalization

| Tensor | Shape | Description |
|--------|-------|-------------|
| Input (x) | `(N, ...)` | Batch first, then feature dims |
| Scale, Bias | `(1, C, H, W)` | Batch dim = 1, feature dims match input |
| Mean, Inv Variance | Stats dims | Batch dims from input, normalized dims = 1 |
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved

Normalization is performed over the feature dimensions (all dims where scale > 1).

#### Matrix Multiplication

| Tensor | Shape | Description |
|--------|-------|-------------|
| A | `(...batch, M, K)` | Leading batch dims, last two are matrix dims |
| B | `(...batch, K, N)` | K must match A's last dim |
| C (output) | `(...batch, M, N)` | Batch dims are broadcast |

Batch dimensions support broadcasting (dims must be equal or divisible).

```cpp
// Matmul example: A(batch, M, K) @ B(batch, K, N) -> C(batch, M, N)
auto a = graph.tensor(TensorAttributes()
.set_dim({4, 128, 64}) // batch=4, M=128, K=64
.set_stride({128*64, 64, 1}));

auto b = graph.tensor(TensorAttributes()
.set_dim({4, 64, 256}) // batch=4, K=64, N=256
.set_stride({64*256, 256, 1}));
```

#### Pointwise Operations

Pointwise operations (ReLU, Sigmoid, Add, Mul, etc.) are **dimension-agnostic** — they accept
tensors of any shape. For binary and ternary operations, inputs are broadcast using NumPy-style
broadcasting rules (dimensions compared right-to-left; compatible if equal or 1).

## Common Pitfalls

### 1. CMAKE_POSITION_INDEPENDENT_CODE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,14 @@ namespace hipdnn_frontend::graph
* This operation normalizes the input tensor across the batch dimension and
* computes running statistics for inference.
*
* **Tensor Shapes:**
* - **X** (input): `(N, C, H, W)` or `(N, C, D, H, W)` — batch, channels, spatial dims
* - **Scale, Bias**: `(1, C, 1, 1)` or `(1, C, 1, 1, 1)` — per-channel parameters
* - **Y** (output): Same shape as X
* - **Mean, Inv_variance**: `(1, C, 1, 1)` or `(1, C, 1, 1, 1)` — per-channel statistics
*
* **Required inputs:**
* - X: Input tensor to normalize (NCHW format)
* - X: Input tensor to normalize
* - Scale: Per-channel scale (gamma) tensor
* - Bias: Per-channel bias (beta) tensor
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ namespace hipdnn_frontend::graph
* operation: y = conv(x, w). The operation computes the convolution of input tensor
* x with filter tensor w.
*
* **Tensor Shapes:**
* - **x** (input): `(N, C, H, W)` or `(N, C, D, H, W)` — batch, channels, spatial dims
* - **w** (weights): `(K, C/groups, R, S)` or `(K, C/groups, T, R, S)` — output channels, input channels per group, kernel spatial dims
* - **y** (output): `(N, K, H_out, W_out)` or `(N, K, D_out, H_out, W_out)` — batch, output channels, output spatial dims
*
* @code{.cpp}
* // Create a 2D convolution with 3x3 kernel, padding=1, stride=1
* auto y = graph.conv_fprop(x, w, ConvFpropAttributes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ namespace hipdnn_frontend::graph
* Unlike batch normalization which normalizes across the batch dimension,
* layer normalization normalizes across the feature dimensions.
*
* **Tensor Shapes:**
* - **X** (input): `(N, ...)` — batch first, then feature dims
* - **Scale, Bias**: `(1, C, H, W)` — batch dim = 1, feature dims match input
* - **Y** (output): Same shape as X
* - **Mean, InvVariance**: Batch dims from input, normalized dims = 1
*
* **Required inputs:**
* - X: Input tensor to normalize
* - Scale: Per-feature scale (gamma) tensor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ namespace hipdnn_frontend::graph
* operation: C = A @ B. Supports batched operations when tensors have
* leading batch dimensions.
*
* **Tensor Shapes:**
* - **A** (left input): `(...batch, M, K)` — leading batch dims, rows, columns
* - **B** (right input): `(...batch, K, N)` — K must match A's last dim
* - **C** (output): `(...batch, M, N)` — batch dims are broadcast
*
* Batch dimensions support broadcasting (dims must be equal or divisible).
*
* @code{.cpp}
* // Matrix multiplication: C = A @ B
* auto c = graph.matmul(a, b, MatmulAttributes());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ namespace hipdnn_frontend::graph
* .set_mode(PointwiseMode::ADD));
* @endcode
*
* **Tensor Shapes:**
* Pointwise operations are dimension-agnostic — input tensors can have any shape.
* For binary and ternary operations, inputs are broadcast using NumPy-style rules
* (dimensions compared right-to-left; compatible if equal or 1).
*
* @see Graph::pointwise(), PointwiseMode
*/
class PointwiseAttributes : public Attributes<PointwiseAttributes>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,19 @@ using hipdnn_data_sdk::types::half;
* - **Virtual tensors**: Intermediate results that don't require explicit memory allocation
* - **Pass-by-value tensors**: Scalar values embedded directly in the tensor
*
* @note **Dimension Ordering**: The expected dimension ordering depends on the operation.
* Convolution and batch normalization tensors use `(N, C, H, W)` or `(N, C, D, H, W)`.
* Matmul tensors use `(...batch, M, K)` for A and `(...batch, K, N)` for B.
* Pointwise operations accept any shape with broadcasting support.
* In all cases, the memory layout is controlled by strides, not by dimension order.
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
* Use `hipdnn_data_sdk::utilities::generateStrides()` to compute strides from a TensorLayout.
*
* @code{.cpp}
* // Create a 4D tensor (NCHW format)
* // Create a 4D convolution input tensor
* // For convolution, dimensions follow (N, C, H, W) ordering
* auto x = Graph::tensor(TensorAttributes()
* .set_dim({1, 64, 28, 28})
* .set_stride({50176, 784, 28, 1})
* .set_dim({1, 64, 28, 28}) // dims: N=1, C=64, H=28, W=28
* .set_stride({50176, 784, 28, 1}) // NCHW layout strides
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
* .set_data_type(DataType::HALF)
* .set_uid(0)
* .set_name("input_x"));
Expand Down Expand Up @@ -272,6 +280,12 @@ class TensorAttributes
* @brief Set the dimensions for this tensor
* @param dim Vector of dimension sizes
* @return Reference to this for method chaining
*
* @note The expected dimension ordering depends on the operation type:
* convolution and batch normalization use (N, C, H, W) / (N, C, D, H, W),
* matmul uses (...batch, M, K) / (...batch, K, N),
* and pointwise operations accept any shape.
* Memory layout is always controlled by strides, not dimension order.
Comment thread
BrianHarrisonAMD marked this conversation as resolved.
Resolved
*/
TensorAttributes&
set_dim(const std::vector<int64_t>& dim) // NOLINT(readability-identifier-naming)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ inline Error validateBatchNormTrainingSpatialDimensions(
// Spatial mode: normalizes over N*spatial_dims per channel
// Requires N*H*W > 1 (or N*D*H*W > 1 for 3D)

// dims are always declared in NCHW & NCDHW order
// Batchnorm dims follow NCHW & NCDHW order
int64_t spatialElements = dims[0]; // Start with N
for(size_t i = 2; i < dims.size(); ++i)
{
Expand Down
Loading