| 91 | } |
| 92 | |
| 93 | void Compute(OpKernelContext* context) override { |
| 94 | try { |
| 95 | SanityCheckInputs(context); |
| 96 | if (!context->status().ok()) return; |
| 97 | |
| 98 | const Tensor& src_tensor = MklGetInput(context, kIdxInput); |
| 99 | MklDnnShape src_dnn_shape; |
| 100 | GetMklShape(context, kIdxInput, &src_dnn_shape); |
| 101 | |
| 102 | // OneDNN has a notion of kernel_size and not depth_radius. |
| 103 | int kernel_size = 2 * depth_radius_ + 1; |
| 104 | float new_alpha = alpha_ * kernel_size; |
| 105 | |
| 106 | // if the input tensor is not an OneDNN Tensor, or if the last |
| 107 | // dimension is not channel, then just use Eigen. |
| 108 | // OneDNN only support normalization over the channel dimension. |
| 109 | if (!src_dnn_shape.IsMklTensor()) { |
| 110 | MklDefaultToEigen(context, src_tensor); |
| 111 | return; |
| 112 | } else if (!src_dnn_shape.IsMklChannelDim(src_dnn_shape.GetDimension() - |
| 113 | 1)) { |
| 114 | Tensor converted_tensor; |
| 115 | OP_REQUIRES_OK(context, |
| 116 | ConvertMklToTF<T>(context, src_tensor, src_dnn_shape, |
| 117 | &converted_tensor)); |
| 118 | MklDefaultToEigen(context, converted_tensor); |
| 119 | return; |
| 120 | } |
| 121 | // At this point, we can assume that the src is an OneDNN Tensor |
| 122 | // and we can enable the workspace |
| 123 | workspace_enabled_ = true; |
| 124 | |
| 125 | MklDnnData<T> src_dnn_data(&cpu_engine_); |
| 126 | MklDnnData<T> dst_dnn_data(&cpu_engine_); |
| 127 | MklDnnData<uint8> workspace_dnn_data(&cpu_engine_); |
| 128 | |
| 129 | TensorShape tf_output_shape = src_tensor.shape(); |
| 130 | |
| 131 | memory::desc src_md = src_dnn_shape.GetCurLayout(); |
| 132 | memory::dims input_dims = src_dnn_shape.GetSizesAsMklDnnDims(); |
| 133 | |
| 134 | // Create memory for user input. |
| 135 | // Since Tensorflow always performs normalization over last dimension, |
| 136 | // and OneDNN performs normalization over Channel, we tell OneDNN |
| 137 | // that input is in NHWC layout with Channel being the last dimension. |
| 138 | src_dnn_data.SetUsrMem(src_md, &src_tensor); |
| 139 | src_dnn_data.SetOpMemDesc(input_dims, MEMORY_FORMAT::nhwc); |
| 140 | src_dnn_data.SetUsrMemDataHandle(&src_tensor, fwd_stream_); |
| 141 | |
| 142 | // dst_dnn_data has the same shape as input. |
| 143 | dst_dnn_data.SetUsrMem(src_md); |
| 144 | dst_dnn_data.SetOpMemDesc(input_dims, MEMORY_FORMAT::nhwc); |
| 145 | |
| 146 | // Create LRN primitive descriptor. |
| 147 | // Tensorflow's normalization semantics is across channels. |
| 148 | // OneDNN also supports normalization within channel. |
| 149 | MklDnnThreadPool eigen_tp(context); |
| 150 | auto lrn_desc = lrn_forward::desc( |
nothing calls this directly
no test coverage detected