| 613 | |
| 614 | template <typename T> |
| 615 | void LaunchConv2DOp<GPUDevice, T>::operator()( |
| 616 | OpKernelContext* ctx, bool use_cudnn, bool cudnn_use_autotune, |
| 617 | const Tensor& input_param, const Tensor& filter, int row_dilation, |
| 618 | int col_dilation, int row_stride, int col_stride, const Padding& padding, |
| 619 | const std::vector<int64>& explicit_paddings, Tensor* output, |
| 620 | TensorFormat data_format) { |
| 621 | #if GOOGLE_CUDA && CUDNN_VERSION >= 8100 |
| 622 | using se::dnn::ExecutionPlanConfig; |
| 623 | using se::dnn::ExecutionPlanDesc; |
| 624 | using se::dnn::ProfileExecutionPlanResult; |
| 625 | #endif // GOOGLE_CUDA && CUDNN_VERSION >= 8100 |
| 626 | using se::dnn::AlgorithmConfig; |
| 627 | using se::dnn::AlgorithmDesc; |
| 628 | using se::dnn::ProfileResult; |
| 629 | auto* stream = ctx->op_device_context()->stream(); |
| 630 | OP_REQUIRES(ctx, stream, errors::Internal("No GPU stream available.")); |
| 631 | |
| 632 | if (!use_cudnn) { |
| 633 | ctx->SetStatus( |
| 634 | errors::Unimplemented("Conv2D for GPU is not currently supported " |
| 635 | "without cudnn")); |
| 636 | return; |
| 637 | } |
| 638 | |
| 639 | Tensor input = input_param; |
| 640 | const int64 in_batch = GetTensorDim(input, data_format, 'N'); |
| 641 | int64 in_rows = GetTensorDim(input, data_format, 'H'); |
| 642 | int64 in_cols = GetTensorDim(input, data_format, 'W'); |
| 643 | const int64 in_depths = GetTensorDim(input, data_format, 'C'); |
| 644 | const int64 patch_rows = filter.dim_size(0); |
| 645 | const int64 patch_cols = filter.dim_size(1); |
| 646 | const int64 patch_depths = filter.dim_size(2); |
| 647 | |
| 648 | // If the filter in-depth (patch_depths) is 1 and smaller than the input |
| 649 | // depth, it's a depthwise convolution. More generally, if the filter in-depth |
| 650 | // divides but is smaller than the input depth, it is a grouped convolution. |
| 651 | bool is_grouped_convolution = patch_depths != in_depths; |
| 652 | if (patch_rows == 1 && patch_cols == 1 && !is_grouped_convolution && |
| 653 | row_dilation == 1 && col_dilation == 1 && row_stride == 1 && |
| 654 | col_stride == 1 && data_format == FORMAT_NHWC && |
| 655 | (padding == VALID || padding == SAME)) { |
| 656 | // 1x1 filter, so call cublas directly. |
| 657 | const uint64 m = in_batch * in_rows * in_cols; |
| 658 | const uint64 k = patch_depths; |
| 659 | const uint64 n = filter.dim_size(3); |
| 660 | |
| 661 | auto a_ptr = AsDeviceMemory(input.template flat<T>().data(), |
| 662 | input.template flat<T>().size()); |
| 663 | auto b_ptr = AsDeviceMemory(filter.template flat<T>().data(), |
| 664 | filter.template flat<T>().size()); |
| 665 | auto c_ptr = AsDeviceMemory(output->template flat<T>().data(), |
| 666 | output->template flat<T>().size()); |
| 667 | |
| 668 | auto no_transpose = se::blas::Transpose::kNoTranspose; |
| 669 | bool blas_launch_status = |
| 670 | stream |
| 671 | ->ThenBlasGemm(no_transpose, no_transpose, n, m, k, 1.0f, b_ptr, n, |
| 672 | a_ptr, k, 0.0f, &c_ptr, n) |
nothing calls this directly
no test coverage detected