| 150 | void HardmaxPlugin::detachFromContext() noexcept {} |
| 151 | |
| 152 | int32_t HardmaxPlugin::enqueue(nvinfer1::PluginTensorDesc const* inputDesc, |
| 153 | nvinfer1::PluginTensorDesc const* outputDesc, void const* const* inputs, void* const* outputs, void* workspace, |
| 154 | cudaStream_t stream) noexcept |
| 155 | { |
| 156 | if (inputDesc[0].type != nvinfer1::DataType::kFLOAT) |
| 157 | { |
| 158 | return -1; |
| 159 | } |
| 160 | |
| 161 | CUBLAS_CALL(cublasSetStream(mCublas, stream)); |
| 162 | |
| 163 | auto const* data = static_cast<float const*>(inputs[0]); |
| 164 | auto* result = static_cast<float*>(outputs[0]); |
| 165 | |
| 166 | // Make sure output is initialized to all 0's. |
| 167 | // Later we will set the correct outputs to be 1's and not touch the rest. |
| 168 | CUDA_CALL(cudaMemsetAsync(result, 0, mDimProductOuter * mDimProductInner * mAxisSize * sizeof(float), stream)); |
| 169 | |
| 170 | // We use the workspace in the case that the first call to 'cublasIsamax' is insufficient. |
| 171 | // The first half of the workspace we use to copy the values of the axis into, so that we can |
| 172 | // subtract out the minimum value and call 'cublasIsamax' again. See the comment below. |
| 173 | // The second half of the workspace will be a costant array of 1's, necessary for our cublasSaxpy call. |
| 174 | auto* const axisFlat = static_cast<float* const>(workspace); |
| 175 | float* const ones = axisFlat + mAxisSize; |
| 176 | float const one = 1.0F; |
| 177 | CUDRIVER_CALL(cuMemsetD32Async(CUdeviceptr(ones), *reinterpret_cast<int const*>(&one), mAxisSize, stream)); |
| 178 | |
| 179 | // This plugin works by parallelizing the argmax operation along a single axis. |
| 180 | // This is efficient when the axis size is very large compared to the other dimensions. |
| 181 | // |
| 182 | // Consider an input shape (1, 512, 3) with axis = 1. This plugin will perform well because |
| 183 | // the work which is parallelized is over the large 512-element-long axis, and the work that is done |
| 184 | // serially is over the small 1-element-long and 3-element-long axes. |
| 185 | // |
| 186 | // However, when the axis size is small compared to the other dimensions, this plugin will be very |
| 187 | // inefficient. If the input shape is (1, 512, 3) and the hardmax is over axis = 2, then |
| 188 | // the work is parallelized over the small 3-element-long axis and the work is done serially over |
| 189 | // the large 512-element-long axis. A smarter plugin would try to recognize this and parallelize |
| 190 | // the work which would take longest. |
| 191 | for (int32_t outer = 0; outer < mDimProductOuter; outer++) |
| 192 | { |
| 193 | for (int32_t inner = 0; inner < mDimProductInner; inner++) |
| 194 | { |
| 195 | int32_t const axesOffset = outer * mDimProductInner * mAxisSize + inner; |
| 196 | float const* arr = &data[axesOffset]; |
| 197 | int32_t const stride = mDimProductInner; |
| 198 | int32_t argmaxResult; |
| 199 | CUBLAS_CALL(cublasIsamax(mCublas, mAxisSize, arr, stride, &argmaxResult)); |
| 200 | |
| 201 | // cublasIsamax returns 1-indexed so convert to 0-indexed |
| 202 | argmaxResult--; |
| 203 | |
| 204 | // cublasIsamax returns the index of the element with the highest absolute value. |
| 205 | // If this element is positive, then we know it is also the max. |
| 206 | // However, if it is negative, we need to |
| 207 | // 1) Copy the axis into our workspace |
| 208 | // 2) Subtract the minimum value we found from our array. This ensures that |
| 209 | // none of the values are negative, and that the largest element remains |
no outgoing calls
no test coverage detected