Quantize, dequantize optimizations for HiFi targets (#2544)
BUG=none
diff --git a/tensorflow/lite/micro/kernels/xtensa/dequantize.cc b/tensorflow/lite/micro/kernels/xtensa/dequantize.cc
new file mode 100644
index 0000000..6d731bf
--- /dev/null
+++ b/tensorflow/lite/micro/kernels/xtensa/dequantize.cc
@@ -0,0 +1,117 @@
+/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include "tensorflow/lite/kernels/internal/reference/dequantize.h"
+
+#include "tensorflow/lite/c/builtin_op_data.h"
+#include "tensorflow/lite/c/common.h"
+#include "tensorflow/lite/kernels/internal/quantization_util.h"
+#include "tensorflow/lite/kernels/internal/reference/quantize.h"
+#include "tensorflow/lite/kernels/internal/reference/requantize.h"
+#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
+#include "tensorflow/lite/kernels/kernel_util.h"
+#include "tensorflow/lite/micro/kernels/dequantize.h"
+#include "tensorflow/lite/micro/kernels/kernel_util.h"
+#include "tensorflow/lite/micro/micro_log.h"
+
+namespace tflite {
+
+void* DequantizeInit(TfLiteContext* context, const char* buffer,
+ size_t length) {
+ TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
+ return context->AllocatePersistentBuffer(context, sizeof(DequantizeOpData));
+}
+
+TfLiteStatus DequantizeEval(TfLiteContext* context, TfLiteNode* node) {
+ TFLITE_DCHECK(node->user_data != nullptr);
+ DequantizeOpData* data = static_cast<DequantizeOpData*>(node->user_data);
+
+ const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
+ TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
+
+ // Output type ensured to be kTfLiteFloat32 at the Prepare stage
+ TFLITE_DCHECK(output->type == kTfLiteFloat32);
+
+ switch (input->type) {
+ case kTfLiteInt8: {
+#if HIFI_VFPU && (defined(HIFI5) || defined(HIFI4))
+ int err;
+ const int8_t* input_data_ptr;
+ float* output_data_ptr;
+ const int flat_size =
+ MatchingFlatSize(tflite::micro::GetTensorShape(input),
+ tflite::micro::GetTensorShape(output));
+ input_data_ptr = tflite::micro::GetTensorData<int8_t>(input);
+ output_data_ptr = tflite::micro::GetTensorData<float>(output);
+
+ err = xa_nn_elm_dequantize_asym8s_f32(
+ output_data_ptr, input_data_ptr, data->quantization_params.zero_point,
+ data->quantization_params.scale, flat_size);
+ TF_LITE_ENSURE(context, (err == 0));
+#else // HIFI_VFPU && (defined(HIFI5) || defined(HIFI4))
+ reference_ops::Dequantize(data->quantization_params,
+ tflite::micro::GetTensorShape(input),
+ tflite::micro::GetTensorData<int8_t>(input),
+ tflite::micro::GetTensorShape(output),
+ tflite::micro::GetTensorData<float>(output));
+#endif // HIFI_VFPU && (defined(HIFI5) || defined(HIFI4))
+ break;
+ }
+ case kTfLiteInt16: {
+#if HIFI_VFPU && (defined(HIFI5) || defined(HIFI4))
+ int err;
+ const int16_t* input_data_ptr;
+ float* output_data_ptr;
+ const RuntimeShape& input_shape = tflite::micro::GetTensorShape(input);
+ const RuntimeShape& output_shape = tflite::micro::GetTensorShape(output);
+ const int flat_size = MatchingFlatSize(input_shape, output_shape);
+ input_data_ptr = tflite::micro::GetTensorData<int16_t>(input);
+ output_data_ptr = tflite::micro::GetTensorData<float>(output);
+ err = xa_nn_elm_dequantize_asym16s_f32(
+ output_data_ptr, input_data_ptr, data->quantization_params.zero_point,
+ data->quantization_params.scale, flat_size);
+ TF_LITE_ENSURE(context, (err == 0));
+#else // HIFI_VFPU && (defined(HIFI5) || defined(HIFI4))
+ reference_ops::Dequantize(data->quantization_params,
+ tflite::micro::GetTensorShape(input),
+ tflite::micro::GetTensorData<int16_t>(input),
+ tflite::micro::GetTensorShape(output),
+ tflite::micro::GetTensorData<float>(output));
+#endif // HIFI_VFPU && (defined(HIFI5) || defined(HIFI4))
+ break;
+ }
+ case kTfLiteUInt8:
+ reference_ops::Dequantize(data->quantization_params,
+ tflite::micro::GetTensorShape(input),
+ tflite::micro::GetTensorData<uint8_t>(input),
+ tflite::micro::GetTensorShape(output),
+ tflite::micro::GetTensorData<float>(output));
+ break;
+ default:
+ MicroPrintf("Input %s, output %s not supported.",
+ TfLiteTypeGetName(input->type),
+ TfLiteTypeGetName(output->type));
+ return kTfLiteError;
+ }
+
+ return kTfLiteOk;
+}
+
+TFLMRegistration Register_DEQUANTIZE() {
+ return tflite::micro::RegisterOp(DequantizeInit, DequantizePrepare,
+ DequantizeEval);
+}
+
+} // namespace tflite
diff --git a/tensorflow/lite/micro/kernels/xtensa/quantize.cc b/tensorflow/lite/micro/kernels/xtensa/quantize.cc
index 15f5243..06d4fbb 100644
--- a/tensorflow/lite/micro/kernels/xtensa/quantize.cc
+++ b/tensorflow/lite/micro/kernels/xtensa/quantize.cc
@@ -75,12 +75,19 @@
case kTfLiteInt8: {
int size = ElementCount(*input->dims);
- reference_ops::Requantize(
- tflite::micro::GetTensorData<int8_t>(input), size,
- op_data->requantize_output_multiplier,
- op_data->requantize_output_shift, op_data->input_zero_point,
- op_data->quantization_params.zero_point,
- tflite::micro::GetTensorData<int8_t>(output));
+ int32_t zero_point = op_data->quantization_params.zero_point;
+ const int8_t* input_data_ptr;
+ int8_t* output_data_ptr;
+ input_data_ptr = tflite::micro::GetTensorData<int8_t>(input);
+ output_data_ptr = tflite::micro::GetTensorData<int8_t>(output);
+
+ TF_LITE_ENSURE_EQ(
+ context,
+ xa_nn_elm_requantize_asym8s_asym8s(
+ output_data_ptr, input_data_ptr, op_data->input_zero_point,
+ zero_point, op_data->requantize_output_shift,
+ op_data->requantize_output_multiplier, size),
+ 0);
break;
}
@@ -98,7 +105,6 @@
case kTfLiteInt32: {
int size = ElementCount(*input->dims);
int32_t zero_point = op_data->quantization_params.zero_point;
-#if defined(HIFI5)
const int8_t* input_data_ptr;
int32_t* output_data_ptr;
input_data_ptr = tflite::micro::GetTensorData<int8_t>(input);
@@ -111,13 +117,6 @@
zero_point, op_data->requantize_output_shift,
op_data->requantize_output_multiplier, size),
0);
-#else
- reference_ops::Requantize(
- tflite::micro::GetTensorData<int8_t>(input), size,
- op_data->requantize_output_multiplier,
- op_data->requantize_output_shift, op_data->input_zero_point,
- zero_point, tflite::micro::GetTensorData<int32_t>(output));
-#endif // defined(HIFI5)
break;
}
@@ -149,18 +148,20 @@
case kTfLiteInt16: {
int size = ElementCount(*input->dims);
- reference_ops::Requantize(
- tflite::micro::GetTensorData<int16_t>(input), size,
- op_data->requantize_output_multiplier,
- op_data->requantize_output_shift, op_data->input_zero_point,
- op_data->quantization_params.zero_point,
- tflite::micro::GetTensorData<int16_t>(output));
+ TF_LITE_ENSURE_EQ(context,
+ xa_nn_elm_requantize_asym16s_asym16s(
+ tflite::micro::GetTensorData<int16_t>(output),
+ tflite::micro::GetTensorData<int16_t>(input),
+ op_data->input_zero_point,
+ op_data->quantization_params.zero_point,
+ op_data->requantize_output_shift,
+ op_data->requantize_output_multiplier, size),
+ 0);
break;
}
case kTfLiteInt32: {
int size = ElementCount(*input->dims);
-#if defined(HIFI5)
TF_LITE_ENSURE_EQ(context,
xa_nn_elm_requantize_asym16s_asym32s(
tflite::micro::GetTensorData<int32_t>(output),
@@ -170,14 +171,6 @@
op_data->requantize_output_shift,
op_data->requantize_output_multiplier, size),
0);
-#else
- int32_t zero_point = op_data->quantization_params.zero_point;
- reference_ops::Requantize(
- tflite::micro::GetTensorData<int16_t>(input), size,
- op_data->requantize_output_multiplier,
- op_data->requantize_output_shift, op_data->input_zero_point,
- zero_point, tflite::micro::GetTensorData<int32_t>(output));
-#endif // defined(HIFI5)
break;
}
@@ -228,22 +221,56 @@
case kTfLiteFloat32: {
switch (output->type) {
case kTfLiteInt8: {
+#if HIFI_VFPU
+ int size = ElementCount(*input->dims);
+ int32_t zero_point = op_data->quantization_params.zero_point;
+ const float* input_data_ptr;
+ int8_t* output_data_ptr;
+ input_data_ptr = tflite::micro::GetTensorData<float>(input);
+ output_data_ptr = tflite::micro::GetTensorData<int8_t>(output);
+
+ TF_LITE_ENSURE_EQ(
+ context,
+ xa_nn_elm_quantize_f32_asym8s(
+ output_data_ptr, input_data_ptr,
+ static_cast<float>(op_data->quantization_params.scale),
+ zero_point, size),
+ 0);
+#else // #if HIFI_VFPU
reference_ops::AffineQuantize(
op_data->quantization_params,
tflite::micro::GetTensorShape(input),
tflite::micro::GetTensorData<float>(input),
tflite::micro::GetTensorShape(output),
tflite::micro::GetTensorData<int8_t>(output));
+#endif // #if HIFI_VFPU
break;
}
case kTfLiteInt16: {
+#if HIFI_VFPU
+ int size = ElementCount(*input->dims);
+ int32_t zero_point = op_data->quantization_params.zero_point;
+ const float* input_data_ptr;
+ int16_t* output_data_ptr;
+ input_data_ptr = tflite::micro::GetTensorData<float>(input);
+ output_data_ptr = tflite::micro::GetTensorData<int16_t>(output);
+
+ TF_LITE_ENSURE_EQ(
+ context,
+ xa_nn_elm_quantize_f32_asym16s(
+ output_data_ptr, input_data_ptr,
+ static_cast<float>(op_data->quantization_params.scale),
+ zero_point, size),
+ 0);
+#else // #if HIFI_VFPU
reference_ops::AffineQuantize(
op_data->quantization_params,
tflite::micro::GetTensorShape(input),
tflite::micro::GetTensorData<float>(input),
tflite::micro::GetTensorShape(output),
tflite::micro::GetTensorData<int16_t>(output));
+#endif // #if HIFI_VFPU
break;
}