Files
TheRock/rocm-systems/projects/clr/rocclr/platform/ndrange.hpp
T
derek.holloway 7502bf64a4
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
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

306 lines
11 KiB
C++

/*
* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
*
* SPDX-License-Identifier: MIT
*/
#ifndef NDRANGE_HPP_
#define NDRANGE_HPP_
#include "top.hpp"
#include "device/device.hpp"
#include <valarray>
#ifdef DEBUG
#include <cstdio>
#endif // DEBUG
namespace amd {
/*! \addtogroup Runtime
* @{
*
* \addtogroup Program Programs and Kernel functions
* @{
*/
//! An N-dimensions index space.
class NDRange : public EmbeddedObject {
private:
const size_t dimensions_ : 2; //!< Number of dimensions [0-3]
size_t data_[3]; //!< indexes array
private:
//! Construct a new index space for an array of elements (no-copy)
NDRange(size_t dimensions, size_t* elements) : dimensions_(dimensions) {
for (uint i = 0; i < dimensions_; ++i) {
data_[i] = elements[i];
}
}
public:
//! Construct a new index space of the given dimensions.
explicit NDRange(size_t dimensions);
NDRange(size_t dataX, size_t dataY, size_t dataZ) : dimensions_(3) {
data_[0] = dataX;
data_[1] = dataY;
data_[2] = dataZ;
}
//! Copy constructor.
NDRange(const NDRange& space);
//! Destroy the index space.
~NDRange();
//! Copy operator
inline NDRange& operator=(const NDRange& space);
//! Make all elements of this space equal to x.
NDRange& operator=(size_t x);
//! Return the number of dimensions.
size_t dimensions() const { return dimensions_; }
//! Return the element at the given \a index.
size_t& operator[](size_t index) {
assert(index < dimensions_ && "index is out of bounds");
return data_[index];
}
//! Return the element at the given \a index.
size_t operator[](size_t index) const {
assert(index < dimensions_ && "index is out of bounds");
return data_[index];
}
//! Return the sum of this index space elements.
inline size_t sum() const;
//! Return the product of this index space elements (size)
inline size_t product() const;
// Binary operators:
inline friend NDRange operator+(const NDRange& x, const NDRange& y);
inline friend NDRange operator-(const NDRange& x, const NDRange& y);
inline friend NDRange operator*(const NDRange& x, const NDRange& y);
inline friend NDRange operator/(const NDRange& x, const NDRange& y);
inline friend NDRange operator%(const NDRange& x, const NDRange& y);
//! Return true if this index space is identical to \a x.
bool operator==(const NDRange& x) const;
//! Return true if this index space and \a x are different.
bool operator!=(const NDRange& x) const { return !(*this == x); }
//! Return true if all elements are equal to \a x.
bool operator==(size_t x) const;
//! Return true if one element of this space is not equal to \a x.
bool operator!=(size_t x) const { return !(*this == x); }
#ifdef DEBUG
//! Print this index space on the given stream.
void printOn(FILE* file) const;
#endif // DEBUG
const size_t* Data() const { return data_; }
};
//! Stucture to store launch parameters.
struct LaunchParams {
NDRange global_; //!< Total number of work-items in N-dims
NDRange local_; //!< Number of work-items in N-dims in a workgroup.
uint32_t sharedMemBytes_; //!< Shared Memory bytes
NDRange cluster_; //!< Total number of clusters in N-dims
NDRange grid_; //!< Total number of workgroups in grid in N-dims.
bool hipParams_; //!< If this is launched through hipParams_
bool validConfig_; //!< Flag will be set to false when config is not correct.
LaunchParams(size_t globalX, size_t globalY, size_t globalZ, uint32_t localX,
uint32_t localY, uint32_t localZ, uint32_t sharedMemBytes, const Device& device,
uint32_t clusterX = 1, uint32_t clusterY = 1, uint32_t clusterZ = 1,
uint32_t gridX = 1, uint32_t gridY = 1, uint32_t gridZ = 1,
bool hipParams = false) : global_(globalX, globalY, globalZ),
local_(localX, localY, localZ), sharedMemBytes_ (sharedMemBytes),
cluster_(clusterX, clusterY, clusterZ), grid_(gridX, gridY, gridZ),
hipParams_(hipParams), validConfig_(true) {
if (hipParams_) {
// if this is launched through HIPLaunchParams, then we need to check the global does not
// take up more than 32 bits, the max we are allowed to launch in the backend.
if (global_[0] > std::numeric_limits<uint32_t>::max()
|| global_[1] > std::numeric_limits<uint32_t>::max()
|| global_[2] > std::numeric_limits<uint32_t>::max()) {
validConfig_ = false;
}
} else {
// Non HIPLaunchParams, App directly calculated the global and local size,
// manually deduce the grid (total blocks) size.
if (local_[0] == 0 || local_[1] == 0 ||local_[2] == 0) {
validConfig_ = false;
return;
}
grid_[0] = global_[0] / local_[0];
grid_[1] = global_[1] / local_[1];
grid_[2] = global_[2] / local_[2];
}
// If cluster parameters is set, then check if it is divisble by grid (total blocks).
if (clusterX > 1 || clusterY > 1 || clusterZ > 1) {
if (!CheckClusterDivisibility(clusterX, clusterY, clusterZ)) {
validConfig_ = false;
}
}
}
bool CheckClusterDivisibility(uint32_t clusterX, uint32_t clusterY, uint32_t clusterZ) {
// With cluster launch, the total number of blocks or threads the work is launched doesnt
// change, except that the work is launch into different CU/WGP's under the same shader engine.
// So, the grid values are basically split among different CUs based on cluster dims, hence
// grid dims has to be divisble by cluster dims.
if ((grid_[0] % clusterX != 0) || (grid_[1] % clusterY != 0) || (grid_[2] % clusterZ != 0)) {
return false;
}
return true;
}
//! Sometimes we receive cluster launch info from kernel, not through HIP launch kernel APIs.
bool UpdateClusterLaunchParams(uint32_t clusterX, uint32_t clusterY, uint32_t clusterZ) {
// If cluster parameters are not > 1, we dont need to update since it is the default value set.
if (clusterX > 1 || clusterY > 1 || clusterZ > 1) {
if (!CheckClusterDivisibility(clusterX, clusterY, clusterZ)) {
return false;
}
cluster_[0] = clusterX; cluster_[1] = clusterY; cluster_[2] = clusterZ;
}
return true;
}
bool IsValidConfig() const { return validConfig_; }
};
//! Structure to store launch parameters in HIP Style (global and local size needs computation).
struct HIPLaunchParams : public LaunchParams {
HIPLaunchParams(uint32_t gridX, uint32_t gridY, uint32_t gridZ, uint32_t blockX,
uint32_t blockY, uint32_t blockZ, uint32_t sharedMemBytes, const Device& device,
uint32_t globalX_remainder = 0, uint32_t globalY_remainder = 0,
uint32_t globalZ_remainder = 0, uint32_t clusterX = 1,
uint32_t clusterY = 1, uint32_t clusterZ = 1)
: LaunchParams(static_cast<size_t>(gridX) * blockX + globalX_remainder,
static_cast<size_t>(gridY) * blockY + globalY_remainder,
static_cast<size_t>(gridZ) * blockZ + globalZ_remainder,
blockX, blockY, blockZ, sharedMemBytes, device, clusterX, clusterY,
clusterZ, gridX, gridY, gridZ, true /*hipParams*/) {}
};
//! A container for the local and global worksizes.
class NDRangeContainer {
private:
const size_t dimensions_; //!< Number of dimensions.
NDRange offset_; //!< Global work-item offset.
NDRange global_; //!< Total number of work-items in N-dims
NDRange local_; //!< Number of work-items in N-dims in a workgroup.
NDRange cluster_; //!< Number of Cluster in N-dims across work group.
public:
/*! \brief Construct a new nd-range container with the given local
* and global worksizes in \a nDimensions dimensions.
*/
NDRangeContainer(size_t dimensions, const size_t* globalWorkOffset, const size_t* globalWorkSize,
const size_t* localWorkSize, const size_t* clusterWorkSize = nullptr)
: dimensions_(dimensions), offset_(dimensions), global_(dimensions), local_(dimensions),
cluster_(dimensions) {
for (size_t i = 0; i < dimensions; ++i) {
offset_[i] = globalWorkOffset != NULL ? globalWorkOffset[i] : 0;
global_[i] = globalWorkSize[i];
local_[i] = localWorkSize[i];
cluster_[i] = clusterWorkSize != nullptr ? clusterWorkSize[i] : 1;
}
}
//! updates nd-range container
void update(size_t dimensions, const size_t* globalWorkOffset, const size_t* globalWorkSize,
const size_t* localWorkSize) {
for (size_t i = 0; i < dimensions; ++i) {
offset_[i] = globalWorkOffset != NULL ? globalWorkOffset[i] : 0;
global_[i] = globalWorkSize[i];
local_[i] = localWorkSize[i];
}
}
//! Return the number of dimensions.
size_t dimensions() const { return dimensions_; }
//! Return the global workoffset.
const NDRange& offset() const { return offset_; }
NDRange& offset() { return offset_; }
//! Return the global worksize.
const NDRange& global() const { return global_; }
NDRange& global() { return global_; }
//! Return the local worksize.
const NDRange& local() const { return local_; }
NDRange& local() { return local_; }
//! Return the cluster worksize.
const NDRange& cluster() const { return cluster_; }
NDRange& cluster() { return cluster_; }
};
/*! @}\
* @}
*/
inline size_t NDRange::sum() const {
size_t result = data_[0];
for (size_t i = 1; i < dimensions_; ++i) {
result += data_[i];
}
return result;
}
inline size_t NDRange::product() const {
size_t result = data_[0];
for (size_t i = 1; i < dimensions_; ++i) {
result *= data_[i];
}
return result;
}
// This function is in this header file for performance improvements:
inline NDRange& NDRange::operator=(const NDRange& space) {
assert(dimensions_ == space.dimensions_ && "dimensions mismatch");
for (size_t i = 0; i < sizeof(data_) / sizeof(*data_); ++i) {
data_[i] = space.data_[i];
}
return *this;
}
#define DEFINE_NDRANGE_BINARY_OP(op) \
inline NDRange operator op(const NDRange& x, const NDRange& y) { \
assert(x.dimensions_ == y.dimensions_ && "dimensions mismatch"); \
\
size_t dimensions = x.dimensions_; \
size_t result[3] = {0}; \
for (size_t i = 0; i < dimensions; ++i) { \
result[i] = x.data_[i] op y.data_[i]; \
} \
\
return NDRange(dimensions, &result[0]); \
}
DEFINE_NDRANGE_BINARY_OP(+);
DEFINE_NDRANGE_BINARY_OP(-);
DEFINE_NDRANGE_BINARY_OP(*);
DEFINE_NDRANGE_BINARY_OP(/);
DEFINE_NDRANGE_BINARY_OP(%);
#undef DEFINE_NDRANGE_BINARY_OP
} // namespace amd
#endif /*NDRANGE_HPP_*/