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

2832 lines
112 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
******************************************************************************/
#include <cuda.h>
#include <exceptions/cuda_exception.h>
#include <exceptions/datatype_exception.h>
#include <execution/AffinityManager.h>
#include <helpers/BlasHelper.h>
#include <helpers/CudaLaunchHelper.h>
#include <helpers/DebugHelper.h>
#include <helpers/PointersManager.h>
#include <helpers/threshold.h>
#include <legacy/NativeOpExecutioner.h>
#include <legacy/NativeOps.h>
#include <loops/reduce_bool.h>
#include <loops/reduce_long.h>
#include <loops/scalar.h>
#include <loops/transform_any.h>
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/OpExecutionLogger.h>
#include <graph/OpContextLifecycleTracker.h>
#include <ops/specials_cuda.h>
#include <system/buffer.h>
#include <helpers/ConstantHelper.h>
#include <helpers/ConstantShapeHelper.h>
#include <helpers/ConstantTadHelper.h>
#include <curand.h>
#include <helpers/DebugHelper.h>
#include <execution/cuda/LaunchDims.h>
#include <loops/special_kernels.h>
#include "../../array/ShapeList.h"
#include "../../helpers/shape.h"
#include "../../ops/declarable/DeclarableOp.h"
#include "../../system/common.h"
#include "../NativeOpExecutioner.h"
#include "../NativeOps.h"
#include <system/type_boilerplate.h>
#include <loops/special_kernels.h>
#include <system/selective_rendering.h>
#include <execution/LaunchContext.h>
cudaDeviceProp *deviceProperties;
cudaFuncAttributes *funcAttributes = new cudaFuncAttributes[64];
int blockLimit = 128;
int maxThreads = 512;
bool allowedP2P = false;
bool supportedP2P = false;
//note we only include this if we're running gcc linux
//and should not be enabled in default builds.
#if defined(SD_GCC_FUNCTRACE)
#include <cxxabi.h> // needed __cxa_demangle
#include <dlfcn.h> // needed for dladdr
#include "exceptions/backward.hpp"
#include "execution/cuda/LaunchDims.h"
//note this is outside extern C. This is fine.
#endif
int minThreads = 32;
// this method just does type conversion in fancy way
int getDeviceId(sd::Pointer ptrToDeviceId) { return (int)(sd::LongType)ptrToDeviceId; }
// Function to execute a custom operation with context
sd::Status execCustomOp2(sd::Pointer *extraPointers, sd::LongType hash, Context *opContext) {
try {
// Retrieve the operation based on the hash
auto op = sd::ops::OpRegistrator::getInstance().getOperation(hash);
if (op == nullptr) {
throw std::invalid_argument("Operation not found for the given hash.");
}
#if defined(SD_GCC_FUNCTRACE)
// Set op name BEFORE execute() so allocations during execution are tagged
if (op->getOpName() != nullptr) {
sd::ops::OpExecutionLogger::setCurrentOpName(*op->getOpName());
// Also update the already-tracked context with the op name
sd::graph::OpContextLifecycleTracker::getInstance().updateContextOpName(opContext, *op->getOpName());
}
#endif
// Execute the custom operation with the provided context
auto result = op->execute(opContext);
// Synchronize the CUDA stream to ensure operation completion
auto res = cudaStreamSynchronize(*opContext->launchContext()->getCudaStream());
if (res != cudaSuccess) {
std::string errorMessage;
errorMessage += "CUDA stream synchronization failed with error code: ";
errorMessage += std::to_string(res);
THROW_EXCEPTION(errorMessage.c_str());
}
// Synchronize fastpath inputs
for (auto v : opContext->fastpath_in()) {
if (!v->isEmpty()) v->syncToDevice();
}
// Synchronize fastpath outputs
for (auto v : opContext->fastpath_out()) {
if (!v->isEmpty()) v->syncToDevice();
}
#if defined(SD_GCC_FUNCTRACE)
sd::ops::OpExecutionLogger::clearCurrentOpName();
#endif
return result;
}
catch (std::exception &e) {
#if defined(SD_GCC_FUNCTRACE)
sd::ops::OpExecutionLogger::clearCurrentOpName();
#endif
// Handle exceptions by setting error codes and messages
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
return sd::Status::KERNEL_FAILURE;
}
}
sd::Pointer lcScalarPointer(OpaqueLaunchContext lc) { return lc->getScalarPointer(); }
sd::Pointer lcReductionPointer(OpaqueLaunchContext lc) { return lc->getReductionPointer(); }
sd::Pointer lcAllocationPointer(OpaqueLaunchContext lc) { return lc->getAllocationPointer(); }
sd::Pointer lcExecutionStream(OpaqueLaunchContext lc) { return lc->getCudaStream(); }
sd::Pointer lcCopyStream(OpaqueLaunchContext lc) { return lc->getCudaSpecialStream(); }
sd::Pointer lcBlasHandle(OpaqueLaunchContext lc) { return lc->getCublasHandle(); }
sd::Pointer lcSolverHandle(OpaqueLaunchContext lc) { return lc->getCusolverHandle(); }
/*
* Basic CUDA constants here: number of blocks per MP
*/
int getDeviceBlockThreshold(int deviceId) {
int ccMinor = deviceProperties[deviceId].minor;
int ccMajor = deviceProperties[deviceId].major;
int blockThreshold = 8;
if (ccMajor >= 5)
blockThreshold = 32;
else if (ccMajor == 3)
blockThreshold = 16;
else if (ccMajor < 3)
blockThreshold = 8;
return blockThreshold;
}
/*
* This message returns shared memory threshold value. default overflow ratio is 0.3
*/
int getDeviceSharedThreshold(int deviceId) {
int ccMinor = deviceProperties[deviceId].minor;
int ccMajor = deviceProperties[deviceId].major;
// please note threshold isn't multiple of 32, and that's NOT a mistake
int shmemThreshold;
if (ccMajor == 6 && ccMinor == 0)
shmemThreshold = 65536;
else if (ccMajor == 6 && ccMinor == 1)
shmemThreshold = 49152;
else if (ccMajor == 5 && ccMinor == 2)
shmemThreshold = 98304;
else if (ccMajor == 5)
shmemThreshold = 65536;
else if (ccMajor == 3 && ccMinor == 7)
shmemThreshold = 114688;
else
shmemThreshold = 49152;
return shmemThreshold / 0.3;
}
sd::buffer::Buffer<sd::LongType> *createScalarBuffer(cudaStream_t stream) {
auto scalarShapeInfo = shape::createScalarShapeInfo();
auto buff = sd::buffer::createBuffer(scalarShapeInfo, shape::shapeInfoLength(2), stream);
copyDataToGpu(&buff, stream);
return buff;
}
template <typename T>
SD_KERNEL void _printBuffers(void* buffer, sd::LongType bufferLength) {
T * inputBuffer = reinterpret_cast<T *>(buffer);
const auto tid = blockIdx.x * blockDim.x + threadIdx.x;
if(tid == 0) {
printf("DEVICE buffer: ");
}
const auto step = gridDim.x * blockDim.x;
for (int t = tid; t < bufferLength; t += step) {
if(t == 0) {
printf("DEVICE buffer: ");
}
printf(" %f ",(double) inputBuffer[t]);
if(t == bufferLength - 1) {
printf("\n");
}
}
}
template <typename T>
void _printHostBuffer(OpaqueDataBuffer *buffer, sd::LongType offset) {
auto xType = buffer->dataBuffer()->getDataType();
sd::LongType len = buffer->dataBuffer()->getNumElements();
auto buff = buffer->dataBuffer()->template primaryAsT<T>();
sd_printf("Data type %s: ", sd::DataTypeUtils::asString(xType).c_str());
sd_printf("Host buffer: ",0);
for(int i = offset; i < len; i++) {
sd_printf("%f ",(double) buff[i]);
}
sd_printf("\n",0);
}
void printDeviceBuffer(OpaqueDataBuffer *buffer, sd::LongType offset) {
if(buffer->special() != nullptr) {
sd_printf("Device pointer address: %d\n", buffer->special());
} else {
sd_printf("Device pointer address: none\n",0);
}
if(buffer->primary() != nullptr) {
sd_printf("Host pointer address: %d\n", buffer->primary());
} else {
sd_printf("Host pointer address: none\n",0);
}
auto xType = buffer->dataBuffer()->getDataType();
BUILD_SINGLE_SELECTOR(xType, _printHostBuffer,(buffer,offset),SD_COMMON_TYPES);
}
template <typename T>
void _printDeviceBuffer(OpaqueDataBuffer *buffer) {
auto xType = buffer->dataBuffer()->getDataType();
sd::LongType len = buffer->dataBuffer()->getNumElements();
_printBuffers<T><<<256, 512, 1024>>>(buffer->special(),len);
cudaDeviceSynchronize();
sd::DebugHelper::checkGlobalErrorCode("print device buffer(...) failed");
}
void printDeviceBuffer(OpaqueDataBuffer *buffer) {
auto xType = buffer->dataBuffer()->getDataType();
sd_printf("Data type %s: ", sd::DataTypeUtils::asString(xType).c_str());
if(buffer->special() != nullptr) {
sd_printf("Device pointer address: %d\n", reinterpret_cast<sd::LongType>(buffer->special()));
} else {
sd_printf("Device pointer address: none\n",0);
}
BUILD_SINGLE_SELECTOR(xType, _printDeviceBuffer,(buffer),SD_COMMON_TYPES);
if(buffer->primary() != nullptr) {
sd_printf("Host pointer address: %d\n", reinterpret_cast<sd::LongType>(buffer->primary()));
} else {
sd_printf("Host pointer address: none\n",0);
}
}
void execPairwiseTransform(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray y, OpaqueNDArray z, void *extraParams) {
try {
x->prepareSpecialUse({z}, {x, y});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execPairwiseTransform(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
extraParams);
x->registerSpecialUse({z}, {x, y});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execPairwiseTransformBool(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray y, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x, y});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execPairwiseBoolTransform(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
extraParams);
x->registerSpecialUse({z}, {x, y});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execSummaryStatsScalar(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, bool biasCorrected) {
try {
x->prepareSpecialUse({z}, {x});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execSummaryStatsScalar(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
biasCorrected);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execBroadcastBool(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray y, OpaqueNDArray z, void *extraParams, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x, y, dimension});
auto dimensionBuffer = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
auto hTADShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers[9]);
auto tadOnlyShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers[10]);
auto tadOffsets = reinterpret_cast<sd::LongType *>(extraPointers[11]);
auto tadOnlyShapeInfoZ = reinterpret_cast<sd::LongType *>(extraPointers[12]);
auto tadOffsetsZ = reinterpret_cast<sd::LongType *>(extraPointers[13]);
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execBroadcastBool(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
extraParams,
dimensionBuffer,
dimensionLength,
tadOnlyShapeInfo,
tadOffsets,
tadOnlyShapeInfoZ,
tadOffsetsZ);
x->registerSpecialUse({z}, {x, y, dimension});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
/**
*
* @param opNum
* @param dX
* @param dXShapeInfo
* @param dY
* @param dYShapeInfo
* @param dZ
* @param dZShapeInfo
* @param dimension
* @param dimensionLength
*/
void execBroadcast(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray y, OpaqueNDArray z, void *extraParams, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x, y, dimension});
auto dimensionBuffer = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
auto hTADShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers[9]);
auto tadOnlyShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers[10]);
auto tadOffsets = reinterpret_cast<sd::LongType *>(extraPointers[11]);
auto tadOnlyShapeInfoZ = reinterpret_cast<sd::LongType *>(extraPointers[12]);
auto tadOffsetsZ = reinterpret_cast<sd::LongType *>(extraPointers[13]);
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execBroadcast(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dimensionBuffer,
dimensionLength,
tadOnlyShapeInfo,
tadOffsets,
tadOnlyShapeInfoZ,
tadOffsetsZ);
x->registerSpecialUse({z}, {x, y, dimension});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
/**
*
* @param opNum
* @param dX
* @param dXShapeInfo
* @param extraParams
* @param dZ
* @param dZShapeInfo
*/
////////////////////////////////////////////////////////////////////////
void execReduceFloat(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceFloatScalar(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo());
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduceSame(sd::Pointer *extraPointers,
int opNum,
OpaqueNDArray x,
void *extraParams,
OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceSameScalar(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr: x->specialBuffer(),
x->specialShapeInfo(), extraParams,
z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo());
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduceSame2(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x});
dimension->prepareSpecialUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
const auto zLen = shape::length(z->shapeInfo());
std::vector<sd::LongType> dimensions(dimensionData, dimensionData + dimensionLength);
const sd::LongType *zShapeInfoH = z->shapeInfo();
if (shape::rank(x->shapeInfo()) - dimensionLength != shape::rank(z->shapeInfo()) && zLen != 1) {
auto zPack = sd::ConstantShapeHelper::getInstance().createShapeInfoWithNoUnitiesForReduce(z->shapeInfo(), &dimensions);
zShapeInfoH = reinterpret_cast<sd::LongType const *>(zPack->primary());
}
std::vector<sd::LongType> *dims =
(zLen != 1) ? sd::ShapeUtils::evalDimsForReduceOp(shape::rank(x->shapeInfo()), &dimensions) : new std::vector<sd::LongType>();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceSame(&lc,
opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
zShapeInfoH,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dims->data(), dims->size());
x->registerSpecialUse({z}, {x});
delete dims;
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduceLong2(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
const auto zLen = shape::length(z->shapeInfo());
std::vector<sd::LongType> dimensions(dimensionData, dimensionData + dimensionLength);
const sd::LongType *zShapeInfoH = z->shapeInfo();
if (shape::rank(x->shapeInfo()) - dimensionLength != shape::rank(z->shapeInfo()) && zLen != 1) {
auto zPack = sd::ConstantShapeHelper::getInstance().createShapeInfoWithNoUnitiesForReduce(z->shapeInfo(), &dimensions);
zShapeInfoH = reinterpret_cast<sd::LongType const *>(zPack->primary());
}
std::vector<sd::LongType> *dims =
(zLen != 1) ? sd::ShapeUtils::evalDimsForReduceOp(shape::rank(x->shapeInfo()), &dimensions) : new std::vector<sd::LongType>();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceLong(&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
zShapeInfoH,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dims->data(), dims->size());
x->registerSpecialUse({z}, {x});
delete dims;
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduceLong(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
const auto zLen = shape::length(z->shapeInfo());
std::vector<sd::LongType> dimensions(dimensionData, dimensionData + dimensionLength);
const sd::LongType *zShapeInfoH = z->shapeInfo();
if (shape::rank(x->shapeInfo()) - dimensionLength != shape::rank(z->shapeInfo()) && zLen != 1) {
auto zPack = sd::ConstantShapeHelper::getInstance().createShapeInfoWithNoUnitiesForReduce(z->shapeInfo(), &dimensions);
zShapeInfoH = reinterpret_cast<sd::LongType const *>(zPack->primary());
}
std::vector<sd::LongType> *dims =
(zLen != 1) ? sd::ShapeUtils::evalDimsForReduceOp(shape::rank(x->shapeInfo()), &dimensions) : new std::vector<sd::LongType>();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceLong(&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
zShapeInfoH,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dims->data(), dims->size());
x->registerSpecialUse({z}, {x});
delete dims;
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduceBool2(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
const auto zLen = shape::length(z->shapeInfo());
std::vector<sd::LongType> dimensions(dimensionData, dimensionData + dimensionLength);
const sd::LongType *zShapeInfoH = z->shapeInfo();
if (shape::rank(x->shapeInfo()) - dimensionLength != shape::rank(z->shapeInfo()) && zLen != 1) {
auto zPack = sd::ConstantShapeHelper::getInstance().createShapeInfoWithNoUnitiesForReduce(z->shapeInfo(), &dimensions);
zShapeInfoH = reinterpret_cast<sd::LongType const *>(zPack->primary());
}
std::vector<sd::LongType> *dims =
(zLen != 1) ? sd::ShapeUtils::evalDimsForReduceOp(shape::rank(x->shapeInfo()), &dimensions) : new std::vector<sd::LongType>();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceBool(&lc,
opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
zShapeInfoH,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dims->data(), dims->size());
x->registerSpecialUse({z}, {x});
delete dims;
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduceBool(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
const auto zLen = shape::length(z->shapeInfo());
std::vector<sd::LongType> dimensions(dimensionData, dimensionData + dimensionLength);
const sd::LongType *zShapeInfoH = z->shapeInfo();
if (shape::rank(x->shapeInfo()) - dimensionLength != shape::rank(z->shapeInfo()) && zLen != 1) {
auto zPack = sd::ConstantShapeHelper::getInstance().createShapeInfoWithNoUnitiesForReduce(z->shapeInfo(), &dimensions);
zShapeInfoH = reinterpret_cast<sd::LongType const *>(zPack->primary());
}
std::vector<sd::LongType> *dims =
(zLen != 1) ? sd::ShapeUtils::evalDimsForReduceOp(shape::rank(x->shapeInfo()), &dimensions) : new std::vector<sd::LongType>();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceBool(&lc,
opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
zShapeInfoH,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dims->data(), dims->size());
x->registerSpecialUse({z}, {x});
delete dims;
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
/**
*
* @param opNum
* @param dX
* @param dXShapeInfo
* @param extraParams
* @param dZ
* @param dZShapeInfo
* @param dimension
* @param dimensionLength
*/
////////////////////////////////////////////////////////////////////////
void execIndexReduce(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
auto tadPack =
sd::ConstantTadHelper::getInstance().tadForDimensions(x->shapeInfo(), dimensionData, dimensionLength);
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execIndexReduce(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dimensionData, dimensionLength, tadPack->specialShapeInfo(), tadPack->specialOffsets());
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
/**
*
* @param opNum
* @param dX
* @param dXShapeInfo
* @param extraParams
* @param dZ
* @param dZShapeInfo
*/
////////////////////////////////////////////////////////////////////////
void execReduceFloat2(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
const auto zLen = shape::length(z->shapeInfo());
std::vector<sd::LongType> dimensions(dimensionData, dimensionData + dimensionLength);
const sd::LongType *zShapeInfoH = z->shapeInfo();
if (shape::rank(x->shapeInfo()) - dimensionLength != shape::rank(z->shapeInfo()) && zLen != 1) {
auto zPack = sd::ConstantShapeHelper::getInstance().createShapeInfoWithNoUnitiesForReduce(z->shapeInfo(), &dimensions);
zShapeInfoH = reinterpret_cast<sd::LongType const *>(zPack->primary());
}
std::vector<sd::LongType> *dims =
(zLen != 1) ? sd::ShapeUtils::evalDimsForReduceOp(shape::rank(x->shapeInfo()), &dimensions) : new std::vector<sd::LongType>();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduceFloat(&lc,
opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
zShapeInfoH,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dims->data(), dims->size());
x->registerSpecialUse({z}, {x});
delete dims;
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
/**
*
* @param opNum
* @param dX
* @param dXShapeInfo
* @param extraParams
*/
////////////////////////////////////////////////////////////////////////
void execIndexReduceScalar(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execIndexReduceScalar(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo());
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execTransformSame(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
auto tadShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[0] : nullptr);
auto tadOffsets = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[1] : nullptr);
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execTransformSame(&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
extraParams, tadShapeInfo, tadOffsets);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execTransformBool(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
auto tadShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[0] : nullptr);
auto tadOffsets = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[1] : nullptr);
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execTransformBool(
&lc, opNum, shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(), x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(), x->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(), z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(), z->specialShapeInfo(), extraParams);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execTransformAny(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
auto stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
auto streamSpecial = reinterpret_cast<cudaStream_t &>(extraPointers[4]);
sd::LaunchContext lc(stream, streamSpecial, extraPointers[5], extraPointers[3], reinterpret_cast<int *>(extraPointers[6]));
NativeOpExecutioner::execTransformAny(
&lc, opNum, shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(), x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(), x->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(), z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(), z->specialShapeInfo(), extraParams, false);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execTransformStrict(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
auto tadShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[10] : nullptr);
auto tadOffsets = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[11] : nullptr);
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execTransformStrict(
&lc, opNum, shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(), x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(), x->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(), z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(), z->specialShapeInfo(), extraParams);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execTransformFloat(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x});
auto tadShapeInfo = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[10] : nullptr);
auto tadOffsets = reinterpret_cast<sd::LongType *>(extraPointers != nullptr ? extraPointers[11] : nullptr);
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execTransformFloat(
&lc, opNum, shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(), x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(), x->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(), z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(), z->specialShapeInfo(), extraParams);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void checkP2P() {
int curDevice = 0;
cudaGetDevice(&curDevice);
int devCnt = 0;
cudaGetDeviceCount(&devCnt);
if (curDevice < 0 && curDevice > devCnt) curDevice = 0;
bool tempSupport = true;
if (devCnt > 1) {
for (int dX = 0; dX < devCnt; dX++) {
for (int dY = 0; dY < devCnt; dY++) {
if (dX == dY) continue;
int canAccess = 0;
cudaSetDevice(dX);
cudaDeviceCanAccessPeer(&canAccess, dX, dY);
if (!canAccess) {
tempSupport = false;
break;
}
}
}
supportedP2P = tempSupport;
cudaSetDevice(curDevice);
} else {
// if we have only 1 device - we say that we support P2P, since all data will be on 1 device
supportedP2P = true;
}
}
void enableP2P(bool enable) {
if (enable == allowedP2P) return;
int curDevice = 0;
cudaGetDevice(&curDevice);
int devCnt = 0;
cudaGetDeviceCount(&devCnt);
if (curDevice < 0 && curDevice > devCnt) curDevice = 0;
if (devCnt > 1) {
for (int dX = 0; dX < devCnt; dX++) {
for (int dY = 0; dY < devCnt; dY++) {
if (dX == dY) continue;
int canAccess = 0;
cudaSetDevice(dX);
cudaDeviceCanAccessPeer(&canAccess, dX, dY);
if (canAccess) {
if (enable) {
cudaDeviceEnablePeerAccess(dY, 0);
} else {
cudaDeviceDisablePeerAccess(dY);
}
} else {
if (sd::Environment::getInstance().isVerbose()) printf("Peer access [%i] -> [%i] isn't possible\n", dX, dY);
}
}
}
cudaSetDevice(curDevice);
}
allowedP2P = enable;
cudaSetDevice(curDevice);
}
bool isP2PAvailable() { return supportedP2P; }
void initializeDevicesAndFunctions() {
try {
int devCnt = 0;
cudaGetDeviceCount(&devCnt);
deviceProperties = new cudaDeviceProp[devCnt];
for (int i = 0; i < devCnt; i++) {
cudaSetDevice(i);
cudaGetDeviceProperties(&deviceProperties[i], i);
cudaDeviceSetLimit(cudaLimitStackSize, 8192);
cudaDeviceSetLimit(cudaLimitMallocHeapSize, 1048576 * 128);
}
cudaSetDevice(0);
checkP2P();
// enabling p2p gpu access if it's supported
if (supportedP2P && devCnt > 1) enableP2P(allowedP2P);
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
/**
* Initialize the shape cache early to prevent race conditions during static initialization.
* This ensures ConstantShapeHelper and its internal DirectShapeTrie are fully initialized
* before any multi-threaded access occurs.
*
* Safe to call multiple times - subsequent calls are no-ops.
*/
void initializeShapeCache() {
sd::ConstantShapeHelper::getInstance();
}
/**
* Initialize the TAD (Tensor-Along-Dimension) cache early to prevent race conditions.
* This ensures ConstantTadHelper and its internal DirectTadTrie are fully initialized
* before any multi-threaded access occurs.
*
* Safe to call multiple times - subsequent calls are no-ops.
*/
void initializeTadCache() {
sd::ConstantTadHelper::getInstance();
}
void initializeFunctions(sd::Pointer *functions) { sd::BlasHelper::getInstance().initializeDeviceFunctions(functions);
}
/**
* This method acquires memory chunk of requested size on host side
*
* @param pointer pointer that'll be used for allocation
* @param memorySize memory size, in bytes
* @param flags optional parameter
*/
sd::Pointer mallocHost(sd::LongType memorySize, int flags) {
sd::Pointer pointer;
// cudaHostAllocMapped |cudaHostAllocPortable
auto res = cudaHostAlloc(reinterpret_cast<void **>(&pointer), memorySize + 8, cudaHostAllocDefault);
if (res != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(res);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaHostAlloc failed");
}
return reinterpret_cast<int8_t *>(pointer);
}
/**
* This method acquires memory chunk of requested size on specified device
*
* @param pointer pointer that'll be used for allocation
* @param memorySize memory size, in bytes
* @param ptrToDeviceId pointer to deviceId. For cuda that's just and int, for OpenCL that's pointer to device_id, etc
* @param flags optional parameter
*/
sd::Pointer mallocDevice(sd::LongType memorySize, int deviceId, int flags) {
sd::Pointer pointer;
auto res = cudaMalloc(reinterpret_cast<void **>(&pointer), memorySize + 8);
if (res != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(res);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaMalloc failed");
}
return reinterpret_cast<int8_t *>(pointer);
}
/**
* This method releases previously allocated host memory space
*
* @param pointer pointer that'll be freed
*/
int freeHost(sd::Pointer pointer) {
/* auto res = cudaFreeHost(reinterpret_cast<void *>(pointer));
if (res != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(res);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaFreeHost failed");
}*/
return 1L;
}
/**
* This method releases previously allocated memory space on device
*
* @param pointer pointer that'll be freed
* @param ptrToDeviceId pointer to deviceId.
*/
int freeDevice(sd::Pointer pointer, int deviceId) {
auto res = cudaFree(reinterpret_cast<void *>(pointer));
// we're intentionally skipping
if (res != 0 && res != 1) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(res);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaFree failed");
}
return res == 0 ? 1L : 0L;
}
sd::Pointer createContext() { return 0L; }
sd::Pointer createStream() {
auto stream = new cudaStream_t();
auto dZ = cudaStreamCreate(stream);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaStreamCreate failed");
}
return stream;
}
sd::Pointer createEvent() {
sd::Pointer nativeEvent = (sd::Pointer)malloc(sizeof(cudaEvent_t));
CHECK_ALLOC(nativeEvent, "Failed to allocate new CUDA event buffer", sizeof(cudaEvent_t));
auto dZ = cudaEventCreateWithFlags(reinterpret_cast<cudaEvent_t *>(&nativeEvent), cudaEventDisableTiming);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaEventCreateWithFlags failed");
}
return nativeEvent;
}
int registerEvent(sd::Pointer event, sd::Pointer stream) {
auto pEvent = reinterpret_cast<cudaEvent_t *>(&event);
auto pStream = reinterpret_cast<cudaStream_t *>(stream);
auto dZ = cudaEventRecord(*pEvent, *pStream);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaEventRecord failed");
}
return 1;
}
int setDevice(int deviceId) {
sd::AffinityManager::setCurrentDevice(deviceId);
return 1;
}
sd::LongType getDeviceFreeMemoryDefault() {
size_t memFree = 0;
size_t memTotal = 0;
cudaMemGetInfo(&memFree, &memTotal);
return (sd::LongType)memFree;
}
sd::LongType getDeviceFreeMemory(int device) {
int orig = -1;
cudaGetDevice(&orig);
if (device >= 0 && device != orig) {
cudaSetDevice(device);
}
size_t memFree = 0;
size_t memTotal = 0;
cudaMemGetInfo(&memFree, &memTotal);
if (device >= 0 && device != orig) {
cudaSetDevice(orig);
}
return (sd::LongType)memFree;
}
sd::LongType getDeviceTotalMemory(int device) {
int orig = -1;
cudaGetDevice(&orig);
if (device >= 0 && device != orig) {
cudaSetDevice(device);
}
size_t memFree = 0;
size_t memTotal = 0;
cudaMemGetInfo(&memFree, &memTotal);
if (device >= 0 && device != orig) {
cudaSetDevice(orig);
}
return (sd::LongType)memTotal;
}
int memcpySync(sd::Pointer dst, sd::Pointer src, sd::LongType size, int flags, sd::Pointer reserved) {
cudaMemcpyKind kind;
switch (flags) {
case 0: {
kind = cudaMemcpyHostToHost;
} break;
case 1: {
kind = cudaMemcpyHostToDevice;
} break;
case 2: {
kind = cudaMemcpyDeviceToHost;
} break;
case 3: {
kind = cudaMemcpyDeviceToDevice;
} break;
default: {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("UNDEFNED MEMCPY");
return 0;
}
}
auto dZ = cudaMemcpy(reinterpret_cast<void *>(dst), const_cast<const void *>(reinterpret_cast<void *>(src)),
static_cast<size_t>(size), kind);
if (dZ != 0) {
printf("Failed on [%p] -> [%p], size: [%i], direction: [%i], dZ: [%i]\n", src, dst, size, flags,
static_cast<int>(dZ));
fflush(stdout);
fflush(stderr);
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaMemcpy failed");
return 0;
}
return 1;
}
int memcpyAsync(sd::Pointer dst, sd::Pointer src, sd::LongType size, int flags, sd::Pointer reserved) {
auto pStream = reinterpret_cast<cudaStream_t *>(reserved);
cudaMemcpyKind kind;
switch (flags) {
case 0: {
kind = cudaMemcpyHostToHost;
} break;
case 1: {
kind = cudaMemcpyHostToDevice;
} break;
case 2: {
kind = cudaMemcpyDeviceToHost;
} break;
case 3: {
kind = cudaMemcpyDeviceToDevice;
} break;
default: {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("UNDEFINED MEMCPY");
return 0;
}
}
auto dZ = cudaMemcpyAsync(reinterpret_cast<void *>(dst), const_cast<const void *>(reinterpret_cast<void *>(src)),
static_cast<size_t>(size), kind, *pStream);
if (dZ != 0) {
printf("Failed on [%p] -> [%p], size: [%i], direction: [%i], dZ: [%i]\n", src, dst, size, flags,
static_cast<int>(dZ));
fflush(stdout);
fflush(stderr);
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaMemcpyAsync failed");
return 0;
}
return 1;
}
int memsetSync(sd::Pointer dst, int value, sd::LongType size, int flags, sd::Pointer reserved) {
auto dZ = cudaMemset(reinterpret_cast<void *>(dst), value, static_cast<size_t>(size));
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaMemset failed");
}
return 1;
}
int memsetAsync(sd::Pointer dst, int value, sd::LongType size, int flags, sd::Pointer reserved) {
auto pStream = reinterpret_cast<cudaStream_t *>(reserved);
auto dZ = cudaMemsetAsync(reinterpret_cast<void *>(dst), value, static_cast<size_t>(size), *pStream);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaMemsetAsync failed");
}
return 1;
}
int destroyEvent(sd::Pointer event) {
auto pEvent = reinterpret_cast<cudaEvent_t *>(&event);
auto dZ = cudaEventDestroy(*pEvent);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaEventDestroy failed");
}
return 1;
}
int streamSynchronize(sd::Pointer stream) {
auto pStream = reinterpret_cast<cudaStream_t *>(stream);
auto dZ = cudaStreamSynchronize(*pStream);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaStreamSynchronize failed");
}
return 1L;
}
int eventSynchronize(sd::Pointer event) {
auto pEvent = reinterpret_cast<cudaEvent_t *>(&event);
auto dZ = cudaEventSynchronize(*pEvent);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaEventSynchronize failed");
}
return 1L;
}
int getAvailableDevices() {
int devCnt = 0;
cudaGetDeviceCount(&devCnt);
return devCnt;
}
void enableDebugMode(bool reallyEnable) { sd::Environment::getInstance().setDebug(reallyEnable); }
void setGridLimit(int gridSize) {
if (gridSize > 8192) gridSize = 8192;
if (gridSize < 1) gridSize = 1;
blockLimit = gridSize;
}
int ompGetMaxThreads() { return maxThreads; }
int ompGetNumThreads() { return maxThreads; }
void setOmpNumThreads(int threads) {
if (threads > 1024) threads = 1024;
if (threads < 32) threads = 32;
maxThreads = threads;
}
/**
* Sets the number of threads used by OpenBLAS for BLAS operations.
* On CUDA backend, this is a no-op since we use cuBLAS, not OpenBLAS.
*/
void setOpenBlasThreads(int threads) {
// No-op on CUDA - we use cuBLAS, not OpenBLAS
// But still track the setting in Environment for consistency
sd::Environment::getInstance().setOpenBlasThreads(threads);
}
/**
* Gets the number of threads OpenBLAS is configured to use.
* On CUDA backend, returns 0 since we use cuBLAS.
*/
int getOpenBlasThreads() {
return sd::Environment::getInstance().getOpenBlasThreads();
}
/**
* Check if BLAS call serialization is enabled.
* On CUDA backend, this is typically not needed since cuBLAS handles threading internally.
*/
bool isSerializeBlasCalls() {
return sd::Environment::getInstance().isSerializeBlasCalls();
}
/**
* Enable or disable BLAS call serialization.
* On CUDA backend, this is typically not needed since cuBLAS handles threading internally.
*/
void setSerializeBlasCalls(bool serialize) {
sd::Environment::getInstance().setSerializeBlasCalls(serialize);
}
void enableVerboseMode(bool reallyEnable) { sd::Environment::getInstance().setVerbose(reallyEnable); }
int getDeviceMajor(int device) { return deviceProperties[device].major; }
int getDeviceMinor(int device) { return deviceProperties[device].minor; }
const char *getDeviceName(int device) { return deviceProperties[device].name; }
void saveNpy(std::string fname, const OpaqueDataBuffer *data, const unsigned int *shape, const unsigned int ndims,
std::string mode) {
auto dtype = data->getDataBuffer()->getDataType();
BUILD_SINGLE_SELECTOR(dtype,cnpy::npy_save,(fname,data->getDataBuffer()->primary(),shape,ndims,mode),SD_COMMON_TYPES);
}
/**
* This method saves
*/
OpaqueTadPack *tadOnlyShapeInfo(sd::LongType *hXShapeInfo, sd::LongType *dimension, sd::LongType dimensionLength) {
try {
auto pack = sd::ConstantTadHelper::getInstance().tadForDimensions(hXShapeInfo, dimension,dimensionLength);
return pack;
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
return nullptr;
}
}
int memcpyConstantAsync(sd::LongType dst, sd::Pointer src, sd::LongType size, int flags, sd::Pointer reserved) {
cudaStream_t *pStream = reinterpret_cast<cudaStream_t *>(reserved);
cudaMemcpyKind kind;
DEBUG_KERNEL(pStream, -1);
switch (flags) {
case 0: {
kind = cudaMemcpyHostToHost;
} break;
case 1: {
kind = cudaMemcpyHostToDevice;
} break;
case 2: {
kind = cudaMemcpyDeviceToHost;
}
case 3: {
kind = cudaMemcpyDeviceToDevice;
} break;
}
auto dZ = cudaMemcpyToSymbolAsync(getConstantSpace(), const_cast<const void *>(src), size, dst, kind, *pStream);
if (dZ != 0) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(dZ);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("cudaMemcpyToSymbolAsync failed");
}
return 1;
}
sd::Pointer getConstantSpace() {
return sd::ConstantHelper::getInstance().getConstantSpace();
}
void pullRows(sd::Pointer *extraPointers, OpaqueNDArray x, OpaqueNDArray z, sd::LongType n, OpaqueNDArray indexes, sd::LongType dimension) {
try {
x->prepareSpecialUse({z}, {x});
cudaStream_t *stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
dim3 launchDims = getLaunchDims("pullRows");
auto xType = x->dataType();
std::vector<void*> xBuffers(n);
std::vector<const sd::LongType*> tadShapeInfoBuffers(n);
std::vector<const sd::LongType*> tadOffsetsBuffers(n);
// Calculate TADs for each x
auto tadPackX = sd::ConstantTadHelper::getInstance().tadForDimensions(x->shapeInfo(), &dimension, 1);
void* zBuffer = z->specialBuffer();
sd::LongType* zShapeInfo = const_cast<sd::LongType*>(z->specialShapeInfo());
// Calculate TADs for z
auto tadPackZ = sd::ConstantTadHelper::getInstance().tadForDimensions(z->shapeInfo(), &dimension, 1);
sd::LongType* zTadShapeInfoBuffer = const_cast<sd::LongType*>(tadPackZ->specialShapeInfo());
sd::LongType* zTadOffsetsBuffer = const_cast<sd::LongType*>(tadPackZ->specialOffsets());
// Use the special buffer for indexes
sd::LongType* indexesBuffer = reinterpret_cast<sd::LongType*>(indexes->specialBuffer());
/*
*
* template<T> void pullRowsKernelGeneric(dim3 &launchDims, cudaStream_t *stream, void *vx, void *vz, LongType n, LongType *indexes, const LongType *tadShapeInfo, const LongType *tadOffsets, const LongType *zTadShapeInfo, const LongType *zTadOffsets)
* */
BUILD_SINGLE_SELECTOR(xType, sd::pullRowsKernelGeneric,
(launchDims, stream, x->specialBuffer(), zBuffer, n,
indexes->specialBufferasT<sd::LongType>(),
const_cast<sd::LongType *>(tadPackX->specialShapeInfo()),
const_cast<sd::LongType *>(tadPackX->specialOffsets()), zTadShapeInfoBuffer, zTadOffsetsBuffer),
SD_COMMON_TYPES);
DEBUG_KERNEL(stream, -1);
for (int i = 0; i < n; ++i) {
x->registerSpecialUse({z}, {x});
}
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
bool isExperimentalEnabled() { return sd::Environment::getInstance().isExperimentalBuild(); }
void shuffle(sd::Pointer *extras,
OpaqueNDArrayArr x,
OpaqueNDArrayArr z,
int N,
OpaqueNDArray dimension,
OpaqueNDArray shuffleMap) {
try {
cudaStream_t *stream = reinterpret_cast<cudaStream_t *>(extras[1]);
auto xType = x[0]->dataType();
dim3 launchDims = getLaunchDims("shuffle");
// Extract buffers from each NDArray in the array
std::vector<void*> xBuffers(N);
std::vector<sd::LongType*> xShapeInfos(N);
std::vector<sd::LongType*> tadShapeInfoBuffers(N);
std::vector<sd::LongType*> tadOffsetsBuffers(N);
std::vector<void*> zBuffers(N);
std::vector<sd::LongType*> zShapeInfos(N);
std::vector<sd::LongType*> zTadShapeInfoBuffers(N);
std::vector<sd::LongType*> zTadOffsetsBuffers(N);
for (int i = 0; i < N; ++i) {
xBuffers[i] = x[i]->specialBuffer();
xShapeInfos[i] = const_cast<sd::LongType*>(x[i]->specialShapeInfo());
zBuffers[i] = z[i]->specialBuffer();
zShapeInfos[i] = const_cast<sd::LongType*>(z[i]->specialShapeInfo());
// Extract dimensions for each x[i] and z[i] from the array of arrays
sd::LongType* dimensions = reinterpret_cast<sd::LongType*>(dimension->buffer());
sd::LongType dimLength = shape::length(dimension->shapeInfo());
// Calculate TADs for each x
auto tadPackX = sd::ConstantTadHelper::getInstance().tadForDimensions(x[i]->shapeInfo(), dimensions, dimLength);
tadShapeInfoBuffers[i] = const_cast<sd::LongType*>(tadPackX->specialShapeInfo());
tadOffsetsBuffers[i] = const_cast<sd::LongType*>(tadPackX->specialOffsets());
// Calculate TADs for each z
auto tadPackZ = sd::ConstantTadHelper::getInstance().tadForDimensions(z[i]->shapeInfo(), dimensions, dimLength);
zTadShapeInfoBuffers[i] = const_cast<sd::LongType*>(tadPackZ->specialShapeInfo());
zTadOffsetsBuffers[i] = const_cast<sd::LongType*>(tadPackZ->specialOffsets());
}
BUILD_SINGLE_SELECTOR(xType, sd::shuffleKernelGeneric,
(launchDims, stream, xBuffers.data(), xShapeInfos.data(), zBuffers.data(), N,
reinterpret_cast<int*>(shuffleMap->buffer()), tadShapeInfoBuffers.data(),
tadOffsetsBuffers.data()),
SD_COMMON_TYPES);
sd::DebugHelper::checkErrorCode(stream, "shuffle(...) failed");
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void setOmpMinThreads(int threads) {
minThreads = sd::math::sd_max<int>(32, threads);
minThreads = sd::math::sd_min<int>(maxThreads, minThreads);
}
int getDevice() { return sd::AffinityManager::currentDeviceId(); }
////////////////////////////////////////////////////////////////////////
void execSummaryStats(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z, bool biasCorrected) {
try {
x->prepareSpecialUse({z}, {x});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execSummaryStats(&lc,
opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(x->shapeInfo())->special(),
extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special(),
biasCorrected);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execSummaryStatsTad(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray z,
OpaqueNDArray dimension, bool biasCorrected) {
try {
x->prepareSpecialUse({z}, {x});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionData = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
int dimensionLength = static_cast<int>(shape::length(dimension->shapeInfo()));
auto tadPack =
sd::ConstantTadHelper::getInstance().tadForDimensions(x->shapeInfo(), dimensionData, dimensionLength);
auto tadShapeInfo = tadPack->specialShapeInfo();
auto tadOffsets = tadPack->specialOffsets();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execSummaryStats(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(x->shapeInfo())->special(),
extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special(),
dimensionData, dimensionLength, tadShapeInfo, tadOffsets, biasCorrected);
x->registerSpecialUse({z}, {x});
dimension->registerSpecialUse({}, {dimension});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduce3(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray y, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x, y});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduce3(
&lc,
opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
extraParams,
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo());
x->registerSpecialUse({z}, {x, y});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduce3Tad(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray y, OpaqueNDArray z, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x, y});
dimension->preparePrimaryUse({}, {dimension});
auto dim = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd:: LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
auto xTadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(x->shapeInfo(), dim, dimensionLength);
auto xTadShapeInfo = xTadPack->specialShapeInfo();
auto xOffsets = xTadPack->specialOffsets();
auto yTadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(y->shapeInfo(), dim, dimensionLength);
auto yTadShapeInfo = yTadPack->specialShapeInfo();
auto yOffsets = yTadPack->specialOffsets();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduce3TAD(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(), extraParams,
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dim, dimensionLength,
xTadShapeInfo, xOffsets, yTadShapeInfo, yOffsets);
x->registerSpecialUse({z}, {x, y});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execReduce3Scalar(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, void *extraParams, OpaqueNDArray y, OpaqueNDArray z) {
try {
x->prepareSpecialUse({z}, {x, y});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduce3Scalar(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(x->shapeInfo())->special(), extraParams,
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(y->shapeInfo())->special(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special());
x->registerSpecialUse({z}, {x, y});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execScalarBool(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray z, OpaqueNDArray scalar, void *extraParams) {
try {
x->prepareSpecialUse({z}, {x, scalar});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execScalarBool(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(x->shapeInfo())->special(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special(),
shape::isEmptyConst(scalar->shapeInfo()) ? nullptr : scalar->buffer(),
scalar->shapeInfo(),
shape::isEmptyConst(scalar->shapeInfo()) ? nullptr : scalar->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(scalar->shapeInfo())->special(), extraParams);
x->registerSpecialUse({z}, {x, scalar});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execScalarBoolTad(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray z, OpaqueNDArray scalar, void *extraParams, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x, scalar});
dimension->preparePrimaryUse({}, {dimension});
auto dim = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
auto xTadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(x->shapeInfo(), dim, dimensionLength);
auto xTadShapeInfo = xTadPack->specialShapeInfo();
auto xOffsets = xTadPack->specialOffsets();
auto zTadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(z->shapeInfo(), dim, dimensionLength);
auto zTadShapeInfo = zTadPack->specialShapeInfo();
auto zOffsets = zTadPack->specialOffsets();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execScalarBool(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(x->shapeInfo())->special(),
extraParams,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special(),
shape::isEmptyConst(scalar->shapeInfo()) ? nullptr : scalar->buffer(),
scalar->shapeInfo(),
shape::isEmptyConst(scalar->shapeInfo()) ? nullptr : scalar->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(scalar->shapeInfo())->special(),
dim, dimensionLength,
xTadShapeInfo, xOffsets, zTadShapeInfo, zOffsets);
x->registerSpecialUse({z}, {x, scalar});
dimension->registerSpecialUse({}, {dimension});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
}
}
////////////////////////////////////////////////////////////////////////
void execScalar(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray z, OpaqueNDArray scalar, void *extraParams) {
try {
x->prepareSpecialUse({z}, {x, scalar});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execScalar(
&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(x->shapeInfo())->special(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special(),
shape::isEmptyConst(scalar->shapeInfo()) ? nullptr : scalar->buffer(),
scalar->shapeInfo(),
shape::isEmptyConst(scalar->shapeInfo()) ? nullptr : scalar->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(scalar->shapeInfo())->special(), extraParams);
x->registerSpecialUse({z}, {x, scalar});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execScalarTad(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray z, OpaqueNDArray scalar, void *extraParams, OpaqueNDArray dimension) {
try {
x->prepareSpecialUse({z}, {x, scalar});
dimension->preparePrimaryUse({}, {dimension});
auto dimensionPtr = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
auto xTadPack =
sd::ConstantTadHelper::getInstance().tadForDimensions(x->shapeInfo(), dimensionPtr, dimensionLength);
auto xTadShapeInfo = xTadPack->specialShapeInfo();
auto xOffsets = xTadPack->specialOffsets();
auto zTadPack =
sd::ConstantTadHelper::getInstance().tadForDimensions(z->shapeInfo(), dimensionPtr, dimensionLength);
auto zTadShapeInfo = zTadPack->specialShapeInfo();
auto zOffsets = zTadPack->specialOffsets();
cudaStream_t *stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
auto xType = sd::ArrayOptions::dataType(x->shapeInfo());
auto yType = sd::ArrayOptions::dataType(scalar->shapeInfo());
auto zType = sd::ArrayOptions::dataType(z->shapeInfo());
dim3 launchDims = getLaunchDims("scalarTad");
BUILD_SINGLE_SELECTOR_THRICE(
xType, functions::scalar::ScalarTransform,
::executeCudaAlongDimension(
launchDims, stream, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
xTadShapeInfo,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
zTadShapeInfo,
shape::isEmptyConst(scalar->shapeInfo()) ? nullptr : scalar->specialBuffer(),
extraParams, dimensionPtr, dimensionLength, xTadShapeInfo, xOffsets, zTadShapeInfo, zOffsets),
SD_COMMON_TYPES);
DEBUG_KERNEL(stream, opNum);
x->registerSpecialUse({z}, {x, scalar});
dimension->registerSpecialUse({}, {dimension});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execRandom(sd::Pointer *extraPointers, int opNum, sd::Pointer stateHost, OpaqueNDArray z, void *extraArguments) {
try {
z->prepareSpecialUse({}, {z});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execRandom(
&lc, opNum, stateHost,
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special(),
extraArguments);
z->registerSpecialUse({}, {z});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execRandom2(sd::Pointer *extraPointers, int opNum, sd::Pointer stateHost, OpaqueNDArray x, OpaqueNDArray z, void *extraArguments) {
try {
x->prepareSpecialUse({z}, {x});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execRandom(
&lc, opNum, stateHost,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(x->shapeInfo())->special(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
sd::ConstantShapeHelper::getInstance().bufferForShapeInfo(z->shapeInfo())->special(), extraArguments);
x->registerSpecialUse({z}, {x});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////////////////////////////////////////////////////////
void execRandom3(sd::Pointer *extraPointers, int opNum, sd::Pointer stateHost, OpaqueNDArray x,
OpaqueNDArray y, OpaqueNDArray z, void *extraArguments) {
try {
x->prepareSpecialUse({z}, {x, y});
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execRandom(&lc, opNum, stateHost,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
extraArguments);
x->registerSpecialUse({z}, {x, y});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
sd::Pointer initRandom(sd::Pointer *extraPointers, long seed, long bufferSize, sd::Pointer ptrToBuffer) {
unsigned long long *ptrHost = reinterpret_cast<unsigned long long *>(extraPointers[0]);
cudaStream_t *stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
// we don't synchronize at random initialization, it's safe to go async here
auto ptrDev = reinterpret_cast<unsigned long long *>(ptrToBuffer);
auto buffer = new sd::random::RandomBuffer(seed, bufferSize, reinterpret_cast<uint64_t *>(ptrHost),
reinterpret_cast<uint64_t *>(ptrDev));
buffer->propagateToDevice(buffer, *stream);
sd::DebugHelper::checkErrorCode(stream, "initRandom(...) failed A");
// we generate sequence in the host memory
sd::random::Xoroshiro128 generator(buffer);
generator.refreshBuffer();
// and copy it to gpu
cudaMemcpyAsync(ptrDev, ptrHost, bufferSize * 8, cudaMemcpyHostToDevice, *stream);
sd::DebugHelper::checkErrorCode(stream, "initRandom(...) failed B");
return buffer;
}
void destroyRandom(sd::Pointer ptrBuffer) {
sd::random::RandomBuffer *buffer = reinterpret_cast<sd::random::RandomBuffer *>(ptrBuffer);
// FIXME: it's bad thing, but we can't know in advance, which stream(s) where using this generator in practice
cudaDeviceSynchronize();
delete buffer;
}
void refreshBuffer(sd::Pointer *extraPointers, long seed, sd::Pointer ptrRandom) {
sd::random::RandomBuffer *buffer = reinterpret_cast<sd::random::RandomBuffer *>(ptrRandom);
unsigned long long *ptrHost = reinterpret_cast<unsigned long long *>(extraPointers[0]);
cudaStream_t *stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
cudaStreamSynchronize(*stream);
uint64_t *ptrDev = buffer->getDeviceBuffer();
// update rng state
buffer->setSeed(seed);
buffer->setOffset(0);
buffer->propagateToDevice(buffer, *stream);
// refresh buffer on host size
sd::random::Xoroshiro128 generator(buffer);
generator.refreshBuffer();
// copy back to gpu
cudaMemcpyAsync(ptrDev, ptrHost, buffer->getSize() * 8, cudaMemcpyHostToDevice, *stream);
}
void reSeedBuffer(sd::Pointer *extraPointers, long seed, sd::Pointer ptrRandom) {
sd::random::RandomBuffer *buffer = reinterpret_cast<sd::random::RandomBuffer *>(ptrRandom);
cudaStream_t *stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
cudaStreamSynchronize(*stream);
// update rng state
buffer->reSeed(seed);
buffer->setOffset(0);
buffer->propagateToDevice(buffer, *stream);
}
/**
* Return the length of a shape buffer
* based on the pointer
* @param buffer the buffer pointer to check
* @return
*/
int lengthForShapeBufferPointer(sd::Pointer buffer) {
auto shapeBuffer = reinterpret_cast<sd::LongType *>(buffer);
return shape::shapeInfoLength(shape::rank(shapeBuffer));
}
/**
* The pointer to get the address for
*
* @param address the address to get the pointer
* @return the pointer for the given address
*/
sd::Pointer pointerForAddress(sd::LongType address) { return reinterpret_cast<sd::Pointer>(address); }
void prescanArrayRecursive(sd::Pointer *extras, int *dZ, int *dX, int numElements, int level) {
auto stream = reinterpret_cast<cudaStream_t *>(extras[1]);
auto g_scanBlockSums = reinterpret_cast<int **>(extras[2]);
int blockSize = 512; // max size of the thread blocks
int numBlocks = sd::math::sd_max<int>(1, static_cast<int>(ceil(static_cast<float>(numElements) / (2.f * blockSize))));
int numThreads;
if (numBlocks > 1)
numThreads = blockSize;
else if (sd::isPowerOfTwo(numElements))
numThreads = numElements / 2;
else
numThreads = sd::floorPow2(numElements);
int numEltsPerBlock = numThreads * 2;
// if this is a non-power-of-2 array, the last block will be non-full
// compute the smallest power of 2 able to compute its scan.
int numEltsLastBlock = numElements - (numBlocks - 1) * numEltsPerBlock;
int numThreadsLastBlock = sd::math::sd_max<int>(1, numEltsLastBlock / 2);
int np2LastBlock = 0;
int sharedMemLastBlock = 0;
if (numEltsLastBlock != numEltsPerBlock) {
np2LastBlock = 1;
if (!sd::isPowerOfTwo(numEltsLastBlock)) numThreadsLastBlock = sd::floorPow2(numEltsLastBlock);
unsigned int extraSpace = (2 * numThreadsLastBlock) / NUM_BANKS;
sharedMemLastBlock = sizeof(int) * (2 * numThreadsLastBlock + extraSpace);
}
// padding space is used to avoid shared memory bank conflicts
int extraSpace = numEltsPerBlock / NUM_BANKS;
int sharedMemSize = sizeof(int) * (numEltsPerBlock + extraSpace);
// setup execution parameters
// if NP2, we process the last block separately
dim3 grid(sd::math::sd_max<int>(1, numBlocks - np2LastBlock), 1, 1);
dim3 threads(numThreads, 1, 1);
dim3 gridOnes(1, 1, 1);
dim3 threadsOnes(numThreadsLastBlock, 1, 1);
if (sharedMemSize < 2048) sharedMemSize = 2048;
if (sharedMemLastBlock < 2048) sharedMemLastBlock = 2048;
// execute the scan
if (numBlocks > 1) {
sd::prescanLauncher<true, false>(grid, threads, sharedMemSize, stream, dZ, dX, g_scanBlockSums[level],
numThreads * 2, 0, 0);
if (np2LastBlock) {
sd::prescanLauncher<true, true>(gridOnes, threadsOnes, sharedMemLastBlock, stream, dZ, dX, g_scanBlockSums[level],
numEltsLastBlock, numBlocks - 1, numElements - numEltsLastBlock);
}
// After scanning all the sub-blocks, we are mostly done. But now we
// need to take all of the last values of the sub-blocks and scan those.
// This will give us a new value that must be sdded to each block to
// get the final results.
// recursive (CPU) call
prescanArrayRecursive(extras, g_scanBlockSums[level], g_scanBlockSums[level], numBlocks, level + 1);
sd::uniformAdd<<<grid, threads, 1024, *stream>>>(dZ, g_scanBlockSums[level], numElements - numEltsLastBlock, 0, 0);
sd::DebugHelper::checkGlobalErrorCode("uniform addfailed(...) failed");
if (np2LastBlock) {
sd::uniformAdd<<<1, numThreadsLastBlock, 1024, *stream>>>(dZ, g_scanBlockSums[level], numEltsLastBlock, numBlocks - 1,
numElements - numEltsLastBlock);
sd::DebugHelper::checkGlobalErrorCode("concat general case failed(...) failed");
}
} else if (sd::isPowerOfTwo(numElements)) {
sd::prescanLauncher<false, false>(grid, threads, sharedMemSize, stream, dZ, dX, 0, numThreads * 2, 0, 0);
} else {
sd::prescanLauncher<false, true>(grid, threads, sharedMemSize, stream, dZ, dX, 0, numElements, 0, 0);
}
sd::DebugHelper::checkErrorCode(stream, "prescanArray(...) failed");
}
////////////////////////////////////////////////////////////////////////
void execReduce3All(sd::Pointer *extraPointers, int opNum, OpaqueNDArray x, OpaqueNDArray y, OpaqueNDArray z, OpaqueNDArray dimension, void *extraParams) {
try {
x->prepareSpecialUse({z}, {x, y, dimension});
x->preparePrimaryUse({}, {dimension});
auto dimensionPtr = dimension != nullptr ? reinterpret_cast<sd::LongType *>(dimension->buffer()) : nullptr;
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
auto xTadPack =
sd::ConstantTadHelper::getInstance().tadForDimensions(x->shapeInfo(), dimensionPtr, dimensionLength);
auto xTadShapeInfo = xTadPack->specialShapeInfo();
auto xOffsets = xTadPack->specialOffsets();
auto yTadPack =
sd::ConstantTadHelper::getInstance().tadForDimensions(y->shapeInfo(), dimensionPtr, dimensionLength);
auto yTadShapeInfo = yTadPack->specialShapeInfo();
auto yOffsets = yTadPack->specialOffsets();
sd::LaunchContext lc(extraPointers[1], extraPointers[4], extraPointers[5], extraPointers[3]);
NativeOpExecutioner::execReduce3All(&lc, opNum,
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->buffer(),
x->shapeInfo(),
shape::isEmptyConst(x->shapeInfo()) ? nullptr : x->specialBuffer(),
x->specialShapeInfo(),
extraParams,
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->buffer(),
y->shapeInfo(),
shape::isEmptyConst(y->shapeInfo()) ? nullptr : y->specialBuffer(),
y->specialShapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->buffer(),
z->shapeInfo(),
shape::isEmptyConst(z->shapeInfo()) ? nullptr : z->specialBuffer(),
z->specialShapeInfo(),
dimensionPtr,
dimensionLength, xTadShapeInfo,
xOffsets, yTadShapeInfo, yOffsets);
x->registerSpecialUse({z}, {x, y});
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void sort(sd::Pointer *extraPointers, OpaqueNDArray x, bool descending) {
try {
// Retrieve the CUDA stream from extraPointers
cudaStream_t *stream = nullptr;
if (extraPointers != nullptr && extraPointers[1] != nullptr) {
stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
} else {
// If no stream is provided, use the default stream
stream = sd::LaunchContext::defaultContext()->getCudaStream();
}
// Extract shape information from NDArray*
const sd::LongType *xShapeInfo = x->shapeInfo();
const sd::LongType *dXShapeInfo = x->specialShapeInfo();
// Calculate the length of the array
auto xLength = shape::length(xShapeInfo);
// Get element-wise stride (not used in original logic but retrieved for consistency)
auto xEWS = shape::elementWiseStride(xShapeInfo);
// Determine the data type of the array
auto xType = sd::ArrayOptions::dataType(xShapeInfo);
// Check if xLength is a power of 2 and within the specified limit
if ((xLength != 0) && ((xLength & (xLength - 1)) == 0) && (xLength <= 1024 * 1024 * 10)) {
// Get the launch dimensions for full sort
dim3 launchDims = getSortFullDims(xLength);
// Perform bitonic sort steps
for (int k = 2; k <= xLength; k *= 2) {
for (int j = k >> 1; j > 0; j >>= 1) {
BUILD_SINGLE_SELECTOR(xType, bitonicSortStepGeneric,
(launchDims, stream, x->specialBuffer(), dXShapeInfo, j, k, xLength, descending),
SD_NUMERIC_TYPES);
}
}
} else {
// Get the launch dimensions for arbitrary sort
dim3 launchDims = getSortFullDims(xLength);
// Determine the maximum window size
int max = 2, dg = 0;
while (max < xLength) {
max <<= 1;
dg++;
}
max <<= 1;
// Perform bitonic sort steps for arbitrary window sizes
for (int window = 2; window < max; window <<= 1) {
int n = window;
int rev = 0;
do {
int half = n >> 1;
BUILD_SINGLE_SELECTOR(xType, bitonicArbitraryStepGeneric,
(launchDims, stream, x->specialBuffer(), dXShapeInfo, n, xLength, rev, descending),
SD_NUMERIC_TYPES);
n >>= 1;
rev = 1;
} while (n > 1);
}
}
// Check for CUDA errors after sort execution
sd::DebugHelper::checkErrorCode(stream, "sort(...) failed");
} catch (std::exception &e) {
// Handle exceptions by setting error codes and messages in the LaunchContext
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void sortByKey(sd::Pointer *extraPointers, OpaqueNDArray x,
OpaqueNDArray y, bool descending) {
try {
// Retrieve the CUDA stream from extraPointers[1]
cudaStream_t *stream = nullptr;
if (extraPointers != nullptr && extraPointers[1] != nullptr) {
stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
} else {
// If no stream is provided, use the default stream from LaunchContext
stream = sd::LaunchContext::defaultContext()->getCudaStream();
}
// Extract shape information from NDArray* objects
const sd::LongType *xShapeInfo = x->shapeInfo();
const sd::LongType *dXShapeInfo = x->specialShapeInfo();
const sd::LongType *yShapeInfo = y->shapeInfo();
const sd::LongType *dyShapeInfo = y->specialShapeInfo();
// Calculate the lengths of the arrays
auto xLength = shape::length(xShapeInfo);
auto yLength = shape::length(yShapeInfo);
// Get element-wise strides (optional, based on original logic)
auto xEWS = shape::elementWiseStride(xShapeInfo);
// Determine the data types of the arrays
auto xType = sd::ArrayOptions::dataType(xShapeInfo);
auto yType = sd::ArrayOptions::dataType(yShapeInfo);
// Check if either array is empty
if (shape::isEmptyConst(xShapeInfo) || shape::isEmptyConst(yShapeInfo)) return;
// Ensure that keys and values have the same length
if (xLength != yLength) THROW_EXCEPTION("sortByKey: keys and values must have the same size");
// Check if xLength is a power of 2 and within the specified limit
if ((xLength != 0) && ((xLength & (xLength - 1)) == 0) && (xLength <= 1024 * 1024 * 10)) {
// Get the launch dimensions for full sort
dim3 launchDims = getSortFullDims(xLength);
// Perform bitonic sort steps
for (int k = 2; k <= xLength; k *= 2) {
for (int j = k >> 1; j > 0; j >>= 1) {
BUILD_DOUBLE_SELECTOR(xType, yType, bitonicSortStepGenericKey,
(launchDims, stream, x->specialBuffer(),
dXShapeInfo, y->specialBuffer(), dyShapeInfo, j, k, xLength, descending),
SD_NUMERIC_TYPES, SD_NUMERIC_TYPES);
}
}
} else {
// Determine the number of threads and blocks
int numThreads = sd::math::sd_min<int>(512, xLength);
int numBlocks = xLength / numThreads;
if (xLength % numThreads > 0 || numBlocks == 0) numBlocks++;
numBlocks = sd::math::sd_min<int>(512, numBlocks);
dim3 launchDims(numBlocks, numThreads, 32768);
// Determine the maximum window size
int max = 2;
while (max < xLength) {
max <<= 1;
}
max <<= 1;
// Perform bitonic sort steps for arbitrary window sizes
for (int window = 2; window < max; window <<= 1) {
int n = window;
int rev = 0;
do {
BUILD_DOUBLE_SELECTOR(xType, yType, bitonicArbitraryStepGenericKey,
(launchDims, stream, x->specialBuffer(),
dXShapeInfo, y->specialBuffer(), dyShapeInfo, n, xLength, rev, descending),
SD_NUMERIC_TYPES, SD_NUMERIC_TYPES);
n >>= 1;
rev = 1;
} while (n > 1);
}
}
// Check for CUDA errors after sort execution
sd::DebugHelper::checkErrorCode(stream, "sortByKey(...) failed");
}
catch (std::exception &e) {
// Handle exceptions by setting error codes and messages in the LaunchContext
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void sortByValue(sd::Pointer *extraPointers,OpaqueNDArray x,
OpaqueNDArray y, bool descending) {
try {
// Retrieve the CUDA stream from extraPointers[1]
cudaStream_t *stream = nullptr;
if (extraPointers != nullptr && extraPointers[1] != nullptr) {
stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
} else {
// If no stream is provided, use the default stream from LaunchContext
stream = sd::LaunchContext::defaultContext()->getCudaStream();
}
// Extract shape information from NDArray* objects
const sd::LongType *xShapeInfo = x->shapeInfo();
const sd::LongType *dXShapeInfo = x->specialShapeInfo();
const sd::LongType *yShapeInfo = y->shapeInfo();
const sd::LongType *dyShapeInfo = y->specialShapeInfo();
// Calculate the lengths of the arrays
auto xLength = shape::length(xShapeInfo);
auto yLength = shape::length(yShapeInfo);
// Get element-wise strides (optional, based on original logic)
auto xEWS = shape::elementWiseStride(xShapeInfo);
// Determine the data types of the arrays
auto xType = sd::ArrayOptions::dataType(yShapeInfo); // Note the swapped types in original code
auto yType = sd::ArrayOptions::dataType(xShapeInfo);
// Check if either array is empty
if (shape::isEmptyConst(xShapeInfo) || shape::isEmptyConst(yShapeInfo)) return;
// Ensure that keys and values have the same length
if (xLength != yLength) THROW_EXCEPTION("sortByValue: keys and values must have the same size");
// Check if xLength is a power of 2 and within the specified limit
if ((xLength != 0) && ((xLength & (xLength - 1)) == 0) && (xLength <= 1024 * 1024 * 10)) {
// Get the launch dimensions for full sort
dim3 launchDims = getSortFullDims(xLength);
// Perform bitonic sort steps
for (int k = 2; k <= xLength; k *= 2) {
for (int j = k >> 1; j > 0; j >>= 1) {
BUILD_DOUBLE_SELECTOR(xType, yType, bitonicSortStepGenericKey,
(launchDims, stream, y->specialBuffer(),
dyShapeInfo, x->specialBuffer(),
dXShapeInfo, j, k, xLength, descending),
SD_NUMERIC_TYPES, SD_NUMERIC_TYPES);
}
}
} else {
// Determine the number of threads and blocks
dim3 launchDims = getSortFullDims(xLength);
// Determine the maximum window size
int max = 2;
while (max < xLength) {
max <<= 1;
}
max <<= 1;
// Perform bitonic sort steps for arbitrary window sizes
for (int window = 2; window < max; window <<= 1) {
int n = window;
int rev = 0;
do {
BUILD_DOUBLE_SELECTOR(xType, yType, bitonicArbitraryStepGenericKey,
(launchDims, stream, y->specialBuffer(),
dyShapeInfo, x->specialBuffer(), dXShapeInfo, n, xLength, rev, descending),
SD_NUMERIC_TYPES, SD_NUMERIC_TYPES);
n >>= 1;
rev = 1;
} while (n > 1);
}
}
// Check for CUDA errors after sort execution
sd::DebugHelper::checkErrorCode(stream, "sortByValue(...) failed");
}
catch (std::exception &e) {
// Handle exceptions by setting error codes and messages in the LaunchContext
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void sortTadByKey(sd::Pointer *extraPointers,
OpaqueNDArray x,
OpaqueNDArray y,
OpaqueNDArray dimension,
bool descending) {
try {
// Retrieve the CUDA stream from extraPointers[1]
cudaStream_t *stream = nullptr;
if (extraPointers != nullptr && extraPointers[1] != nullptr) {
stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
} else {
// If no stream is provided, use the default stream from LaunchContext
stream = sd::LaunchContext::defaultContext()->getCudaStream();
}
// Extract shape information from NDArray* objects
sd::LongType *xShapeInfo = x->shapeInfo();
const sd::LongType *dXShapeInfo = x->specialShapeInfo();
const sd::LongType *yShapeInfo = y->shapeInfo();
const sd::LongType *dyShapeInfo = y->specialShapeInfo();
// Determine the data types of the arrays
auto xType = sd::ArrayOptions::dataType(xShapeInfo);
auto yType = sd::ArrayOptions::dataType(yShapeInfo);
// Get the dimension buffer and length
auto dimensionPtr = reinterpret_cast<sd::LongType *>(dimension->buffer());
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
// Get the TAD pack for the given dimensions
auto tadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(xShapeInfo, dimensionPtr,dimensionLength);
// Get the number of TADs
auto numTads = tadPack->numberOfTads();
// Get the launch dimensions for sorting TADs
dim3 launchDims = getSortTadDims(numTads);
// Execute the sortTadByKey operation based on data types
BUILD_DOUBLE_SELECTOR(xType, yType, oesTadGenericKey,
(launchDims, stream, x->specialBuffer(),
dXShapeInfo, y->specialBuffer(), dyShapeInfo,
dimensionPtr, dimensionLength, tadPack->platformShapeInfo(), tadPack->platformOffsets(), descending),
SD_NUMERIC_TYPES, SD_NUMERIC_TYPES);
// Check for CUDA errors after sort execution
sd::DebugHelper::checkErrorCode(stream, "sortTadByKey(...) failed");
}
catch (std::exception &e) {
// Handle exceptions by setting error codes and messages in the LaunchContext
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void sortTadByValue(sd::Pointer *extraPointers,
OpaqueNDArray x,
OpaqueNDArray y,
OpaqueNDArray dimension,
bool descending) {
try {
// Retrieve the CUDA stream from extraPointers[1]
cudaStream_t *stream = nullptr;
if (extraPointers != nullptr && extraPointers[1] != nullptr) {
stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
} else {
// If no stream is provided, use the default stream from LaunchContext
stream = sd::LaunchContext::defaultContext()->getCudaStream();
}
// Extract shape information from NDArray* objects
sd::LongType *xShapeInfo = x->shapeInfo();
const sd::LongType *dXShapeInfo = x->specialShapeInfo();
const sd::LongType *yShapeInfo = y->shapeInfo();
const sd::LongType *dyShapeInfo = y->specialShapeInfo();
// Determine the data types of the arrays
auto xType = sd::ArrayOptions::dataType(yShapeInfo); // Note the swapped types in original code
auto yType = sd::ArrayOptions::dataType(xShapeInfo);
// Get the dimension buffer and length
auto dimensionPtr = reinterpret_cast<sd::LongType *>(dimension->buffer());
sd::LongType dimensionLength = static_cast<sd::LongType>(shape::length(dimension->shapeInfo()));
// Get the TAD pack for the given dimensions
auto tadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(xShapeInfo, dimensionPtr,dimension->lengthOf());
// Get the number of TADs
auto numTads = tadPack->numberOfTads();
// Get the launch dimensions for sorting TADs
dim3 launchDims = getSortTadDims(numTads);
// Execute the sortTadByValue operation based on data types
BUILD_DOUBLE_SELECTOR(xType, yType, oesTadGenericKey,
(launchDims, stream, y->specialBuffer(), dyShapeInfo, x->specialBuffer(), dXShapeInfo,
dimensionPtr, dimensionLength, tadPack->platformShapeInfo(), tadPack->platformOffsets(), descending),
SD_NUMERIC_TYPES, SD_NUMERIC_TYPES);
// Check for CUDA errors after sort execution
sd::DebugHelper::checkErrorCode(stream, "sortTadByValue(...) failed");
}
catch (std::exception &e) {
// Handle exceptions by setting error codes and messages in the LaunchContext
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
void sortTad(sd::Pointer *extraPointers, OpaqueNDArray x,
sd::LongType *dimension, sd::LongType dimensionLength,
sd::LongType *tadShapeInfo, sd::LongType *tadOffsets, bool descending) {
try {
// Retrieve the CUDA stream from extraPointers[1]
cudaStream_t *stream = nullptr;
if (extraPointers != nullptr && extraPointers[1] != nullptr) {
stream = reinterpret_cast<cudaStream_t *>(extraPointers[1]);
} else {
// If no stream is provided, use the default stream from LaunchContext
stream = sd::LaunchContext::defaultContext()->getCudaStream();
}
// Extract shape information from NDArray* objects
sd::LongType *xShapeInfo = x->shapeInfo();
sd::LongType *dXShapeInfo = x->specialShapeInfo();
// Determine the data type of the array
auto xType = sd::ArrayOptions::dataType(xShapeInfo);
// Get the TAD pack for the given dimensions
auto tadPack = sd::ConstantTadHelper::getInstance().tadForDimensions(xShapeInfo, dimension,dimensionLength);
// Get the number of TADs
auto numTads = tadPack->numberOfTads();
// Get the launch dimensions for sorting TADs
dim3 launchDims = getSortTadLarge(numTads);
// Execute the sortTad operation based on data type
BUILD_SINGLE_SELECTOR(
xType, oesTadGeneric,
(launchDims, stream, x->specialBuffer(), dXShapeInfo, dimension, dimensionLength, tadShapeInfo, tadOffsets, descending),
SD_NUMERIC_TYPES
);
// Check for CUDA errors after sort execution
sd::DebugHelper::checkErrorCode(stream, "sortTad(...) failed");
}
catch (std::exception &e) {
// Handle exceptions by setting error codes and messages in the LaunchContext
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
////////////////////
void SD_KERNEL tryPointerKernel(void *p, int len) {
auto buf = reinterpret_cast<int8_t *>(p);
auto tid = threadIdx.x + blockIdx.x * blockDim.x;
__shared__ int b;
if (tid < len) atomicAdd(&b, buf[tid]);
__syncthreads();
}
void tryPointer(sd::Pointer extra, sd::Pointer p, int len) {
try {
cudaStream_t stream;
cudaStreamCreate(&stream);
tryPointerKernel<<<256, 512, len + 64, stream>>>(p, len);
sd::DebugHelper::checkGlobalErrorCode("try pointer failed(...) failed");
auto e = cudaStreamSynchronize(stream);
if (e != 0) {
THROW_EXCEPTION("CUDA error");
}
cudaStreamDestroy(stream);
} catch (std::exception &e) {
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(1);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage(e.what());
}
}
bool isBlasVersionMatches(int major, int minor, int build) {
auto result = major == sd::Environment::getInstance()._blasMajorVersion &&
minor == sd::Environment::getInstance()._blasMinorVersion &&
build == sd::Environment::getInstance()._blasPatchVersion;
if (!result) {
sd_printf("CUDA/cuBLAS version mismatch. Expected: %i.%i.%i but got %i.%i.%i instead\n",
sd::Environment::getInstance()._blasMajorVersion, sd::Environment::getInstance()._blasMinorVersion,
sd::Environment::getInstance()._blasPatchVersion, major, minor, build);
sd::LaunchContext::defaultContext()->errorReference()->setErrorCode(152);
sd::LaunchContext::defaultContext()->errorReference()->setErrorMessage("CUDA/cuBLAS version mismatch");
}
return result;
}
void setGraphContextCudaContext(Context *ptr, void *stream, void *reductionPointer,
void *allocationPointer) {
ptr->setCudaContext(stream, reductionPointer, allocationPointer);
}
int binaryLevel() { return 0; }
int optimalLevel() { return 0; }
bool isMinimalRequirementsMet() { return true; }
bool isOptimalRequirementsMet() { return true; }
void setShapeBuffer(sd::LongType *inputShapeData,sd::DataType dt,sd::LongType *bufferToSet,char order,int elementWiseStride,bool isEmpty,bool isView) {
if (inputShapeData == nullptr) THROW_EXCEPTION("setShapeBuffer: inputShapeData is null");
if (bufferToSet == nullptr) THROW_EXCEPTION("setShapeBuffer: bufferToSet is null");
sd::LongType rank = inputShapeData[0];
if (rank > SD_MAX_RANK || rank < 0) THROW_EXCEPTION("Invalid rank for shape buffer.");
std::vector<sd::LongType> shape;
std::vector<sd::LongType> strides;
// shape, stride, data type
for (sd::LongType i = 1; i < rank * 2 + 1; i++) {
if (i <= rank) {
shape.push_back(inputShapeData[i]);
} else if (shape.size() == rank) {
strides.push_back(inputShapeData[i]);
}
}
auto len = shape::shapeInfoLength(rank);
for (int i = 0; i < len; i++) {
bufferToSet[i] = inputShapeData[i];
}
sd::ArrayOptions::setDataType(bufferToSet, dt);
if (isView) {
sd::ArrayOptions::toggleIsView(bufferToSet);
}
if (!sd::ArrayOptions::isEmpty(inputShapeData) && isEmpty) {
sd::ArrayOptions::toggleIsEmpty(bufferToSet);
}
if (rank == 0) {
// detect when the shape buffer values are unset.
auto len = shape::shapeInfoLength(rank);
// min number of values in a shape info buffer
bool allZero = true;
for (int i = 0; i < len; i++) {
if (bufferToSet[i] != 0) {
allZero = false;
break;
}
}
if (allZero) {
THROW_EXCEPTION("Found shape buffer with all zero values. Values likely unset.");
}
}
}