blob: 370baa113df884eca6ce44e21ffd94c78e785cbf [file]
#include "THCReduceApplyUtils.cuh"
#include "THCSortUtils.cuh"
#include "THCTensorCopy.h"
#include "THCTensorTypeUtils.cuh"
#include <thrust/device_ptr.h>
#include <thrust/sort.h>
#if CUDA_VERSION >= 7000
#include <thrust/system/cuda/execution_policy.h>
#endif
template <typename T>
struct ThrustGTOp {
__device__ bool operator()(const T& lhs, const T& rhs) const {
return THCNumerics<T>::gt(lhs, rhs);
}
};
template <typename T>
struct ThrustLTOp {
__device__ bool operator()(const T& lhs, const T& rhs) const {
return THCNumerics<T>::lt(lhs, rhs);
}
};
// Returns 2^(ceil(lg(n)) from Stanford bit twiddling hacks
unsigned long nextHighestPowerOf2(unsigned long n) {
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
n++;
return n;
}
// `base` is the base address of a tensor
// For each slice (defined as a linear point of `out`, from 0 ->
// (sliceSize - 1) * sliceStride, we fill that slice from `0` to
// `sliceSize - 1`.
template <typename IndexType, int Dim>
__global__ void
fillSliceWithIndex(TensorInfo<long, IndexType> out,
IndexType totalSlices,
IndexType sliceSize,
IndexType sliceStride) {
IndexType slice = getLinearBlockId<IndexType>();
if (slice >= totalSlices) {
return;
}
const unsigned long offset =
IndexToOffset<long, IndexType, Dim>::get(slice, out);
long* base = &out.data[offset];
for (long i = threadIdx.x; i < sliceSize; i += blockDim.x) {
// Torch indices are 1-based (hence the +1)
base[i * sliceStride] = i + TH_INDEX_BASE;
}
}
void THCudaLongTensor_fillSliceWithIndex(THCState* state,
THCudaLongTensor* t,
int dim) {
long dims = THCudaLongTensor_nDimension(state, t);
THArgCheck(dims <= MAX_CUTORCH_DIMS, 2, CUTORCH_DIM_WARNING);
long inElements = THCudaLongTensor_nElement(state, t);
long sliceSize = THCudaLongTensor_size(state, t, dim);
long numSlices = inElements / sliceSize;
dim3 grid;
if (!THC_getGridFromTiles(numSlices, grid)) {
THError("Slice to fill with indices is too large");
}
long maxThreads =
THCState_getCurrentDeviceProperties(state)->maxThreadsPerBlock;
long numThreads = sliceSize;
if (numThreads > maxThreads) {
numThreads = maxThreads;
}
dim3 block(numThreads);
#define FILL_INDEX(T, DIM) \
fillSliceWithIndex<T, DIM> \
<<<grid, block, 0, THCState_getCurrentStream(state)>>>( \
info, numSlices, sliceSize, info.strides[collapseDim])
if (TensorUtils<THCudaLongTensor>::canUse32BitIndexMath(state, t)) {
TensorInfo<long, unsigned int> info =
getTensorInfo<THCudaLongTensor, unsigned int>(state, t);
info.reduceDim(dim);
int collapseDim = info.collapseDims(dim);
if (info.isContiguous()) {
FILL_INDEX(unsigned int, -2);
} else {
if (info.dims == 1) {
FILL_INDEX(unsigned int, 1);
} else if (info.dims == 2) {
FILL_INDEX(unsigned int, 2);
} else {
FILL_INDEX(unsigned int, -1);
}
}
} else {
TensorInfo<long, unsigned long> info =
getTensorInfo<THCudaLongTensor, unsigned long>(state, t);
info.reduceDim(dim);
int collapseDim = info.collapseDims(dim);
// catch-all implementation
FILL_INDEX(unsigned long, -1);
}
#undef FILL_INDEX
THCudaCheck(cudaGetLastError());
}
// For slice sorting in Thrust; extracts a slice index from a linear
// index and uses that for comparison
struct SliceComp {
SliceComp(long size) : sliceSize(size) {}
__device__ bool operator()(const long& a, const long& b) const {
// Since the slices are guaranteed to be innermost, the segment is
// just via long division
long segA = a / sliceSize;
long segB = b / sliceSize;
return segA < segB;
}
const long sliceSize;
};
// For sorting in Thurst; extracts a within-slice index from a linear index
struct GlobalIndexToPerSliceIndex {
GlobalIndexToPerSliceIndex(long size) : sliceSize(size) {}
__device__ inline void operator()(long& v) const {
v = v % sliceSize + TH_INDEX_BASE;
}
const long sliceSize;
};
#include "generic/THCTensorSort.cu"
#include "THCGenerateAllTypes.h"