1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/stream_executor/dnn.h"
17 
18 #include "absl/hash/hash.h"
19 #include "absl/strings/str_cat.h"
20 #include "absl/strings/str_format.h"
21 
22 namespace stream_executor {
23 namespace dnn {
24 
25 constexpr DataType ToDataType<float>::value;
26 constexpr DataType ToDataType<double>::value;
27 constexpr DataType ToDataType<Eigen::half>::value;
28 constexpr DataType ToDataType<int8>::value;
29 constexpr DataType ToDataType<int32>::value;
30 
hash() const31 uint64 AlgorithmDesc::hash() const {
32   auto p = std::make_pair(algo_id(), tensor_ops_enabled());
33   return absl::Hash<decltype(p)>()(p);
34 }
35 
ToString() const36 std::string AlgorithmDesc::ToString() const {
37   if (tensor_ops_enabled()) {
38     return absl::StrCat(algo_id(), "#TC");
39   } else {
40     return absl::StrCat(algo_id());
41   }
42 }
43 
GetConvolveAlgorithms(bool with_winograd_nonfused,int cc_major,int cc_minor,std::vector<AlgorithmDesc> * out_algorithms)44 bool DnnSupport::GetConvolveAlgorithms(
45     bool with_winograd_nonfused, int cc_major, int cc_minor,
46     std::vector<AlgorithmDesc>* out_algorithms) {
47   return false;
48 }
49 
GetMIOpenConvolveAlgorithms(dnn::ConvolutionKind,dnn::DataType,Stream *,const dnn::BatchDescriptor &,DeviceMemoryBase input_data,const dnn::FilterDescriptor &,DeviceMemoryBase filter_data,const dnn::BatchDescriptor &,DeviceMemoryBase output_data,const dnn::ConvolutionDescriptor &,ScratchAllocator * scratch_allocator,std::vector<ProfileResult> *)50 bool DnnSupport::GetMIOpenConvolveAlgorithms(
51     dnn::ConvolutionKind /*kind*/, dnn::DataType /*element_type*/,
52     Stream* /*stream*/, const dnn::BatchDescriptor& /*input_descriptor*/,
53     DeviceMemoryBase input_data,
54     const dnn::FilterDescriptor& /*filter_descriptor*/,
55     DeviceMemoryBase filter_data,
56     const dnn::BatchDescriptor& /*output_descriptor*/,
57     DeviceMemoryBase output_data,
58     const dnn::ConvolutionDescriptor& /*convolution_descriptor*/,
59     ScratchAllocator* scratch_allocator,
60     std::vector<ProfileResult>* /*out_algorithms*/) {
61   return false;
62 }
63 
GetRnnAlgorithms(std::vector<AlgorithmDesc> * out_algorithms)64 bool DnnSupport::GetRnnAlgorithms(std::vector<AlgorithmDesc>* out_algorithms) {
65   return false;
66 }
67 
GetConvolveBackwardDataAlgorithms(bool with_winograd_nonfused,int cc_major,int cc_minor,std::vector<AlgorithmDesc> * out_algorithms)68 bool DnnSupport::GetConvolveBackwardDataAlgorithms(
69     bool with_winograd_nonfused, int cc_major, int cc_minor,
70     std::vector<AlgorithmDesc>* out_algorithms) {
71   return false;
72 }
73 
GetConvolveBackwardFilterAlgorithms(bool with_winograd_nonfused,int cc_major,int cc_minor,std::vector<AlgorithmDesc> * out_algorithms)74 bool DnnSupport::GetConvolveBackwardFilterAlgorithms(
75     bool with_winograd_nonfused, int cc_major, int cc_minor,
76     std::vector<AlgorithmDesc>* out_algorithms) {
77   return false;
78 }
79 
QuantizedActivationModeString(QuantizedActivationMode mode)80 std::string QuantizedActivationModeString(QuantizedActivationMode mode) {
81   switch (mode) {
82     case dnn::QuantizedActivationMode::k8Bit:
83       return "uint8";
84     case dnn::QuantizedActivationMode::k16Bit:
85       return "uint16";
86     case dnn::QuantizedActivationMode::k32Bit:
87       return "int32";
88     default:
89       LOG(FATAL) << "Unknown quantized_activation_mode "
90                  << static_cast<int32>(mode);
91   }
92   return "unknown quantized_activation_mode";
93 }
94 
ActivationModeString(ActivationMode mode)95 std::string ActivationModeString(ActivationMode mode) {
96   switch (mode) {
97     case ActivationMode::kNone:
98       return "none";
99     case ActivationMode::kSigmoid:
100       return "sigmoid";
101     case ActivationMode::kRelu:
102       return "relu";
103     case ActivationMode::kRelu6:
104       return "relu6";
105     case ActivationMode::kReluX:
106       return "reluX";
107     case ActivationMode::kTanh:
108       return "tanh";
109     case ActivationMode::kBandPass:
110       return "bandpass";
111     default:
112       LOG(FATAL) << "Unknown activation_mode " << static_cast<int32>(mode);
113   }
114   return "unknown activation_mode";
115 }
116 
ElementwiseOperationString(ElementwiseOperation op)117 std::string ElementwiseOperationString(ElementwiseOperation op) {
118   switch (op) {
119     case ElementwiseOperation::kAdd:
120       return "add";
121     case ElementwiseOperation::kMultiply:
122       return "multiply";
123     default:
124       LOG(FATAL) << "Unknown elementwise op " << static_cast<int32>(op);
125   }
126   return "unknown element wise op";
127 }
128 
DataLayoutString(DataLayout layout)129 std::string DataLayoutString(DataLayout layout) {
130   switch (layout) {
131     case DataLayout::kYXDepthBatch:
132       return "YXDepthBatch";
133     case DataLayout::kYXBatchDepth:
134       return "YXBatchDepth";
135     case DataLayout::kBatchYXDepth:
136       return "BatchYXDepth";
137     case DataLayout::kBatchDepthYX:
138       return "BatchDepthYX";
139     case DataLayout::kBatchDepthYX4:
140       return "BatchDepthYX4";
141     default:
142       LOG(FATAL) << "Unknown data layout " << static_cast<int32>(layout);
143   }
144   return "unknown data layout";
145 }
146 
FilterLayoutString(FilterLayout layout)147 std::string FilterLayoutString(FilterLayout layout) {
148   switch (layout) {
149     case FilterLayout::kOutputInputYX:
150       return "OutputInputYX";
151     case FilterLayout::kOutputYXInput:
152       return "OutputYXInput";
153     case FilterLayout::kOutputInputYX4:
154       return "OutputInputYX4";
155     case FilterLayout::kInputYXOutput:
156       return "InputYXOutput";
157     case FilterLayout::kYXInputOutput:
158       return "YXInputOutput";
159     default:
160       LOG(FATAL) << "Unknown filter layout " << static_cast<int32>(layout);
161   }
162   return "unknown filter layout";
163 }
164 
PadAlignmentString(PadAlignment alignment)165 std::string PadAlignmentString(PadAlignment alignment) {
166   switch (alignment) {
167     case PadAlignment::kDefault:
168       return "default";
169     case PadAlignment::kCudnnPadding:
170       return "cuDNN padding";
171     case PadAlignment::kTensorFlowPadding:
172       return "TensorFlow padding";
173   }
174   return "unknown pad alignment";
175 }
176 
operator <<(std::ostream & str,dnn::PadAlignment alignment)177 std::ostream& operator<<(std::ostream& str, dnn::PadAlignment alignment) {
178   return str << PadAlignmentString(alignment);
179 }
180 
ShortPoolingModeString(PoolingMode mode)181 std::string ShortPoolingModeString(PoolingMode mode) {
182   switch (mode) {
183     case PoolingMode::kMaximum:
184       return "Max";
185     case PoolingMode::kAverage:
186       return "Avg";
187     default:
188       LOG(FATAL) << "Unknown filter layout " << static_cast<int32>(mode);
189   }
190   return "unknown filter layout";
191 }
192 
GetDimIndices(const DataLayout & layout,const int data_dims)193 std::tuple<int, int, int> GetDimIndices(const DataLayout& layout,
194                                         const int data_dims) {
195   int depth_idx, batch_idx, spatial_idx;
196   switch (layout) {
197     case DataLayout::kYXBatchDepth:
198       depth_idx = data_dims - 1;
199       batch_idx = data_dims - 2;
200       spatial_idx = 0;
201       break;
202 
203     case DataLayout::kYXDepthBatch:
204       depth_idx = data_dims - 2;
205       batch_idx = data_dims - 1;
206       spatial_idx = 0;
207       break;
208 
209     case DataLayout::kBatchYXDepth:
210       depth_idx = data_dims - 1;
211       batch_idx = 0;
212       spatial_idx = 1;
213       break;
214 
215     case DataLayout::kBatchDepthYX:
216     case DataLayout::kBatchDepthYX4:
217       depth_idx = 1;
218       batch_idx = 0;
219       spatial_idx = 2;
220       break;
221 
222     default:
223       LOG(FATAL) << "Unknown layout " << layout;
224   }
225 
226   return std::make_tuple(depth_idx, batch_idx, spatial_idx);
227 }
228 
ReorderDims(const std::vector<int64> & input,const DataLayout & from,const DataLayout & to)229 std::vector<int64> ReorderDims(const std::vector<int64>& input,
230                                const DataLayout& from, const DataLayout& to) {
231   if (from == to) return input;
232 
233   int d_idx_from, b_idx_from, spatial_idx_from;
234   int d_idx_to, b_idx_to, spatial_idx_to;
235 
236   std::tie(d_idx_from, b_idx_from, spatial_idx_from) =
237       GetDimIndices(from, input.size());
238   std::tie(d_idx_to, b_idx_to, spatial_idx_to) =
239       GetDimIndices(to, input.size());
240 
241   std::vector<int64> reordered(input.size());
242   reordered[b_idx_to] = input[b_idx_from];
243   reordered[d_idx_to] = input[d_idx_from];
244 
245   for (size_t i = 0; i < input.size() - 2;
246        i++, spatial_idx_from++, spatial_idx_to++) {
247     reordered[spatial_idx_to] = input[spatial_idx_from];
248   }
249 
250   return reordered;
251 }
252 
253 // -- AlgorithmConfig
254 
ToString() const255 std::string AlgorithmConfig::ToString() const {
256   std::string algo = "none";
257   if (algorithm().has_value()) {
258     algo = algorithm()->ToString();
259   }
260   std::string algo_no_scratch = "none";
261   if (algorithm_no_scratch().has_value()) {
262     algo_no_scratch = algorithm_no_scratch()->ToString();
263   }
264   return absl::StrCat(algo, ", ", algo_no_scratch);
265 }
266 
267 // -- BatchDescriptor
268 
BatchDescriptor(int ndims)269 BatchDescriptor::BatchDescriptor(int ndims)
270     : value_max_(0.0),
271       value_min_(0.0),
272       quantized_activation_mode_(QuantizedActivationMode::k8Bit) {
273   tensor_.mutable_dimensions()->Resize(ndims + 2, 0);
274   set_layout(DataLayout::kYXDepthBatch);
275 }
276 
BatchDescriptor()277 BatchDescriptor::BatchDescriptor() : BatchDescriptor(/*ndims=*/2) {}
278 
full_dims(const DataLayout & layout) const279 std::vector<int64> BatchDescriptor::full_dims(const DataLayout& layout) const {
280   std::vector<int64> bdyx_dims(ndims() + 2);
281   bdyx_dims[0] = count();
282   bdyx_dims[1] = feature_map_count();
283   std::copy(spatial_size().begin(), spatial_size().end(),
284             bdyx_dims.begin() + 2);
285   return ReorderDims(bdyx_dims, DataLayout::kBatchDepthYX, layout);
286 }
287 
full_strides(const DataLayout & layout) const288 std::vector<int64> BatchDescriptor::full_strides(
289     const DataLayout& layout) const {
290   if (this->layout() == DataLayout::kBatchDepthYX4) {
291     LOG(FATAL)
292         << "Cannot compute full strides for batch descriptor " << ToString()
293         << ", because its layout is kBatchDepthYX4. In fact, "
294            "cudnnSetTensorNdDescriptor doesn't work for kBatchDepthYX4 at all. "
295            "Use cudnnSetTensor4DDescriptor to set cudnnTensorDescriptor_t "
296            "instead.";
297   }
298   std::vector<int64> phys_dims = full_dims(this->layout());
299   std::vector<int64> phys_strides(phys_dims.size());
300   phys_strides[ndims() + 1] = 1;
301   for (int i = ndims(); i >= 0; i--) {
302     phys_strides[i] = phys_strides[i + 1] * phys_dims[i + 1];
303   }
304   return ReorderDims(phys_strides, this->layout(), layout);
305 }
306 
CloneFrom(const BatchDescriptor & other)307 void BatchDescriptor::CloneFrom(const BatchDescriptor& other) {
308   tensor_ = other.tensor_;
309   value_max_ = other.value_max_;
310   value_min_ = other.value_min_;
311   quantized_activation_mode_ = other.quantized_activation_mode_;
312 }
313 
ToString() const314 std::string BatchDescriptor::ToString() const {
315   std::string spatial;
316   for (int i = 0; i < ndims(); i++) {
317     absl::StrAppendFormat(&spatial, "%d ", spatial_size()[i]);
318   }
319   return absl::StrFormat(
320       "{count: %d feature_map_count: %d spatial: %s "
321       "value_min: %f value_max: %f layout: %s}",
322       count(), feature_map_count(), spatial, value_min_, value_max_,
323       DataLayoutString(layout()));
324 }
325 
ToShortString() const326 std::string BatchDescriptor::ToShortString() const {
327   // All the constituent strings are less than 15 characters, so the
328   // small string optimization ensures that there will be at most one
329   // heap memory allocation.
330   std::string depth = absl::StrCat("d", feature_map_count());
331   std::string batch = absl::StrCat("b", count());
332 
333   std::string spatial = "s";
334   for (int i = 0; i < ndims(); i++) {
335     absl::StrAppendFormat(&spatial, "%d ", spatial_size()[i]);
336   }
337 
338   std::string suffix;
339   if (value_min() != value_max()) {
340     absl::StrAppend(&suffix, "[", value_min(), ";", value_max(), "]");
341   }
342   if (quantized_activation_mode() == QuantizedActivationMode::k16Bit) {
343     suffix += "_16bit";
344   }
345 
346   switch (layout()) {
347     case DataLayout::kYXDepthBatch:
348       return absl::StrCat(spatial, depth, batch, suffix);
349     case DataLayout::kYXBatchDepth:
350       return absl::StrCat(spatial, batch, depth, suffix);
351     case DataLayout::kBatchYXDepth:
352       return absl::StrCat(batch, spatial, depth, suffix);
353     case DataLayout::kBatchDepthYX:
354       return absl::StrCat(batch, depth, spatial, suffix);
355     case DataLayout::kBatchDepthYX4:
356       return absl::StrCat(batch, depth, spatial, suffix, "(VECT_C)");
357     default:
358       LOG(FATAL) << "Unknown layout " << static_cast<int32>(layout());
359       return "";  // Avoid return warning (unreachable)
360   }
361 }
362 
NodesPerFeatureMap() const363 int64 BatchDescriptor::NodesPerFeatureMap() const {
364   int64 ret = 1;
365   for (int i = 0; i < ndims(); i++) {
366     ret *= spatial_size()[i];
367   }
368   return ret;
369 }
370 
NodesAcrossFeatureMaps() const371 int64 BatchDescriptor::NodesAcrossFeatureMaps() const {
372   return NodesPerFeatureMap() * feature_map_count();
373 }
374 
ElementCount() const375 int64 BatchDescriptor::ElementCount() const {
376   return count() * feature_map_count() * NodesPerFeatureMap();
377 }
378 
FullyConnectedWeightCount(const BatchDescriptor & input,const BatchDescriptor & output)379 int64 BatchDescriptor::FullyConnectedWeightCount(
380     const BatchDescriptor& input, const BatchDescriptor& output) {
381   return input.NodesAcrossFeatureMaps() * output.NodesAcrossFeatureMaps();
382 }
383 
FullyConnectedBiasCount(const BatchDescriptor & output)384 int64 BatchDescriptor::FullyConnectedBiasCount(const BatchDescriptor& output) {
385   return output.NodesAcrossFeatureMaps();
386 }
387 
DepthConcatenateOutputDescriptor(port::ArraySlice<dnn::BatchDescriptor> inputs)388 BatchDescriptor BatchDescriptor::DepthConcatenateOutputDescriptor(
389     port::ArraySlice<dnn::BatchDescriptor> inputs) {
390   if (inputs.empty()) {
391     return BatchDescriptor();
392   }
393   int feature_map_count = 0;
394   for (const auto& dimensions : inputs) {
395     feature_map_count += dimensions.feature_map_count();
396   }
397   BatchDescriptor output = inputs[0];
398   output.set_feature_map_count(feature_map_count);
399   return output;
400 }
401 
ToProto(DataType data_type) const402 TensorDescriptorProto BatchDescriptor::ToProto(DataType data_type) const {
403   CHECK_EQ(0.0, value_max_);
404   CHECK_EQ(0.0, value_min_);
405   CHECK(quantized_activation_mode_ == QuantizedActivationMode::k8Bit);
406 
407   TensorDescriptorProto ret = tensor_;
408   ret.set_data_type(data_type);
409   return ret;
410 }
411 
412 // -- FilterDescriptor
413 
FilterDescriptor(int ndims)414 FilterDescriptor::FilterDescriptor(int ndims) {
415   tensor_.mutable_dimensions()->Resize(ndims + 2, 0);
416   set_layout(FilterLayout::kOutputInputYX);
417 }
418 
FilterDescriptor()419 FilterDescriptor::FilterDescriptor() : FilterDescriptor(/*ndims=*/2) {}
420 
~FilterDescriptor()421 FilterDescriptor::~FilterDescriptor() {}
422 
CloneFrom(const FilterDescriptor & other)423 void FilterDescriptor::CloneFrom(const FilterDescriptor& other) {
424   tensor_ = other.tensor_;
425 }
426 
ToString() const427 std::string FilterDescriptor::ToString() const {
428   std::string desc = absl::StrFormat(
429       "{output_feature_map_count: %d input_feature_map_count: %d "
430       "layout: %s shape: ",
431       output_feature_map_count(), input_feature_map_count(),
432       FilterLayoutString(layout()));
433   for (int i = 0; i < ndims(); i++) {
434     absl::StrAppendFormat(&desc, "%d ", input_filter_dims()[i]);
435   }
436   absl::StrAppend(&desc, "}");
437 
438   return desc;
439 }
440 
ToShortString() const441 std::string FilterDescriptor::ToShortString() const {
442   // All the constituent strings are less than 15 characters, so the
443   // small string optimization ensures that there will be at most one
444   // heap memory allocation.
445   std::string od = absl::StrCat("od", output_feature_map_count());
446   std::string id = absl::StrCat("id", input_feature_map_count());
447 
448   std::string spatial = "s";
449   for (int i = 0; i < ndims(); i++) {
450     absl::StrAppendFormat(&spatial, "%d ", input_filter_dims()[i]);
451   }
452 
453   switch (layout()) {
454     case FilterLayout::kOutputInputYX:
455       return absl::StrCat(od, id, spatial);
456     case FilterLayout::kOutputYXInput:
457       return absl::StrCat(od, spatial, id);
458     case FilterLayout::kOutputInputYX4:
459       return absl::StrCat(od, id, spatial, "(VECT_C)");
460     case FilterLayout::kInputYXOutput:
461       return absl::StrCat(id, spatial, od);
462     case FilterLayout::kYXInputOutput:
463       return absl::StrCat(spatial, id, od);
464     default:
465       LOG(FATAL) << "Unknown layout " << static_cast<int32>(layout());
466       return "";  // Avoid return warning (unreachable)
467   }
468 }
469 
ComputeWeightCount() const470 int64 FilterDescriptor::ComputeWeightCount() const {
471   int64 ret = output_feature_map_count() * input_feature_map_count();
472   for (int i = 0; i < ndims(); i++) {
473     ret *= input_filter_dims()[i];
474   }
475   return ret;
476 }
477 
ToProto(DataType data_type) const478 TensorDescriptorProto FilterDescriptor::ToProto(DataType data_type) const {
479   TensorDescriptorProto ret = tensor_;
480   ret.set_data_type(data_type);
481   return ret;
482 }
483 
484 // -- ConvolutionDescriptor
485 
ConvolutionDescriptor(int ndims)486 ConvolutionDescriptor::ConvolutionDescriptor(int ndims) {
487   proto_.mutable_paddings()->Resize(ndims, 0);
488   proto_.mutable_strides()->Resize(ndims, 1);
489   proto_.mutable_dilations()->Resize(ndims, 1);
490   proto_.set_group_count(1);
491   proto_.set_convolution_mode(ConvolutionMode::CROSS_CORRELATION);
492 }
493 
ConvolutionDescriptor()494 ConvolutionDescriptor::ConvolutionDescriptor()
495     : ConvolutionDescriptor(/*ndims=*/2) {}
496 
~ConvolutionDescriptor()497 ConvolutionDescriptor::~ConvolutionDescriptor() {}
498 
ToString() const499 std::string ConvolutionDescriptor::ToString() const {
500   std::string padding;
501   std::string strides;
502   std::string dilations;
503   for (int i = 0; i < ndims(); i++) {
504     absl::StrAppendFormat(&padding, "%d ", this->padding()[i]);
505     absl::StrAppendFormat(&strides, "%d ", this->strides()[i]);
506     absl::StrAppendFormat(&dilations, "%d ", this->dilations()[i]);
507   }
508 
509   return absl::StrFormat(
510       "{zero_padding: %s pad_alignment: %s filter_strides: %s dilation_rates: "
511       "%s}",
512       padding, PadAlignmentString(pad_alignment()), strides, dilations);
513 }
514 
ToShortString() const515 std::string ConvolutionDescriptor::ToShortString() const {
516   std::string desc;
517   for (int i = 0; i < ndims(); i++) {
518     if (i > 0) absl::StrAppend(&desc, "_");
519     absl::StrAppendFormat(&desc, "p%d:%d", i, padding()[i]);
520   }
521   for (int i = 0; i < ndims(); i++) {
522     absl::StrAppendFormat(&desc, "_s%d:%d", i, strides()[i]);
523   }
524   for (int i = 0; i < ndims(); i++) {
525     absl::StrAppendFormat(&desc, "_d%d:%d", i, dilations()[i]);
526   }
527   return desc;
528 }
529 
530 // -- PoolingDescriptor
531 
PoolingDescriptor(int ndims)532 PoolingDescriptor::PoolingDescriptor(int ndims)
533     : mode_(dnn::PoolingMode::kMaximum),
534       ndims_(ndims),
535       propagate_nans_(false),
536       window_(ndims, 0),
537       padding_(ndims, 0),
538       strides_(ndims, 1) {}
539 
PoolingDescriptor()540 PoolingDescriptor::PoolingDescriptor() : PoolingDescriptor(/*ndims=*/2) {}
541 
CloneFrom(const PoolingDescriptor & other)542 void PoolingDescriptor::CloneFrom(const PoolingDescriptor& other) {
543   mode_ = other.mode_;
544   ndims_ = other.ndims_;
545   window_ = other.window_;
546   padding_ = other.padding_;
547   strides_ = other.strides_;
548   propagate_nans_ = other.propagate_nans_;
549 }
550 
ToString() const551 std::string PoolingDescriptor::ToString() const {
552   const char* mode_string =
553       mode_ == dnn::PoolingMode::kMaximum ? "kMaximum" : "kAverage";
554 
555   std::string window, strides, padding;
556   for (int i = 0; i < ndims_; i++) {
557     absl::StrAppendFormat(&window, "%d ", window_[i]);
558     absl::StrAppendFormat(&strides, "%d ", strides_[i]);
559     absl::StrAppendFormat(&padding, "%d", padding_[i]);
560   }
561 
562   const char* propagate_string = propagate_nans_ ? "Yes" : "No";
563 
564   return absl::StrFormat(
565       "{mode: %s window: %s strides: %s padding: %s propagate NaNs: %s}",
566       mode_string, window, strides, padding, propagate_string);
567 }
568 
ToShortString() const569 std::string PoolingDescriptor::ToShortString() const {
570   std::string window, strides, padding;
571   for (int i = 0; i < ndims_; i++) {
572     absl::StrAppendFormat(&window, "_w%d:%d", i, window_[i]);
573     absl::StrAppendFormat(&strides, "_s%d:%d", i, strides_[i]);
574     absl::StrAppendFormat(&padding, "_p%d:%d", i, padding_[i]);
575   }
576   return absl::StrCat(mode_ == dnn::PoolingMode::kMaximum ? "max" : "avg",
577                       window, strides, padding,
578                       propagate_nans_ ? "propagate_nans" : "ignore_nans");
579 }
580 
581 // -- NormalizeDescriptor
582 
NormalizeDescriptor()583 NormalizeDescriptor::NormalizeDescriptor()
584     : bias_(0.0),
585       range_(0),
586       alpha_(0.0),
587       beta_(0.0),
588       wrap_around_(false),
589       segment_size_(0) {}
590 
CloneFrom(const NormalizeDescriptor & other)591 void NormalizeDescriptor::CloneFrom(const NormalizeDescriptor& other) {
592   bias_ = other.bias_;
593   range_ = other.range_;
594   alpha_ = other.alpha_;
595   beta_ = other.beta_;
596   wrap_around_ = other.wrap_around_;
597   segment_size_ = other.segment_size_;
598 }
599 
ToString() const600 std::string NormalizeDescriptor::ToString() const {
601   return absl::StrFormat(
602       "{bias: %f range: %d alpha: %f beta: %f wrap_around: %d "
603       "segment_size: %d}",
604       bias_, range_, alpha_, beta_, wrap_around_, segment_size_);
605 }
606 
ToShortString() const607 std::string NormalizeDescriptor::ToShortString() const {
608   return absl::StrCat("bias:", bias_, "_range:", range_, "_alpha:", alpha_,
609                       "_beta:", beta_, "_wrap:", wrap_around_,
610                       "_size:", segment_size_);
611 }
612 
IsStatusOk(const port::Status & status,bool report_error)613 bool DnnSupport::IsStatusOk(const port::Status& status, bool report_error) {
614   if (status.ok()) {
615     return true;
616   }
617   if (report_error) {
618     LOG(ERROR) << status.error_message();
619   }
620   return false;
621 }
622 
DoCtcLoss(Stream * stream,dnn::DataType element_type,const RnnStateTensorDescriptor & probs_desc,const DeviceMemoryBase probs_data,absl::Span<const int> labels_data,absl::Span<const int> labels_lengths_data,absl::Span<const int> input_lengths_data,DeviceMemoryBase costs_data,const RnnStateTensorDescriptor & grads_desc,DeviceMemoryBase grads_data,DeviceMemory<uint8> scratch_memory,int ctc_loss_algo_id)623 port::Status DnnSupport::DoCtcLoss(
624     Stream* stream, dnn::DataType element_type,
625     const RnnStateTensorDescriptor& probs_desc,
626     const DeviceMemoryBase probs_data, absl::Span<const int> labels_data,
627     absl::Span<const int> labels_lengths_data,
628     absl::Span<const int> input_lengths_data, DeviceMemoryBase costs_data,
629     const RnnStateTensorDescriptor& grads_desc, DeviceMemoryBase grads_data,
630     DeviceMemory<uint8> scratch_memory, int ctc_loss_algo_id) {
631   return port::UnimplementedError("CtcLoss not implemented");
632 }
633 
634 }  // namespace dnn
635 }  // namespace stream_executor
636