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

148 lines
5.3 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 raver119@gmail.com
//
#include <array/NDArrayFactory.h>
#include <ops/declarable/helpers/histogram.h>
#include "execution/cuda/LaunchDims.h"
#include "helpers/DebugHelper.h"
namespace sd {
namespace ops {
namespace helpers {
template <typename X, typename Z>
static void SD_KERNEL histogramKernel(void *xBuffer, const LongType *xShapeInfo, void *zBuffer,
const LongType *zShapeInfo, void *allocationPointer, void *reductionPointer,
LongType numBins, X *min_val, X *max_val) {
int tid = blockIdx.x * blockDim.x + threadIdx.x;
auto dx = reinterpret_cast<X *>(xBuffer);
auto result = reinterpret_cast<Z *>(zBuffer);
__shared__ Z *bins;
__shared__ int length;
__shared__ Z *reductor;
if (threadIdx.x == 0) {
extern __shared__ unsigned char shmem[];
bins = (Z *)shmem;
reductor = ((Z *)allocationPointer) + (numBins * blockIdx.x);
length = shape::length(xShapeInfo);
}
__syncthreads();
X binSize = X((*max_val - *min_val) / numBins);
// nullify bins
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
bins[e] = (Z)0;
}
__syncthreads();
for (int e = tid; e < length; e += blockDim.x * gridDim.x) {
int idx = int((dx[e] - *min_val) / binSize);
idx = math::sd_max(idx, 0); // atomicMax(&idx, 0);//atomicMax(&idx, 0);
idx = math::sd_min(idx, int(numBins - 1)); // atomicMin(&idx, int(numBins - 1));
math::atomics::sd_atomicAdd<Z>(&bins[idx], (Z)1);
}
__syncthreads();
// at this point all bins in shared memory are calculated, so we aggregate them now via threadfence trick
// transfer shared memory to reduction memory
if (gridDim.x > 1) {
unsigned int *tc = (unsigned int *)reductionPointer;
__shared__ bool amLast;
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
reductor[e] = bins[e];
}
__threadfence();
__syncthreads();
if (threadIdx.x == 0) {
unsigned int ticket = atomicInc(&tc[16384], gridDim.x);
amLast = (ticket == gridDim.x - 1);
}
__syncthreads();
if (amLast) {
tc[16384] = 0;
// nullify shared memory for future accumulation
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
bins[e] = (Z)0;
}
// accumulate reduced bins
for (int r = 0; r < gridDim.x; r++) {
Z *ptrBuf = ((Z *)allocationPointer) + (r * numBins);
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
math::atomics::sd_atomicAdd(&bins[e], ptrBuf[e]);
}
}
__syncthreads();
// write them out to Z
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
result[e] = bins[e];
}
}
} else {
// if there's only 1 block - just write away data
for (int e = threadIdx.x; e < numBins; e += blockDim.x) {
result[e] = bins[e];
}
}
}
template <typename X, typename Z>
static void histogram_(LaunchContext *context, void *xBuffer, const LongType *xShapeInfo,
const LongType *dxShapeInfo, void *zBuffer, const LongType *zShapeInfo, LongType numBins, void *min_val, void *max_val) {
dim3 histogramDims = getHistogramDims(shape::length(xShapeInfo),numBins);
int workspaceSize = histogramDims.x * numBins;
auto tmp = NDArrayFactory::create<Z>('c', {workspaceSize}, context);
histogramKernel<X, Z><<<histogramDims.x, histogramDims.y, histogramDims.z, *context->getCudaStream()>>>(
xBuffer, dxShapeInfo, zBuffer, zShapeInfo, tmp.specialBuffer(), context->getReductionPointer(), numBins,
reinterpret_cast<X *>(min_val), reinterpret_cast<X *>(max_val));
DebugHelper::checkErrorCode(context->getCudaStream(),"histogramKernel failed");
cudaStreamSynchronize(*context->getCudaStream());
}
void histogramHelper(LaunchContext *context, NDArray &input, NDArray &output) {
LongType numBins = output.lengthOf();
NDArray::registerSpecialUse({&output}, {&input});
auto min_val = input.reduceNumber(reduce::SameOps::Min);
auto max_val = input.reduceNumber(reduce::SameOps::Max);
BUILD_DOUBLE_SELECTOR(
input.dataType(), output.dataType(), histogram_,
(context, input.specialBuffer(), input.shapeInfo(), input.specialShapeInfo(), output.specialBuffer(),
output.specialShapeInfo(), numBins, min_val.specialBuffer(), max_val.specialBuffer()),
SD_COMMON_TYPES, SD_INTEGER_TYPES);
NDArray::registerSpecialUse({&output}, {&input});
}
} // namespace helpers
} // namespace ops
} // namespace sd