onnxruntime/onnxruntime/core/util/matrix_layout.h
Yulong Wang 32af2ba68f
enhance string util functions (#21893)
### Description
- make `MakeString` force inline
- refactor ORT_FORCEINLINE macro - move to one place to avoid macro
redefinition error
- ~~add a `StringJoin` utility~~


### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->
2024-08-29 10:37:50 -07:00

470 lines
12 KiB
C++

/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
* Module Name:
* matrix_layout.h
*
* Abstract:
* Utils for simplifying positioning and striding in tensors. Inspired
* by CUTLASS, striving for 0 runtime cost while promote safety.
*
* Only supports 2D tensors (matrix) for now.
*/
#pragma once
#include <cstdint>
#include <gsl/gsl>
#include "core/util/force_inline.h"
namespace onnxruntime {
//
// Clang-format doesn't handle force inline decorator well, it insists on
// adding extra indentation to the next line, making it very confusing
// to read. So we turn it off for this file.
// clang-format off
//
/**
* @brief A tuple of integers to represent tensor coordinates
*/
template <
int Rank_, ///< Logical rank of coordinate
typename Index_ = int, ///< Index type used for each dimension
typename LongIndex_ = int64_t ///< Long index type used for linear offsets
>
struct Position {
public:
/// Number of elements in Position
static int const kRank = Rank_;
/// Index type used to store elements
using Index = Index_;
/// Type used to represent linear offsets
using LongIndex = LongIndex_;
private:
Index idx[kRank];
public:
ORT_FORCEINLINE explicit Position(Index value = Index(0)) {
for (int i = 0; i < kRank; ++i) {
idx[i] = value;
}
}
/// Constructs from an array of integers
ORT_FORCEINLINE
Position(Index const (&_idx)[kRank]) {
for (int i = 0; i < kRank; ++i) {
idx[i] = _idx[i];
}
}
template <int R, typename I, typename L>
ORT_FORCEINLINE
Position(Position<R, I, L> other) {
for (int i = 0; i < kRank; ++i) {
idx[i] = other[i];
}
}
ORT_FORCEINLINE
Position operator+(Position const& b) const {
Position c;
for (int i = 0; i < kRank; ++i) {
c.idx[i] = idx[i] + b.idx[i];
}
return c;
}
ORT_FORCEINLINE
Position operator-(Position const& b) const {
Position c;
for (int i = 0; i < kRank; ++i) {
c.idx[i] = idx[i] - b.idx[i];
}
return c;
}
ORT_FORCEINLINE
Position operator*(Position const& b) const {
Position c;
for (int i = 0; i < kRank; ++i) {
c.idx[i] = idx[i] * b.idx[i];
}
return c;
}
ORT_FORCEINLINE
Position operator/(Position const& b) const {
Position c;
for (int i = 0; i < kRank; ++i) {
c.idx[i] = idx[i] / b.idx[i];
}
return c;
}
ORT_FORCEINLINE
Position& operator+=(Position const& b) {
for (int i = 0; i < kRank; ++i) {
idx[i] += b.idx[i];
}
return *this;
}
ORT_FORCEINLINE
Position& operator-=(Position const& b) {
for (int i = 0; i < kRank; ++i) {
idx[i] -= b.idx[i];
}
return *this;
}
ORT_FORCEINLINE
Position& operator*=(Position const& b) {
for (int i = 0; i < kRank; ++i) {
idx[i] *= b.idx[i];
}
return *this;
}
ORT_FORCEINLINE
Position& operator/=(Position const& b) {
for (int i = 0; i < kRank; ++i) {
idx[i] /= b.idx[i];
}
return *this;
}
ORT_FORCEINLINE Index& operator[](int dim) { return idx[dim]; }
ORT_FORCEINLINE Index const& operator[](int dim) const { return idx[dim]; }
ORT_FORCEINLINE bool operator==(Position const& b) const {
bool equal = true;
for (int i = 0; equal && i < kRank; ++i) {
equal = (idx[i] == b.idx[i]);
}
return equal;
}
ORT_FORCEINLINE bool operator!=(Position const& b) const { return !(*this == b); }
ORT_FORCEINLINE
Position& clamp(Position const& max, Position const& min = Position()) {
for (int i = 0; i < kRank; ++i) {
idx[i] = std::max(std::min(idx[i], max.idx[i]), min.idx[i]);
}
return *this;
}
ORT_FORCEINLINE
Index sum() const {
Index sum_(idx[0]);
for (int i = 1; i < kRank; ++i) {
sum_ += idx[i];
}
return sum_;
}
ORT_FORCEINLINE
LongIndex product() const {
LongIndex product_(idx[0]);
for (int i = 1; i < kRank; ++i) {
product_ *= idx[i];
}
return product_;
}
};
template <typename T, typename L = int64_t>
Position<2, T, L> make_Position(T _0, T _1) {
T values[2] = {_0, _1};
return Position<2, T, L>(values);
}
template <typename T, typename L = int64_t>
Position<3, T, L> make_Position(T _0, T _1, T _2) {
T values[3] = {_0, _1, _2};
return Position<2, T, L>(values);
}
/// Describes the size of a matrix tile
template <
int Row_, ///< rows of a matrix
int Column_ ///< columns of a matrix
>
struct MatrixShape {
static int const kRow = Row_; ///< rows of a matrix
static int const kColumn = Column_; ///< columns of a matrix
static int const kCount = Row_ * Column_; ///< total number of elements in a matrix
ORT_FORCEINLINE static Position<2> toCoord() {
return make_Position(kRow, kColumn);
}
};
/**
* @brief Defines a mapping from logical coordinate to linear memory
* offsets in a row major layout matrix
*/
class RowMajorLayout {
public:
/// Index type used for coordinates
using Index = int;
/// Long index type used for offsets
using LongIndex = int64_t;
/// Logical coordinate
using MatCoord = Position<2, Index, LongIndex>;
private:
Index stride_;
public:
ORT_FORCEINLINE
RowMajorLayout(Index ldm = 0) : stride_(ldm) {}
ORT_FORCEINLINE static RowMajorLayout packed(MatCoord const& extent) {
return RowMajorLayout(extent[1]);
}
/// Returns the offset of a coordinate in linear memory.
/// Assumes coordinate has convention (row, column)
ORT_FORCEINLINE
LongIndex operator()(MatCoord const& coord) const {
return LongIndex(coord[0]) * stride_ + coord[1];
}
/// Inverse of layout function, mapping linear offset to logical coordinate
ORT_FORCEINLINE
MatCoord inverse(LongIndex offset) const {
return make_Position(Index(offset / stride_), Index(offset % stride_));
}
ORT_FORCEINLINE
Index stride() const {
return stride_;
}
};
class ColumnMajorLayout {
public:
/// Index type used for coordinates
using Index = int;
/// Long index type used for offsets
using LongIndex = int64_t;
/// Logical coordinate
using MatCoord = Position<2, Index, LongIndex>;
private:
Index stride_;
public:
ORT_FORCEINLINE
ColumnMajorLayout(Index ldm = 0) : stride_(ldm) {}
ORT_FORCEINLINE static ColumnMajorLayout packed(MatCoord const& extent) {
return ColumnMajorLayout(extent[0]);
}
/// Returns the offset of a coordinate in linear memory.
/// Assumes coordinate has convention (row, column)
ORT_FORCEINLINE
LongIndex operator()(MatCoord const& coord) const {
return LongIndex(coord[1]) * LongIndex(stride_) + coord[0];
}
/// Inverse of layout function, mapping linear offset to logical coordinate
ORT_FORCEINLINE
MatCoord inverse(LongIndex offset) const {
return make_Position(Index(offset % stride_), Index(offset / stride_));
}
ORT_FORCEINLINE
Index stride() const {
return stride_;
}
};
/**
* @brief A reference to a tensor, with a layout object to map logical
* coordinates to linear offsets.
*/
template <
/// Data type of element stored within tensor, must be numerical types
typename Element_,
/// Defines a mapping from logical coordinate to linear memory offsets
typename Layout_,
/// If true, extra bounds checking is performed on all accesses
bool ExtraBoundsCheck_ = false>
class MatrixRef {
public:
/// Data type of individual access
using Element = Element_;
using Reference = Element&;
/// Mapping function from logical coordinate to linear memory
using Layout = Layout_;
/// Index type
using Index = typename Layout::Index;
/// Long index used for pointer offsets
using LongIndex = typename Layout::LongIndex;
/// Coordinate in logical tensor space
using MatCoord = typename Layout::MatCoord;
/// MatrixRef to constant data
using ConstMatrixRef = MatrixRef<
typename std::remove_const<Element>::type const,
Layout, ExtraBoundsCheck_>;
/// MatrixRef to non-constant data
using NonConstMatrixRef = MatrixRef<
typename std::remove_const<Element>::type,
Layout, ExtraBoundsCheck_>;
static constexpr bool IsNonConstRef = std::is_same<NonConstMatrixRef, MatrixRef<Element_, Layout_>>::value;
private:
/// Pointer to data
gsl::span<Element> data_;
/// Shape of matrix
MatCoord shape_;
/// Layout object maps logical coordinates to linear offsets
Layout layout_;
public:
ORT_FORCEINLINE
MatrixRef() : data_() {}
ORT_FORCEINLINE
MatrixRef(
gsl::span<Element> const& data, ///< pointer to start of tensor
MatCoord const& shape ///< shape of tensor
) : data_(data), shape_(shape), layout_(Layout::packed(shape)) {
Expects(data_.size() >= size_t(shape_.product()));
}
ORT_FORCEINLINE
MatrixRef(
Element* ptr, ///< pointer to start of tensor
LongIndex size, ///< size of tensor in elements
MatCoord const& shape ///< shape of tensor
) : data_(ptr, size), shape_(shape), layout_(Layout::packed(shape)) {
Expects(data_.size() >= shape_.product());
}
/// Converting constructor from MatrixRef to non-constant data.
template <typename _Magic = int>
ORT_FORCEINLINE
MatrixRef(
NonConstMatrixRef const& ref, ///< MatrixRef to non-const data
/// SFINAE trick to avoid creating a copy-constructor when Element_ is already non-const
[[maybe_unused]] _Magic magic = (typename std::enable_if<!IsNonConstRef, _Magic>::type)0
) : data_(ref.data()), shape_(ref.shape()), layout_(Layout::packed(ref.shape())) {}
ORT_FORCEINLINE
ConstMatrixRef const_ref() const {
return ConstMatrixRef(data_, shape_);
}
ORT_FORCEINLINE
NonConstMatrixRef non_const_ref() {
return NonConstMatrixRef(
const_cast<typename std::remove_const<Element>::type*>(data_.data()),
data_.size(), shape_);
}
/// Returns true if the MatrixRef is non-null
ORT_FORCEINLINE
bool good() const { return !data_.empty(); }
ORT_FORCEINLINE
gsl::span<Element> const& data() const { return data_; }
ORT_FORCEINLINE
MatCoord const& shape() const { return shape_; }
ORT_FORCEINLINE
Layout& layout() { return layout_; }
ORT_FORCEINLINE
Layout layout() const { return layout_; }
ORT_FORCEINLINE
Index stride() const { return layout_.stride(); }
ORT_FORCEINLINE
Index& stride() { return layout_.stride(); }
/// Computes the offset of an index from the origin of the tensor
ORT_FORCEINLINE
LongIndex offset(MatCoord const& coord) const {
if constexpr (ExtraBoundsCheck_) {
Expects(coord[0] >= 0 && coord[0] < shape_[0]);
Expects(coord[1] >= 0 && coord[1] < shape_[1]);
}
return layout_(coord);
}
/// Returns a reference to the element at a given Coord
ORT_FORCEINLINE
Reference at(MatCoord const& coord) const {
return data_[offset(coord)];
}
ORT_FORCEINLINE
Reference at(int row, int col) const {
return data_[offset(make_Position(row, col))];
}
/// Returns a reference to the element at a given Coord
ORT_FORCEINLINE
Reference operator[](MatCoord const& coord) const {
return data_[offset(coord)];
}
};
/// Constructs a MatrixRef, deducing types from arguments.
template <
typename Element,
typename Layout = RowMajorLayout,
bool ExtraBoundsCheck = false>
ORT_FORCEINLINE
MatrixRef<Element, Layout, ExtraBoundsCheck>
make_MatrixRef(
Element* ptr,
int64_t size,
typename Layout::MatCoord const& shape) {
return MatrixRef<Element, Layout, ExtraBoundsCheck>(ptr, size, shape);
}
template <
typename Element,
typename Layout = RowMajorLayout,
bool ExtraBoundsCheck = false>
ORT_FORCEINLINE
MatrixRef<Element, Layout, ExtraBoundsCheck>
make_MatrixRef(
const gsl::span<Element>& span,
typename Layout::MatCoord const& shape) {
return MatrixRef<Element, Layout, ExtraBoundsCheck>(span, shape);
}
// clang-format off
} // namespace onnxruntime