/* ****************************************************************************** * * * 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 raver119@gmail.com // @author Yurii Shyrma (iuriish@yahoo.com) // #include #include #include #include #include #include #include "execution/cuda/LaunchDims.h" #include "helpers/DebugHelper.h" namespace sd { namespace ops { namespace helpers { /////////////////////////////////////////////////////////////////// // x - indices, y - contains number of bad indices, z - input/output template SD_KERNEL static void checkIndicesCuda(const void *vx, const LongType *xShapeInfo, LongType *y, const LongType *zShapeInfo, const int axis) { const auto x = reinterpret_cast(vx); __shared__ LongType xRank, xLen, numOfBadIndxPerBlock; __shared__ const LongType *xShape, *xStride, *zShape; __shared__ LongType *coords; if (threadIdx.x == 0) { extern __shared__ unsigned char shmem[]; coords = reinterpret_cast(shmem); xRank = shape::rank(xShapeInfo); xLen = shape::length(xShapeInfo); xShape = shape::shapeOf(xShapeInfo); xStride = shape::stride(xShapeInfo); zShape = shape::shapeOf(zShapeInfo); numOfBadIndxPerBlock = 0; } __syncthreads(); auto xCoords = coords + threadIdx.x * xRank; for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < xLen; i += gridDim.x * blockDim.x) { INDEX2COORDS(i, xRank, xShape, xCoords); LongType xOffset; COORDS2INDEX(xRank, xStride, xCoords, xOffset); const LongType currentInd = x[xOffset]; const LongType limit = shape::sizeAt(zShapeInfo, axis == -1 ? xCoords[xRank - 1] : axis); if (currentInd >= limit) { sd::math::atomics::sd_atomicAdd(&numOfBadIndxPerBlock, 1); } } __syncthreads(); if (threadIdx.x == 0 && numOfBadIndxPerBlock != 0) { sd::math::atomics::sd_atomicAdd(y, numOfBadIndxPerBlock); } } /////////////////////////////////////////////////////////////////// template static void checkIndicesCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem, const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo, LongType *y, const LongType *zShapeInfo, const int axis) { checkIndicesCuda<<>>(vx, xShapeInfo, y, zShapeInfo, axis); sd::DebugHelper::checkErrorCode(const_cast(stream), "checkIndicesCuda failed"); } /////////////////////////////////////////////////////////////////// LongType checkIndices(LaunchContext *context, NDArray&indices, NDArray&output, const int axis) { const int threadsPerBlock = SD_MAX_NUM_THREADS / 2; const int blocksPerGrid = (indices.lengthOf() + threadsPerBlock - 1) / threadsPerBlock; const int sharedMem = threadsPerBlock * sizeof(LongType) * indices.rankOf() + 256; dim3 scatterDimsIndices = scatterDimsCheckIndices(indices.lengthOf(), indices.rankOf()); const auto xType = indices.dataType(); PointersManager manager(context, "scatterNDcheckIndices"); // scalar, initial value = 0 NDArray numOfBadIndx(INT64, context, true); NDArray::prepareSpecialUse({&numOfBadIndx}, {&indices}); BUILD_SINGLE_SELECTOR( xType, checkIndicesCudaLauncher, (scatterDimsIndices.y, scatterDimsIndices.x, scatterDimsIndices.z, context->getCudaStream(), indices.specialBuffer(), indices.specialShapeInfo(), reinterpret_cast(numOfBadIndx.specialBuffer()), output.specialShapeInfo(), axis), SD_INTEGER_TYPES); NDArray::registerSpecialUse({&numOfBadIndx}, {&indices}); manager.synchronize(); return numOfBadIndx.t(0); } /////////////////////////////////////////////////////////////////// // x - indices, y - updates, z - input/output template SD_KERNEL static void scatterLockCuda(const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) { const auto x = reinterpret_cast(vx); const auto y = reinterpret_cast(vy); auto z = reinterpret_cast(vz); __shared__ LongType xRank, yRank, zRank, xNonUnitDim, yNonUnitDim, zNonUnitDim; __shared__ const LongType *xShape, *yShape, *zShape, *xStride, *yStride, *zStride; __shared__ LongType xLen, zLen; __shared__ bool is1Dcase, xySameStride; __shared__ LongType *coords; if (threadIdx.x == 0) { extern __shared__ unsigned char shmem[]; coords = reinterpret_cast(shmem); xRank = shape::rank(xShapeInfo); yRank = shape::rank(yShapeInfo); zRank = shape::rank(zShapeInfo); xShape = shape::shapeOf(xShapeInfo); yShape = shape::shapeOf(yShapeInfo); zShape = shape::shapeOf(zShapeInfo); xStride = shape::stride(xShapeInfo); yStride = shape::stride(yShapeInfo); zStride = shape::stride(zShapeInfo); xLen = shape::length(xShapeInfo); zLen = shape::length(zShapeInfo); xNonUnitDim = yNonUnitDim = zNonUnitDim = 0; is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) && (shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) && (shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo)); if (is1Dcase) xySameStride = xStride[xNonUnitDim] == yStride[yNonUnitDim]; } __syncthreads(); LongType yOffset, zOffset; LongType zFirstCoord, *yCoords, *zCoords; for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < zLen; i += gridDim.x * blockDim.x) { if (!is1Dcase) { yCoords = coords + threadIdx.x * (yRank + zRank); zCoords = yCoords + yRank; INDEX2COORDS(i, zRank, zShape, zCoords); } for (LongType j = 0; j < xLen; ++j) { if (is1Dcase) { yOffset = j * yStride[yNonUnitDim]; zFirstCoord = x[xySameStride ? yOffset : j]; if (i != zFirstCoord) continue; zOffset = i * zStride[zNonUnitDim]; } else { INDEX2COORDS(j, xRank, xShape, yCoords); LongType xOffset; COORDS2INDEX(xRank, xStride, yCoords, xOffset); zFirstCoord = x[xOffset]; if (zCoords[0] != zFirstCoord) continue; for (LongType k = 0; k < yRank - xRank; ++k) yCoords[xRank + k] = zCoords[k + 1]; COORDS2INDEX(yRank, yStride, yCoords, yOffset); COORDS2INDEX(zRank, zStride, zCoords, zOffset); } switch (opCode) { case pairwise::Add: z[zOffset] += y[yOffset]; break; case pairwise::Subtract: z[zOffset] -= y[yOffset]; break; case pairwise::Multiply: z[zOffset] *= y[yOffset]; break; case pairwise::Divide: z[zOffset] /= y[yOffset]; break; case pairwise::ReverseSubtract: z[zOffset] = y[yOffset] - z[zOffset]; break; case pairwise::ReverseDivide: z[zOffset] = y[yOffset] / z[zOffset]; break; case pairwise::CopyPws: z[zOffset] = y[yOffset]; break; case pairwise::MaxPairwise: if (z[zOffset] < y[yOffset]) z[zOffset] = y[yOffset]; break; case pairwise::MinPairwise: if (z[zOffset] > y[yOffset]) z[zOffset] = y[yOffset]; break; default: continue; } } } } /////////////////////////////////////////////////////////////////// // x - indices, y - updates, z - input/output template SD_KERNEL static void scatterCuda(const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) { const auto x = reinterpret_cast(vx); const auto y = reinterpret_cast(vy); auto z = reinterpret_cast(vz); __shared__ LongType xRank, yRank, zRank, xNonUnitDim, yNonUnitDim, zNonUnitDim; __shared__ const LongType *xShape, *yShape, *zShape, *xStride, *yStride, *zStride; __shared__ LongType yLen; __shared__ bool is1Dcase, xySameStride; __shared__ LongType *coords; if (threadIdx.x == 0) { extern __shared__ unsigned char shmem[]; coords = reinterpret_cast(shmem); xRank = shape::rank(xShapeInfo); yRank = shape::rank(yShapeInfo); zRank = shape::rank(zShapeInfo); xShape = shape::shapeOf(xShapeInfo); yShape = shape::shapeOf(yShapeInfo); zShape = shape::shapeOf(zShapeInfo); xStride = shape::stride(xShapeInfo); yStride = shape::stride(yShapeInfo); zStride = shape::stride(zShapeInfo); yLen = shape::length(yShapeInfo); xNonUnitDim = yNonUnitDim = zNonUnitDim = 0; is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) && (shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) && (shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo)); if (is1Dcase) xySameStride = xStride[xNonUnitDim] == yStride[yNonUnitDim]; } __syncthreads(); LongType xOffset, yOffset, zOffset; LongType *yCoords, *zCoords; if (!is1Dcase) { yCoords = coords + threadIdx.x * (yRank + zRank); zCoords = yCoords + yRank; } for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < yLen; i += gridDim.x * blockDim.x) { if (is1Dcase) { yOffset = i * yStride[yNonUnitDim]; zOffset = x[xySameStride ? yOffset : i * xStride[xNonUnitDim]] * zStride[zNonUnitDim]; } else { INDEX2COORDS(i, yRank, yShape, yCoords); COORDS2INDEX(yRank, yStride, yCoords, yOffset); COORDS2INDEX(xRank, xStride, yCoords, xOffset); zCoords[0] = x[xOffset]; for (LongType j = 0; j < yRank - xRank; ++j) { zCoords[j + 1] = yCoords[xRank + j]; } COORDS2INDEX(zRank, zStride, zCoords, zOffset); } switch (opCode) { case pairwise::Add: z[zOffset] += y[yOffset]; break; case pairwise::Subtract: z[zOffset] -= y[yOffset]; break; case pairwise::Multiply: z[zOffset] *= y[yOffset]; break; case pairwise::Divide: z[zOffset] /= y[yOffset]; break; case pairwise::ReverseSubtract: z[zOffset] = y[yOffset] - z[zOffset]; break; case pairwise::ReverseDivide: z[zOffset] = y[yOffset] / z[zOffset]; break; case pairwise::CopyPws: z[zOffset] = y[yOffset]; break; case pairwise::MaxPairwise: if (z[zOffset] < y[yOffset]) z[zOffset] = y[yOffset]; break; case pairwise::MinPairwise: if (z[zOffset] > y[yOffset]) z[zOffset] = y[yOffset]; break; default: continue; } } } /////////////////////////////////////////////////////////////////// template static void scatterCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem, const cudaStream_t *stream, const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo, const bool lock) { if (lock) scatterLockCuda<<>>(opCode, vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo); else scatterCuda<<>>(opCode, vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo); sd::DebugHelper::checkErrorCode(const_cast(stream), "scatterLockCuda failed"); } /////////////////////////////////////////////////////////////////// void scatter(LaunchContext *context, pairwise::Ops op, NDArray&indices, NDArray&updates, NDArray &output, const bool lock) { const auto xType = indices.dataType(); const auto yType = updates.dataType(); dim3 launchDims = scatterDims(lock ? output.lengthOf() : updates.lengthOf(), updates.rankOf() + output.rankOf()); PointersManager manager(context, "scatter"); NDArray::prepareSpecialUse({&output}, {&updates, &indices}); BUILD_DOUBLE_SELECTOR(xType, yType, scatterCudaLauncher, (launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), op, indices.specialBuffer(), indices.specialShapeInfo(), updates.specialBuffer(), updates.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), lock), SD_INDEXING_TYPES, SD_GENERIC_NUMERIC_TYPES); NDArray::registerSpecialUse({&output}, {&updates, &indices}); manager.synchronize(); } /////////////////////////////////////////////////////////////////// // x - indices, y - updates, z - output template SD_KERNEL static void scatterNDLockCuda(const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) { const auto x = reinterpret_cast(vx); const auto y = reinterpret_cast(vy); auto z = reinterpret_cast(vz); __shared__ LongType xRank, yRank, zRank, biggerXYRank, xLastDim, xNonUnitDim, yNonUnitDim, zNonUnitDim; __shared__ const LongType *xShape, *yShape, *zShape, *xStride, *yStride, *zStride; __shared__ LongType zLen, len; __shared__ bool is1Dcase; __shared__ LongType *coords; if (threadIdx.x == 0) { extern __shared__ unsigned char shmem[]; coords = reinterpret_cast(shmem); xRank = shape::rank(xShapeInfo); yRank = shape::rank(yShapeInfo); zRank = shape::rank(zShapeInfo); xLastDim = shape::sizeAt(xShapeInfo, -1); xShape = shape::shapeOf(xShapeInfo); yShape = shape::shapeOf(yShapeInfo); zShape = shape::shapeOf(zShapeInfo); xStride = shape::stride(xShapeInfo); yStride = shape::stride(yShapeInfo); zStride = shape::stride(zShapeInfo); biggerXYRank = xRank > yRank ? xRank : yRank; xNonUnitDim = yNonUnitDim = zNonUnitDim = 0; is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) && (shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) && (shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo)); len = is1Dcase ? shape::length(xShapeInfo) : shape::length(xShapeInfo) / xLastDim; zLen = shape::length(zShapeInfo); } __syncthreads(); LongType yOffset, zOffset, xOffset; LongType *yCoords, *zCoords; if (!is1Dcase) { yCoords = coords + threadIdx.x * (biggerXYRank + zRank); zCoords = yCoords + biggerXYRank; } for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < zLen; i += gridDim.x * blockDim.x) { if (!is1Dcase) INDEX2COORDS(i, zRank, zShape, zCoords); for (LongType j = 0; j < len; j++) { if (is1Dcase) { if (x[j * xStride[xNonUnitDim]] != i) continue; COORDS2INDEX(yRank, yStride, yCoords, yOffset); COORDS2INDEX(zRank, zStride, zCoords, zOffset); } else { INDEX2COORDS(j, xRank - 1, xShape, yCoords); yCoords[xRank - 1] = 0; COORDS2INDEX(xRank, xStride, yCoords, xOffset); if (zCoords[0] != x[xOffset]) continue; bool matched = true; for (LongType k = 1; k < xLastDim; k++) { yCoords[xRank - 1] = k; COORDS2INDEX(xRank, xStride, yCoords, xOffset); if (zCoords[k] != x[xOffset]) { matched = false; break; } } if (!matched) continue; for (LongType k = xLastDim; k < zRank; ++k) yCoords[yRank - zRank + k] = zCoords[k]; COORDS2INDEX(yRank, yStride, yCoords, yOffset); COORDS2INDEX(zRank, zStride, zCoords, zOffset); } switch (opCode) { case pairwise::Add: z[zOffset] += y[yOffset]; break; case pairwise::Subtract: z[zOffset] -= y[yOffset]; break; case pairwise::Multiply: z[zOffset] *= y[yOffset]; break; case pairwise::Divide: z[zOffset] /= y[yOffset]; break; case pairwise::ReverseSubtract: z[zOffset] = y[yOffset] - z[zOffset]; break; case pairwise::ReverseDivide: z[zOffset] = y[yOffset] / z[zOffset]; break; case pairwise::CopyPws: z[zOffset] = y[yOffset]; break; case pairwise::MaxPairwise: if (z[zOffset] < y[yOffset]) z[zOffset] = y[yOffset]; break; case pairwise::MinPairwise: if (z[zOffset] > y[yOffset]) z[zOffset] = y[yOffset]; break; default: continue; } } } } /////////////////////////////////////////////////////////////////// // x - indices, y - updates, z - output template SD_KERNEL static void scatterNDCuda(const int opCode, const void* vx, const LongType* xShapeInfo, const void* vy, const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo) { // Cast input and output pointers const auto x = reinterpret_cast(vx); const auto y = reinterpret_cast(vy); auto z = reinterpret_cast(vz); // Shared memory for shape information and flags __shared__ LongType xRank, yRank, zRank, biggerXYRank, xLastDim, xNonUnitDim, yNonUnitDim, zNonUnitDim, yLen; __shared__ bool is1Dcase; // Shared memory for coordinates __shared__ LongType* coords; if (threadIdx.x == 0) { // Dynamically allocated shared memory extern __shared__ unsigned char shmem[]; coords = reinterpret_cast(shmem); // Initialize shared values xRank = shape::rank(xShapeInfo); yRank = shape::rank(yShapeInfo); zRank = shape::rank(zShapeInfo); xLastDim = shape::sizeAt(xShapeInfo, -1); yLen = shape::length(yShapeInfo); biggerXYRank = max(xRank, yRank); xNonUnitDim = yNonUnitDim = zNonUnitDim = 0; // Check if the operation involves 1D cases is1Dcase = (shape::isCommonVector(zShapeInfo, zNonUnitDim) || shape::isScalar(zShapeInfo)) && (shape::isCommonVector(yShapeInfo, yNonUnitDim) || shape::isScalar(yShapeInfo)) && (shape::isCommonVector(xShapeInfo, xNonUnitDim) || shape::isScalar(xShapeInfo)); } __syncthreads(); // Dynamically allocated memory for local coordinates LongType* yCoords = coords + threadIdx.x * (biggerXYRank + zRank); LongType* zCoords = yCoords + biggerXYRank; // Process each element in y for (LongType i = blockIdx.x * blockDim.x + threadIdx.x; i < yLen; i += gridDim.x * blockDim.x) { LongType yOffset, zOffset; // Convert linear index to multi-dimensional coordinates for y INDEX2COORDS(i, yRank, shape::shapeOf(yShapeInfo), yCoords); COORDS2INDEX(yRank, shape::stride(yShapeInfo), yCoords, yOffset); // Save the last coordinate of y if needed if (yRank >= xRank) { zCoords[xLastDim] = yCoords[xRank - 1]; } // Map y coordinates to x and z coordinates for (LongType j = 0; j < xLastDim; ++j) { yCoords[xRank - 1] = j; COORDS2INDEX(xRank, shape::stride(xShapeInfo), yCoords, zCoords[j]); } // Adjust remaining coordinates for z for (LongType j = xLastDim + 1; j < zRank; ++j) { zCoords[j] = yCoords[yRank - zRank + j]; } // Compute linear index for z COORDS2INDEX(zRank, shape::stride(zShapeInfo), zCoords, zOffset); // Perform the operation based on opCode switch (opCode) { case pairwise::Add: z[zOffset] += y[yOffset]; break; case pairwise::Subtract: z[zOffset] -= y[yOffset]; break; case pairwise::Multiply: z[zOffset] *= y[yOffset]; break; case pairwise::Divide: z[zOffset] /= y[yOffset]; break; case pairwise::ReverseSubtract: z[zOffset] = y[yOffset] - z[zOffset]; break; case pairwise::ReverseDivide: z[zOffset] = y[yOffset] / z[zOffset]; break; case pairwise::CopyPws: z[zOffset] = y[yOffset]; break; case pairwise::MaxPairwise: z[zOffset] = max(z[zOffset], y[yOffset]); break; case pairwise::MinPairwise: z[zOffset] = min(z[zOffset], y[yOffset]); break; default: break; } } } /////////////////////////////////////////////////////////////////// template static void scatterNDCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem, const cudaStream_t *stream, const int opCode, const void *vx, const LongType *xShapeInfo, const void *vy, const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo, const bool lock) { if (lock) scatterNDLockCuda<<>>(opCode, vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo); else scatterNDCuda<<>>(opCode, vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo); sd::DebugHelper::checkErrorCode(const_cast(stream), "scatterNDCuda failed"); } /////////////////////////////////////////////////////////////////// void scatterND(LaunchContext *context, pairwise::Ops op, NDArray&indices, NDArray&updates, NDArray &output, const bool lock) { const int xRank = indices.rankOf(); const int yRank = updates.rankOf(); const int zRank = output.rankOf(); dim3 launchDims = scatterNdDims(lock ? output.lengthOf() : updates.lengthOf(), ((yRank > xRank ? yRank : xRank) + zRank)); const auto xType = indices.dataType(); const auto yType = updates.dataType(); PointersManager manager(context, "scatterND"); NDArray::prepareSpecialUse({&output}, {&updates, &indices}); BUILD_DOUBLE_SELECTOR(xType, yType, scatterNDCudaLauncher, (launchDims.y, launchDims.x, launchDims.z, context->getCudaStream(), op, indices.specialBuffer(), indices.specialShapeInfo(), updates.specialBuffer(), updates.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo(), lock), SD_INDEXING_TYPES, SD_GENERIC_NUMERIC_TYPES); NDArray::registerSpecialUse({&output}, {&updates, &indices}); manager.synchronize(); } /////////////////////////////////////////////////////////////////// template SD_KERNEL void scatterForLossCuda(const void* vx, const LongType* xShapeInfo, void* vy, const LongType* yShapeInfo, void* vz, const LongType* zShapeInfo) { // Cast input and output pointers const auto x = reinterpret_cast(vx); auto y = reinterpret_cast(vy); auto z = reinterpret_cast(vz); // Shared memory for shape information and coordinates __shared__ LongType xLen; __shared__ LongType xRank; __shared__ const LongType* xShape; __shared__ const LongType* xStride; __shared__ const LongType* yStride; __shared__ const LongType* zStride; if (threadIdx.x == 0) { // Initialize shared memory variables xLen = shape::length(xShapeInfo); xRank = shape::rank(xShapeInfo); xShape = shape::shapeOf(xShapeInfo); xStride = shape::stride(xShapeInfo); yStride = shape::stride(yShapeInfo); zStride = zShapeInfo ? shape::stride(zShapeInfo) : nullptr; } __syncthreads(); // Calculate global thread index const LongType xInd = threadIdx.x + blockIdx.x * blockDim.x; // Return if the thread index exceeds the length of x if (xInd >= xLen) return; // Dynamically allocated shared memory for coordinates extern __shared__ unsigned char shmem[]; auto coords = reinterpret_cast(shmem) + threadIdx.x * (xRank + 1); // Convert linear index to coordinates for x INDEX2COORDS(xInd, xRank, xShape, coords); // Calculate offset for x LongType xOffset; COORDS2INDEX(xRank, xStride, coords, xOffset); // Update the last coordinate with the value from x coords[xRank] = x[xOffset]; // Calculate offset for y LongType yOffset; COORDS2INDEX(xRank + 1, yStride, coords, yOffset); if (z == nullptr) { // Gradient calculation y[yOffset] -= 1.f; } else { // Calculate offset for z LongType zOffset; COORDS2INDEX(xRank + 1, zStride, coords, zOffset); // Update z with the value from y z[zOffset] = y[yOffset]; } } /////////////////////////////////////////////////////////////////// template static void scatterForLossCudaLauncher(const int blocksPerGrid, const int threadsPerBlock, const int sharedMem, const cudaStream_t *stream, const void *vx, const LongType *xShapeInfo, void *vy, const LongType *yShapeInfo, void *vz, const LongType *zShapeInfo) { scatterForLossCuda <<>>(vx, xShapeInfo, vy, yShapeInfo, vz, zShapeInfo); sd::DebugHelper::checkErrorCode(const_cast(stream), "scatterUpdateCuda failed"); } /////////////////////////////////////////////////////////////////// void scatterForLoss(LaunchContext *context, NDArray&indices, NDArray &updates, NDArray &output, const bool calcGrad) { // shapes of indices and output must be the same // shape of indices should be the same as updates shape with last dimension excluded, for example if updates is // {a,b,c} then indices should be {a,b} PointersManager manager(context, "scatterForLoss"); dim3 launchDIms = scatterDims(indices.lengthOf(), updates.rankOf()); if (calcGrad) { NDArray::prepareSpecialUse({&updates}, {&indices}); BUILD_DOUBLE_SELECTOR( indices.dataType(), updates.dataType(), scatterForLossCudaLauncher, (launchDIms.y, launchDIms.x, launchDIms.z, context->getCudaStream(), indices.specialBuffer(), indices.specialShapeInfo(), updates.specialBuffer(), updates.specialShapeInfo(), nullptr, nullptr), SD_INDEXING_TYPES, SD_FLOAT_TYPES); NDArray::registerSpecialUse({&updates}, {&indices}); } else { NDArray::prepareSpecialUse({&output}, {&indices, &updates}); BUILD_DOUBLE_SELECTOR(indices.dataType(), updates.dataType(), scatterForLossCudaLauncher, (launchDIms.y, launchDIms.x, launchDIms.z, context->getCudaStream(), indices.specialBuffer(), indices.specialShapeInfo(), updates.specialBuffer(), updates.specialShapeInfo(), output.specialBuffer(), output.specialShapeInfo()), SD_INDEXING_TYPES, SD_FLOAT_TYPES); NDArray::registerSpecialUse({&output}, {&indices, &updates}); } manager.synchronize(); } } // namespace helpers } // namespace ops } // namespace sd