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

196 lines
7.1 KiB
Plaintext

/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
//
// @author Yurii Shyrma (iuriish@yahoo.com)
//
#include <array/NDArrayFactory.h>
#include <array/ResultSet.h>
#include <exceptions/cuda_exception.h>
#include <helpers/ConstantTadHelper.h>
#include <helpers/PointersManager.h>
#include <helpers/ShapeUtils.h>
#include <ops/declarable/helpers/transforms.h>
#include <numeric>
namespace sd {
namespace ops {
namespace helpers {
///////////////////////////////////////////////////////////////////
template <typename T>
__global__ static void splitCuda(const void* vx, const LongType* xShapeInfo, void* pVz,
const LongType* zTadShapeInfo, const LongType axis) {
const T* x = reinterpret_cast<const T*>(vx);
// Shared memory for caching shape information and related variables
extern __shared__ unsigned char shmem[];
LongType* sharedMem = reinterpret_cast<LongType*>(shmem);
// Shared variables
__shared__ LongType shared_xLen;
__shared__ LongType shared_totalThreads;
__shared__ int shared_xRank;
__shared__ LongType shared_zDim;
// Cached shape and stride pointers
__shared__ const LongType* shared_xShape;
__shared__ const LongType* shared_xStride;
__shared__ const LongType* shared_zTadShape;
__shared__ const LongType* shared_zTadStride;
__shared__ int shared_zTadRank;
if (threadIdx.x == 0) {
// Cache shape and stride information for xShapeInfo
shared_xRank = shape::rank(xShapeInfo);
shared_xShape = shape::shapeOf(xShapeInfo);
shared_xStride = shape::stride(xShapeInfo);
// Cache shape and stride information for zTadShapeInfo
shared_zTadRank = shape::rank(zTadShapeInfo);
shared_zTadShape = shape::shapeOf(zTadShapeInfo);
shared_zTadStride = shape::stride(zTadShapeInfo);
shared_zDim = shared_zTadShape[axis]; // Assuming zDim is constant across splits
// Cache length and total threads
shared_xLen = shape::length(xShapeInfo);
shared_totalThreads = gridDim.x * blockDim.x;
}
// Ensure all threads have access to the cached values
__syncthreads();
const LongType tid = blockIdx.x * blockDim.x + threadIdx.x;
// Allocate space in shared memory for coordinates
LongType* coords = sharedMem + threadIdx.x * shared_xRank;
for (LongType i = tid; i < shared_xLen; i += shared_totalThreads) {
// Convert linear index to multi-dimensional coordinates
INDEX2COORDS(i, shared_xRank, shared_xShape, coords);
LongType xOffset;
// Convert coordinates to linear index for x
COORDS2INDEX(shared_xRank, shared_xStride, coords, xOffset);
// Determine the split index along the specified axis
LongType splitIndex = coords[axis] / shared_zDim;
// Retrieve the pointer to the target output tensor
T* z = reinterpret_cast<T*>(reinterpret_cast<void**>(pVz)[splitIndex]);
// Update the coordinate along the split axis
coords[axis] %= shared_zDim;
LongType zOffset;
// Convert updated coordinates to linear index for z
COORDS2INDEX(shared_zTadRank, shared_zTadStride, coords, zOffset);
// Perform the split operation
z[zOffset] = x[xOffset];
}
}
///////////////////////////////////////////////////////////////////
template <typename T>
SD_HOST static void splitCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const cudaStream_t* stream,
const void* vx, const LongType* xShapeInfo, void* pVz,
const LongType* zTadShapeInfo, const LongType axis) {
splitCuda<T><<<blocksPerGrid, threadsPerBlock, 256, *stream>>>(vx, xShapeInfo, pVz, zTadShapeInfo, axis);
sd::DebugHelper::checkErrorCode(const_cast<cudaStream_t *>(stream), "splitCuda failed");
}
BUILD_SINGLE_TEMPLATE( void splitCudaLauncher,
(const int blocksPerGrid, const int threadsPerBlock, const cudaStream_t* stream, const void* vx,
const sd::LongType* xShapeInfo, void* pVz, const sd::LongType* zTadShapeInfo, const sd::LongType axis),
SD_COMMON_TYPES);
//////////////////////////////////////////////////////////////////////////
void split(LaunchContext* context, NDArray& input, std::vector<NDArray*>& outArrs, const LongType axis) {
const int numOfSubArrs = outArrs.size();
const auto sizeofT = input.sizeOfT();
for (int i = 0; i < numOfSubArrs; ++i) outArrs[i]->syncToDevice();
input.syncToDevice();
bool luckCase1 = false;
if (luckCase1) {
for (LongType i = 0; i < numOfSubArrs; ++i) {
luckCase1 &= outArrs[i]->ordering() == input.ordering();
if (!luckCase1) break;
}
}
if (luckCase1) { // for example {1,10} + {2,10} + {3,10} = {6, 10} order c; or {10,1} + {10,2} + {10,3} = {10, 6}
// order f
auto x = static_cast<const int8_t*>(input.specialBuffer());
for (LongType i = 0; i < numOfSubArrs; ++i) {
const auto memAmountToCopy = outArrs[i]->lengthOf() * sizeofT;
cudaMemcpyAsync(static_cast<int8_t*>(outArrs[i]->specialBuffer()), x, memAmountToCopy, cudaMemcpyDeviceToDevice,
*context->getCudaStream());
x = static_cast<const int8_t*>(x) + memAmountToCopy;
}
if (cudaStreamSynchronize(*context->getCudaStream()) != 0)
THROW_EXCEPTION("split cuda: luckCase1 failed!");
for (int i = 0; i < numOfSubArrs; ++i) outArrs[i]->tickWriteDevice();
input.tickReadDevice();
return;
}
const int threadsPerBlock = SD_MAX_NUM_THREADS / 2;
const int blocksPerGrid = (input.lengthOf() + threadsPerBlock - 1) / threadsPerBlock;
// prepare arrays of pointers on buffers and shapes
std::vector<void*> hOutBuffers(numOfSubArrs);
for (int i = 0; i < numOfSubArrs; ++i) hOutBuffers[i] = outArrs[i]->specialBuffer();
PointersManager manager(context, "helpers::split");
void* dOutBuffers = manager.replicatePointer(hOutBuffers.data(), hOutBuffers.size() * sizeof(void*));
BUILD_SINGLE_SELECTOR(input.dataType(), splitCudaLauncher,
(blocksPerGrid, threadsPerBlock, context->getCudaStream(), input.specialBuffer(),
input.specialShapeInfo(), dOutBuffers, outArrs[0]->specialShapeInfo(), axis),
SD_COMMON_TYPES);
manager.synchronize();
// }
for (int i = 0; i < numOfSubArrs; ++i) outArrs[i]->tickWriteDevice();
input.tickReadDevice();
}
} // namespace helpers
} // namespace ops
} // namespace sd