CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
548 lines
17 KiB
C++
548 lines
17 KiB
C++
/*******************************************************************************
|
|
*
|
|
* MIT License
|
|
*
|
|
* Copyright (C) 2022-2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#pragma once
|
|
|
|
#include <cassert>
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <numeric>
|
|
#include <vector>
|
|
|
|
#include <Tensile/DataTypes.hpp>
|
|
#include <Tensile/Debug.hpp>
|
|
#include <tensilelitehost/export.h>
|
|
#include <Tensile/Utils.hpp>
|
|
|
|
namespace TensileLite
|
|
{
|
|
template <typename SizeIter>
|
|
inline size_t CoordCount(SizeIter sizeBegin, SizeIter sizeEnd)
|
|
{
|
|
size_t rv = 1;
|
|
|
|
while(sizeBegin != sizeEnd)
|
|
{
|
|
rv *= *sizeBegin;
|
|
sizeBegin++;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
template <typename CoordIter, typename SizeIter>
|
|
inline size_t CoordFlattenIndex(CoordIter coordBegin,
|
|
CoordIter coordEnd,
|
|
SizeIter sizeBegin,
|
|
SizeIter sizeEnd)
|
|
{
|
|
auto coord = coordEnd - 1;
|
|
auto size = sizeEnd - 2;
|
|
size_t rv = 0;
|
|
|
|
while(coord != coordBegin)
|
|
{
|
|
rv = (rv + *coord) * (*size);
|
|
|
|
coord--;
|
|
size--;
|
|
}
|
|
|
|
rv += *coord;
|
|
|
|
return rv;
|
|
}
|
|
|
|
template <typename CoordIter, typename SizeIter>
|
|
inline void CoordNumbered(
|
|
size_t num, CoordIter coordBegin, CoordIter coordEnd, SizeIter sizeBegin, SizeIter sizeEnd)
|
|
{
|
|
auto coord = coordBegin;
|
|
auto size = sizeBegin;
|
|
|
|
while(coord != coordEnd && size != sizeEnd)
|
|
{
|
|
*coord = num % *size;
|
|
num /= *size;
|
|
|
|
coord++;
|
|
size++;
|
|
}
|
|
|
|
if(coord != coordEnd || size != sizeEnd)
|
|
throw std::runtime_error("Inconsistent size of coordinates.");
|
|
}
|
|
|
|
template <typename CoordIter, typename SizeIter>
|
|
inline void CoordNumberedExclude(size_t num,
|
|
CoordIter coordBegin,
|
|
CoordIter coordEnd,
|
|
SizeIter sizeBegin,
|
|
SizeIter sizeEnd,
|
|
size_t excludeDim)
|
|
{
|
|
auto coord = coordBegin;
|
|
auto size = sizeBegin;
|
|
size_t currentDim = 0;
|
|
while(coord != coordEnd && size != sizeEnd)
|
|
{
|
|
if(currentDim != excludeDim)
|
|
{
|
|
*coord = num % *size;
|
|
num /= *size;
|
|
}
|
|
currentDim++;
|
|
coord++;
|
|
size++;
|
|
}
|
|
|
|
if(coord != coordEnd || size != sizeEnd)
|
|
throw std::runtime_error("Inconsistent size of coordinates.");
|
|
}
|
|
|
|
template <typename CoordIter, typename SizeIter>
|
|
inline bool IncrementCoord(CoordIter coordBegin,
|
|
CoordIter coordEnd,
|
|
SizeIter sizeBegin,
|
|
SizeIter sizeEnd)
|
|
{
|
|
auto coord = coordBegin;
|
|
auto size = sizeBegin;
|
|
|
|
while(coord != coordEnd)
|
|
{
|
|
(*coord)++;
|
|
if(*coord < *size)
|
|
return true;
|
|
|
|
*coord = 0;
|
|
|
|
coord++;
|
|
size++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Describes a tensor including dimensions, memory layout, and data type.
|
|
* Decoupled from any particular pointer value or memory location.
|
|
*
|
|
* Provides functions for indexing and otherwise iterating through a tensor.
|
|
*/
|
|
class TENSILELITEHOST_EXPORT TensorDescriptor
|
|
{
|
|
public:
|
|
static const size_t UseDefaultStride;
|
|
|
|
TensorDescriptor() {}
|
|
|
|
TensorDescriptor(const char* name)
|
|
: m_name(name)
|
|
{
|
|
}
|
|
|
|
template <typename IterA, typename IterB>
|
|
TensorDescriptor(const char* name,
|
|
rocisa::DataType t,
|
|
IterA sizesBegin,
|
|
IterA sizesEnd,
|
|
IterB stridesBegin,
|
|
IterB stridesEnd)
|
|
: m_name(name)
|
|
, m_sizes(sizesBegin, sizesEnd)
|
|
, m_strides(stridesBegin, stridesEnd)
|
|
, m_dataType(t)
|
|
{
|
|
this->calculate();
|
|
}
|
|
|
|
template <typename Iter>
|
|
TensorDescriptor(const char* name, rocisa::DataType t, Iter sizesBegin, Iter sizesEnd)
|
|
: m_name(name)
|
|
, m_sizes(sizesBegin, sizesEnd)
|
|
, m_dataType(t)
|
|
{
|
|
this->calculate();
|
|
}
|
|
|
|
TensorDescriptor(const char* name, rocisa::DataType t, std::initializer_list<size_t> sizes)
|
|
: m_name(name)
|
|
, m_sizes(sizes)
|
|
, m_dataType(t)
|
|
|
|
{
|
|
this->calculate();
|
|
}
|
|
|
|
TensorDescriptor(const char* name,
|
|
rocisa::DataType t,
|
|
std::initializer_list<size_t> sizes,
|
|
std::initializer_list<size_t> strides)
|
|
: m_name(name)
|
|
, m_sizes(sizes)
|
|
, m_strides(strides)
|
|
, m_dataType(t)
|
|
{
|
|
this->calculate();
|
|
}
|
|
|
|
TensorDescriptor(const TensorDescriptor& rhs)
|
|
: m_name(rhs.m_name)
|
|
, m_sizes(rhs.m_sizes)
|
|
, m_strides(rhs.m_strides)
|
|
, m_totalLogicalElements(rhs.m_totalLogicalElements)
|
|
, m_totalAllocatedElements(rhs.m_totalAllocatedElements)
|
|
, m_dataType(rhs.m_dataType)
|
|
, m_isOutput(rhs.m_isOutput)
|
|
{
|
|
}
|
|
|
|
void resize(std::initializer_list<size_t>& sizes, std::initializer_list<size_t>& strides)
|
|
{
|
|
m_sizes = sizes;
|
|
m_strides = strides;
|
|
this->calculate();
|
|
}
|
|
|
|
void setName(const char* name)
|
|
{
|
|
m_name = name;
|
|
}
|
|
|
|
const std::string& getName() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
void setAsOutput(bool isOutput)
|
|
{
|
|
m_isOutput = isOutput;
|
|
}
|
|
|
|
bool isOutput() const
|
|
{
|
|
return m_isOutput;
|
|
}
|
|
|
|
inline void calculate()
|
|
{
|
|
if(m_sizes.empty())
|
|
{
|
|
m_strides = m_sizes;
|
|
m_totalLogicalElements = 0;
|
|
m_totalAllocatedElements = 0;
|
|
return;
|
|
}
|
|
|
|
m_strides.resize(m_sizes.size(), UseDefaultStride);
|
|
if(m_strides[0] == UseDefaultStride)
|
|
{
|
|
m_strides[0] = 1;
|
|
}
|
|
m_totalLogicalElements = m_sizes[0];
|
|
|
|
for(int i = 1; i < m_sizes.size(); i++)
|
|
{
|
|
m_totalLogicalElements *= m_sizes[i];
|
|
|
|
if(m_strides[i] == UseDefaultStride)
|
|
{
|
|
m_strides[i] = m_strides[i - 1] * m_sizes[i - 1];
|
|
}
|
|
}
|
|
|
|
m_totalAllocatedElements = 1;
|
|
for(int i = 0; i < m_sizes.size(); i++)
|
|
m_totalAllocatedElements += m_strides[i] * (m_sizes[i] - 1);
|
|
|
|
if(Debug::Instance().printTensorInfo())
|
|
{
|
|
#ifndef CODE_COVERAGE
|
|
std::cout << "TensorDescriptor:calculate " << *this
|
|
<< "totalLogicalElements=" << m_totalLogicalElements
|
|
<< " totalAllocatedElem=" << m_totalAllocatedElements << "\n";
|
|
#endif
|
|
}
|
|
}
|
|
|
|
const std::vector<size_t>& sizes() const
|
|
{
|
|
return m_sizes;
|
|
}
|
|
const std::vector<size_t>& strides() const
|
|
{
|
|
return m_strides;
|
|
}
|
|
bool empty() const
|
|
{
|
|
return m_sizes.empty();
|
|
}
|
|
|
|
void appendDim(size_t logicalCount);
|
|
void appendDim(size_t logicalCount, size_t allocatedCount);
|
|
|
|
/**
|
|
* Returns the number of elements of padding in the given dimension (0 if
|
|
* unpadded). May be negative if stride is less than size
|
|
*/
|
|
int64_t dimensionPadding(size_t dim) const;
|
|
|
|
/**
|
|
* Collapses dimensions in the interval [begin, end).
|
|
*
|
|
* preconditions:
|
|
* - end >= begin
|
|
* - begin < dimensions()
|
|
* - end <= dimensions()
|
|
* - dimensions in the interval [begin, end-1) are not padded.
|
|
*
|
|
* postconditions:
|
|
* - dimensions() is diminished by end-begin
|
|
* - total elements (allocated and logical) remain the same
|
|
* - dimension 'begin' is the product of all the dimensions in the interval
|
|
* [begin, end).
|
|
*/
|
|
void collapseDims(size_t begin, size_t end);
|
|
|
|
size_t dimensions() const
|
|
{
|
|
return m_sizes.size();
|
|
}
|
|
size_t totalLogicalElements() const
|
|
{
|
|
return m_totalLogicalElements;
|
|
}
|
|
size_t totalAllocatedElements() const
|
|
{
|
|
return m_totalAllocatedElements;
|
|
}
|
|
size_t totalAllocatedBytes() const
|
|
{
|
|
// info.elementSize is the per-element segment size in bytes
|
|
// (post-PR #6499: ElementSize = sizeof(T) / Packing). The
|
|
// total byte count is integral as long as the element count
|
|
// is a whole number of packed containers.
|
|
auto const info = DataTypeInfo::Get(m_dataType);
|
|
assert(totalAllocatedElements() % info.packing == 0);
|
|
return multiplyElementSize(totalAllocatedElements(), info.elementSize);
|
|
}
|
|
|
|
float elementBytes() const
|
|
{
|
|
// info.elementSize is already the per-element segment size in
|
|
// bytes (post-PR #6499: BaseTypeInfo::ElementSize = sizeof(T) /
|
|
// Packing), so this returns 0.5 for Float4 and 0.75 for
|
|
// Float6/BFloat6. Callers should pair this with
|
|
// multiplyElementSize() from Utils.hpp when converting element
|
|
// counts to byte counts.
|
|
return DataTypeInfo::Get(m_dataType).elementSize;
|
|
}
|
|
|
|
void setDataType(rocisa::DataType type)
|
|
{
|
|
m_dataType = type;
|
|
}
|
|
|
|
rocisa::DataType dataType() const
|
|
{
|
|
return m_dataType;
|
|
}
|
|
|
|
template <typename Container>
|
|
inline size_t index(Container const& indices) const
|
|
{
|
|
if(indices.size() != dimensions())
|
|
throw std::runtime_error("Incorrect number of indices.");
|
|
|
|
for(int i = 0; i < indices.size(); i++)
|
|
if(indices[i] >= m_sizes[i])
|
|
throw std::runtime_error("Index out of bounds.");
|
|
|
|
return std::inner_product(indices.begin(), indices.end(), m_strides.begin(), (size_t)0);
|
|
}
|
|
|
|
template <typename T>
|
|
inline size_t index(std::initializer_list<T> indices) const
|
|
{
|
|
if(indices.size() != dimensions())
|
|
throw std::runtime_error("Incorrect number of indices.");
|
|
|
|
for(auto i = std::make_pair(indices.begin(), m_sizes.begin()); i.first != indices.end();
|
|
i.first++, i.second++)
|
|
if(*i.first >= *i.second)
|
|
throw std::runtime_error("Index out of bounds.");
|
|
|
|
return std::inner_product(indices.begin(), indices.end(), m_strides.begin(), (size_t)0);
|
|
}
|
|
|
|
template <class... Ts,
|
|
typename = typename std::enable_if<
|
|
std::is_integral<typename std::common_type<Ts...>::type>::value>::type>
|
|
inline size_t index(Ts... is) const
|
|
{
|
|
return this->index({is...});
|
|
}
|
|
|
|
inline bool incrementCoord(std::vector<size_t>& coord, size_t firstDimension = 0) const
|
|
{
|
|
if(coord.size() != dimensions())
|
|
throw std::runtime_error(concatenate(
|
|
"Invalid coordinate size ", coord.size(), " for ", dimensions(), "-tensor"));
|
|
|
|
if(firstDimension >= dimensions())
|
|
return false;
|
|
|
|
return IncrementCoord(
|
|
coord.begin() + firstDimension, coord.end(), m_sizes.begin(), m_sizes.end());
|
|
}
|
|
|
|
bool operator==(const TensorDescriptor& rhs) const;
|
|
bool operator!=(const TensorDescriptor& rhs) const;
|
|
|
|
std::string ToString() const;
|
|
|
|
friend TENSILELITEHOST_EXPORT std::ostream& operator<<(std::ostream& stream,
|
|
const TensorDescriptor& t);
|
|
|
|
private:
|
|
std::string m_name;
|
|
std::vector<size_t> m_sizes;
|
|
std::vector<size_t> m_strides;
|
|
|
|
size_t m_totalLogicalElements = 0;
|
|
size_t m_totalAllocatedElements = 0;
|
|
|
|
rocisa::DataType m_dataType = rocisa::DataType::Float;
|
|
|
|
bool m_isOutput = false;
|
|
};
|
|
|
|
TENSILELITEHOST_EXPORT std::ostream& operator<<(std::ostream& stream, const TensorDescriptor& t);
|
|
|
|
template <typename T>
|
|
void WriteTensor1D(std::ostream& stream,
|
|
T* data,
|
|
TensorDescriptor const& desc,
|
|
bool decorated = true)
|
|
{
|
|
if(desc.dimensions() != 1)
|
|
throw std::runtime_error(
|
|
"WriteTensor1D is only compatible with 1-dimensional tensors.");
|
|
|
|
if(decorated)
|
|
stream << "[";
|
|
|
|
constexpr size_t packing = TypeInfo<std::remove_cv_t<T>>::Packing;
|
|
|
|
if(desc.sizes()[0] > 0)
|
|
stream << data[0];
|
|
|
|
for(size_t i = packing; i < desc.sizes()[0]; i += packing)
|
|
stream << " " << data[i / packing];
|
|
|
|
if(decorated)
|
|
stream << "]" << std::endl;
|
|
}
|
|
|
|
/**
|
|
* @brief Writes a tensor to an output stream.
|
|
*
|
|
* \param stream The stream to write to
|
|
* \param data Pointer to the tensor data
|
|
* \param desc Tensor descriptor
|
|
* \param ptrValue Pointer value to print to describe the location of the data.
|
|
* \param decorated Print brackets [] to indicate start/end of tensor dims
|
|
*/
|
|
template <typename T>
|
|
void WriteTensor(std::ostream& stream,
|
|
T const* data,
|
|
TensorDescriptor const& desc,
|
|
T const* ptrValue = nullptr,
|
|
bool decorated = true)
|
|
{
|
|
stream << "Tensor(";
|
|
streamJoin(stream, desc.sizes(), ", ");
|
|
stream << ", data_ptr: " << data << ")" << std::endl;
|
|
|
|
if(desc.dimensions() == 0)
|
|
return;
|
|
|
|
if(desc.dimensions() == 1)
|
|
{
|
|
WriteTensor1D(stream, data, desc, decorated);
|
|
return;
|
|
}
|
|
|
|
auto const& sizes = desc.sizes();
|
|
std::vector<size_t> coord(desc.dimensions(), 0);
|
|
const auto stride0 = desc.strides()[0];
|
|
|
|
auto upperDimCount = CoordCount(sizes.begin() + 2, sizes.end());
|
|
|
|
for(size_t idx = 0; idx < upperDimCount; idx++)
|
|
{
|
|
CoordNumbered(idx, coord.begin() + 2, coord.end(), sizes.begin() + 2, sizes.end());
|
|
|
|
coord[0] = 0;
|
|
coord[1] = 0;
|
|
|
|
if(decorated)
|
|
{
|
|
stream << "(";
|
|
streamJoin(stream, coord, ", ");
|
|
stream << ")" << std::endl << "[" << std::endl;
|
|
}
|
|
|
|
for(coord[1] = 0; coord[1] < sizes[1]; coord[1]++)
|
|
{
|
|
coord[0] = 0;
|
|
|
|
auto const* localPtr = data + (desc.index(coord) / TypeInfo<T>::Packing);
|
|
|
|
if(sizes[0] > 0)
|
|
stream << localPtr[0];
|
|
|
|
for(coord[0] = TypeInfo<T>::Packing; coord[0] < sizes[0]; coord[0]+=TypeInfo<T>::Packing)
|
|
{
|
|
stream << " " << localPtr[coord[0] * stride0 / TypeInfo<T>::Packing];
|
|
}
|
|
|
|
stream << std::endl;
|
|
}
|
|
|
|
if(decorated)
|
|
{
|
|
stream << "]" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace TensileLite
|