Files
2026-07-13 12:47:05 +08:00

365 lines
14 KiB
Plaintext

/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
//
// @author Yurii Shyrma (iuriish@yahoo.com), created on 20.04.2018
//
#include <array/NDArrayFactory.h>
#include <array/ResultSet.h>
#include <exceptions/cuda_exception.h>
#include <helpers/ConstantTadHelper.h>
#include <helpers/PointersManager.h>
#include <helpers/ShapeUtils.h>
#include <ops/declarable/helpers/transforms.h>
#include <numeric>
#include "execution/cuda/LaunchDims.h"
namespace sd {
namespace ops {
namespace helpers {
///////////////////////////////////////////////////////////////////
template <typename T>
SD_KERNEL static void invertPermutationCuda(const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo) {
const T* x = reinterpret_cast<const T*>(vx);
T* z = reinterpret_cast<T*>(vz);
__shared__ LongType len, totalThreads;
if (threadIdx.x == 0) {
len = shape::length(xShapeInfo);
totalThreads = gridDim.x * blockDim.x;
}
__syncthreads();
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
LongType xCoords[SD_MAX_RANK];
LongType zCoords[SD_MAX_RANK];
LongType xOffset;
LongType zOffset;
for (LongType i = tid; i < len; i += totalThreads) {
INDEX2COORDS(i, shape::rank(xShapeInfo), shape::shapeOf(xShapeInfo), xCoords);
COORDS2INDEX(shape::rank(xShapeInfo), shape::stride(xShapeInfo), xCoords, xOffset);
const LongType index = x[xOffset];
INDEX2COORDS(index, shape::rank(zShapeInfo), shape::shapeOf(zShapeInfo), zCoords);
COORDS2INDEX(shape::rank(zShapeInfo), shape::stride(zShapeInfo), zCoords, zOffset);
z[zOffset] = i;
}
}
///////////////////////////////////////////////////////////////////
template <typename T>
SD_HOST static void invertPermutationCudaLauncher(const int blocksPerGrid, const int threadsPerBlock,
const int sharedMemory, const cudaStream_t* stream, const void* vx,
const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo) {
invertPermutationCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMemory, *stream>>>(vx, xShapeInfo, vz, zShapeInfo);
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "invertPermutationCuda failed");
}
////////////////////////////////////////////////////////////////////////
void invertPermutation(LaunchContext* context, NDArray& input, NDArray& output) {
dim3 invertPermuteDims = invertPermutationDims(input.lengthOf());
PointersManager manager(context, "invertPermutation");
NDArray::prepareSpecialUse({&output}, {&input});
BUILD_SINGLE_SELECTOR(input.dataType(), invertPermutationCudaLauncher,
(invertPermuteDims.x, invertPermuteDims.y, invertPermuteDims.z,context->getCudaStream(), input.specialBuffer(),
input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo()),
SD_COMMON_TYPES);
NDArray::registerSpecialUse({&output}, {&input});
manager.synchronize();
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
SD_KERNEL static void traceCuda(const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, const LongType diagLen) {
const auto x = reinterpret_cast<const T*>(vx);
auto z = reinterpret_cast<T*>(vz);
__shared__ T sharedMem[SD_CUDA_BLOCK_SIZE];
// Shared variables for ranks, shapes, and strides
__shared__ sd::LongType xRank, zRank;
__shared__ const sd::LongType* xShapePtr;
__shared__ const sd::LongType* xStridePtr;
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* zStridePtr;
__shared__ LongType xLen, zLen;
// Cache all shape-related values in thread 0
if (threadIdx.x == 0) {
xRank = shape::rank(xShapeInfo);
zRank = shape::rank(zShapeInfo);
xShapePtr = shape::shapeOf(xShapeInfo);
xStridePtr = shape::stride(xShapeInfo);
zShapePtr = shape::shapeOf(zShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
xLen = shape::length(xShapeInfo);
zLen = shape::length(zShapeInfo); // corresponds to number of matrices
}
__syncthreads();
LongType coords[SD_MAX_RANK];
// One block per each element of z, that is per each matrix
for (LongType m = blockIdx.x; m < zLen; m += gridDim.x) {
INDEX2COORDS(m, zRank, zShapePtr, coords);
LongType zOffset;
COORDS2INDEX(zRank, zStridePtr, coords, zOffset);
sharedMem[threadIdx.x] = 0;
for (LongType i = threadIdx.x; i < diagLen; i += blockDim.x) {
coords[zRank] = coords[zRank + 1] = i;
LongType xOffset;
COORDS2INDEX(xRank, xStridePtr, coords, xOffset);
sharedMem[threadIdx.x] += x[xOffset];
}
__syncthreads();
// Aggregate sum
for (LongType activeThreads = blockDim.x / 2; activeThreads > 0; activeThreads /= 2) {
if (threadIdx.x < activeThreads) {
sharedMem[threadIdx.x] += sharedMem[threadIdx.x + activeThreads];
}
__syncthreads();
}
if (threadIdx.x == 0) {
z[zOffset] = *sharedMem;
}
__syncthreads();
}
}
///////////////////////////////////////////////////////////////////
template <typename T>
static void traceCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, const LongType diagLen) {
traceCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, diagLen);
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "traceCuda failed");
}
///////////////////////////////////////////////////////////////////
void trace(LaunchContext* context, NDArray& input, NDArray& output) {
PointersManager manager(context, "trace");
const LongType diagLen = input.sizeAt(-1) < input.sizeAt(-2) ? input.sizeAt(-1) : input.sizeAt(-2);
const int threadsPerBlock = SD_CUDA_BLOCK_SIZE;
const int blocksPerGrid = (output.lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
const int sharedMem = 1024;
dim3 traceDims2 = traceDims(output.lengthOf());
NDArray::prepareSpecialUse({&output}, {&input});
BUILD_SINGLE_SELECTOR(input.dataType(), traceCudaLauncher,
(traceDims2.y, traceDims2.x, traceDims2.z, context->getCudaStream(), input.specialBuffer(),
input.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), diagLen),
SD_COMMON_TYPES);
NDArray::registerSpecialUse({&output}, {&input});
manager.synchronize();
}
///////////////////////////////////////////////////////////////////
template <typename T>
SD_KERNEL static void triuBPCuda(const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, const int diag) {
const auto x = reinterpret_cast<const T*>(vx); // gradO
auto z = reinterpret_cast<T*>(vz); // gradI
__shared__ int rank, areSameOffsets;
__shared__ LongType len, totalThreads; // xLen = zLen
// Cache shape information
__shared__ const sd::LongType* xShapePtr;
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* xStridePtr;
__shared__ const sd::LongType* zStridePtr;
__shared__ int xRank, zRank;
if (threadIdx.x == 0) {
areSameOffsets = shape::haveSameShapeAndStrides(xShapeInfo, zShapeInfo);
rank = shape::rank(xShapeInfo);
len = shape::length(zShapeInfo);
totalThreads = gridDim.x * blockDim.x;
// Cache shape information
xRank = shape::rank(xShapeInfo);
zRank = shape::rank(zShapeInfo);
xShapePtr = shape::shapeOf(xShapeInfo);
zShapePtr = shape::shapeOf(zShapeInfo);
xStridePtr = shape::stride(xShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
}
__syncthreads();
LongType coords[SD_MAX_RANK];
const LongType tid = blockIdx.x * blockDim.x + threadIdx.x;
for (LongType i = tid; i < len; i += totalThreads) {
INDEX2COORDS(i, zRank, zShapePtr, coords);
sd::LongType zOffset;
COORDS2INDEX(zRank, zStridePtr, coords, zOffset);
if ((coords[rank - 2] + diag > coords[rank - 1])) // row + diag > col
z[zOffset] = 0;
else {
if (areSameOffsets) {
z[zOffset] = x[zOffset];
} else {
sd::LongType xOffset;
COORDS2INDEX(xRank, xStridePtr, coords, xOffset);
z[zOffset] = x[xOffset];
}
}
}
}
///////////////////////////////////////////////////////////////////
template <typename T>
static void triuBPCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, const int diag) {
triuBPCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, diag);
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "triuBP failed");
}
///////////////////////////////////////////////////////////////////
void triuBP(LaunchContext* context, NDArray& input, NDArray& gradO, NDArray& gradI,
const int diagonal) {
const int threadsPerBlock = SD_MAX_NUM_THREADS / 4;
const int blocksPerGrid = (gradO.lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
const int sharedMem = threadsPerBlock * sizeof(LongType) * gradO.rankOf() + 128;
dim3 triuDims2 = triuDims(gradO.lengthOf(),gradO.rankOf());
PointersManager manager(context, "triuBP");
NDArray::prepareSpecialUse({&gradI}, {&gradO});
BUILD_SINGLE_SELECTOR(gradI.dataType(), triuBPCudaLauncher,
(triuDims2.y, triuDims2.x, triuDims2.z, context->getCudaStream(), gradO.specialBuffer(),
gradO.specialShapeInfo(), gradI.specialBuffer(), gradI.specialShapeInfo(), diagonal),
SD_COMMON_TYPES);
NDArray::registerSpecialUse({&gradI}, {&gradO});
manager.synchronize();
}
///////////////////////////////////////////////////////////////////
template <typename T>
SD_KERNEL static void tileBPCuda(const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo,
LongType* globMem) {
const auto x = reinterpret_cast<const T*>(vx); // gradO
auto z = reinterpret_cast<T*>(vz); // gradI
__shared__ int xRank, zRank;
__shared__ LongType numOfXOffsets, zLen, totalThreads;
// Cache shape information
__shared__ const sd::LongType* zShapePtr;
__shared__ const sd::LongType* zStridePtr;
if (threadIdx.x == 0) {
xRank = shape::rank(zShapeInfo);
zRank = shape::rank(zShapeInfo);
zLen = shape::length(zShapeInfo);
numOfXOffsets = shape::length(xShapeInfo) / zLen;
totalThreads = gridDim.x * blockDim.x;
// Cache shape information
zShapePtr = shape::shapeOf(zShapeInfo);
zStridePtr = shape::stride(zShapeInfo);
}
__syncthreads();
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
LongType memBuff[SD_MAX_RANK * 2];
auto xOffsets = globMem + tid * numOfXOffsets;
for (LongType i = tid; i < zLen; i += totalThreads) {
LongType zCoords[SD_MAX_RANK];
LongType zOffset;
INDEX2COORDS(i, zRank, zShapePtr, zCoords);
COORDS2INDEX(zRank, zStridePtr, zCoords, zOffset);
shape::outerArrayOffsets(xOffsets, i, xShapeInfo, zShapeInfo, memBuff, nullptr);
z[zOffset] = x[xOffsets[0]]; // first offset
for (LongType j = 1; j < numOfXOffsets; ++j) // rest offsets
z[zOffset] += x[xOffsets[j]];
}
}
///////////////////////////////////////////////////////////////////
template <typename T>
static void tileBPCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem,
const cudaStream_t* stream, const void* vx, const LongType* xShapeInfo, void* vz,
const LongType* zShapeInfo, LongType* globMem) {
tileBPCuda<T><<<blocksPerGrid, threadsPerBlock, sharedMem, *stream>>>(vx, xShapeInfo, vz, zShapeInfo, globMem);
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "tileBPCudaLauncher failed");
}
//////////////////////////////////////////////////////////////////////////
void tileBP(LaunchContext* context, NDArray gradO /*input*/, NDArray& gradI /*output*/,
const std::vector<LongType> reps) {
auto grad0Shape = gradO.getShapeAsVector();
NDArray memBuff(
'c', grad0Shape, INT64,
context); // empty auxiliary array for storing device memory which will be used in kernel calculations
dim3 tileDims2 = tileDims(gradI.lengthOf(),gradI.rankOf());
PointersManager manager(context, "tileBP");
NDArray::prepareSpecialUse({&gradI}, {&gradO, &memBuff});
BUILD_SINGLE_SELECTOR(gradI.dataType(), tileBPCudaLauncher,
(tileDims2.y, tileDims2.x, tileDims2.z, context->getCudaStream(), gradO.specialBuffer(),
gradO.specialShapeInfo(), gradI.specialBuffer(), gradI.specialShapeInfo(),
reinterpret_cast<sd::LongType*>(memBuff.specialBuffer())),
SD_FLOAT_TYPES);
NDArray::registerSpecialUse({&gradI}, {&gradO, &memBuff});
manager.synchronize();
}
//////////////////////////////////////////////////////////////////////////
void eye(LaunchContext* context, NDArray& output) { output.setIdentity(); }
} // namespace helpers
} // namespace ops
} // namespace sd