Add endian-aware flatbuffer conversions to MicroAllocator.
Currently, MicroAllocator manually maps TfLite*Array struct values directly to flatbuffer values. This change cleans up other instances inside MicroAllocator that are not endian-aware. This works only on little-endian (LE) architecture systems because of the layout of TfLite*Array: struct TfLiteIntArray { int size; int data[]; } The compiler maintains mapping, but |size| and |data| are laid out as the following in LE: [lowest-order-byte(e.g. data) .... highest-order-byte(e.g. size)] Casting and remapping work on LE because the vector is written in the lowest-order-byte sequence. On BE systems, this memory savings trick does not work and requires a malloc from the arena and manual copying of values from the flatbuffer. PiperOrigin-RevId: 317730072 Change-Id: I1baff898356e3d82b2faed6468a50ae44acd3082
This commit is contained in:
parent
28582fb549
commit
a3dc11ea13
@ -392,28 +392,48 @@ TfLiteStatus CommitPlan(ErrorReporter* error_reporter, MemoryPlanner* planner,
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
// Allocate a TfLiteIntArray and copy the contents of a FlatBuffers Vector
|
// Handles architecture safe mapping of flatbuffer vectors to a TfLite*Array
|
||||||
// into it.
|
// struct. Matching types are required (e.g. float and TfLiteFloatArray).
|
||||||
template <class T>
|
template <typename kFlatBufferVectorType, typename kTfLiteArrayType>
|
||||||
TfLiteStatus FlatBufferIntArrayToTfLiteIntArray(
|
TfLiteStatus FlatBufferVectorToTfLiteTypeArray(
|
||||||
SimpleMemoryAllocator* allocator, ErrorReporter* error_reporter,
|
SimpleMemoryAllocator* allocator, ErrorReporter* error_reporter,
|
||||||
const flatbuffers::Vector<T>* flat_array, TfLiteIntArray** result) {
|
const flatbuffers::Vector<kFlatBufferVectorType>* flatbuffer_array,
|
||||||
TfLiteIntArray* ret =
|
kTfLiteArrayType** result) {
|
||||||
reinterpret_cast<TfLiteIntArray*>(allocator->AllocateFromTail(
|
TFLITE_DCHECK(error_reporter != nullptr);
|
||||||
TfLiteIntArrayGetSizeInBytes(flat_array->Length()),
|
TFLITE_DCHECK(flatbuffer_array != nullptr);
|
||||||
alignof(TfLiteIntArray)));
|
// Only two conversions are supported - float and int32 - ensure that these
|
||||||
if (nullptr == ret) {
|
// match at compile time instead of duplicating functions here:
|
||||||
TF_LITE_REPORT_ERROR(
|
static_assert((std::is_same<kFlatBufferVectorType, int32_t>() &&
|
||||||
error_reporter,
|
std::is_same<kTfLiteArrayType, TfLiteIntArray>()) ||
|
||||||
"Failed to allocate %d bytes of memory to copy an array.",
|
(std::is_same<kFlatBufferVectorType, float>() &&
|
||||||
TfLiteIntArrayGetSizeInBytes(flat_array->Length()));
|
std::is_same<kTfLiteArrayType, TfLiteFloatArray>()));
|
||||||
return kTfLiteError;
|
if (FLATBUFFERS_LITTLEENDIAN) {
|
||||||
|
// On little-endian machines, TfLite*Array happens to have the same memory
|
||||||
|
// layout as flatbuffers:Vector<kFlatBufferVectorType>, so we can
|
||||||
|
// reinterpret_cast the flatbuffer vector and avoid a copy and malloc.
|
||||||
|
*result = const_cast<kTfLiteArrayType*>(
|
||||||
|
reinterpret_cast<const kTfLiteArrayType*>(flatbuffer_array));
|
||||||
|
} else {
|
||||||
|
// Big-endian architecture can not use the same memory layout as
|
||||||
|
// flatbuffers::Vector<kFlatBufferVectorType>. Allocate from the tail and
|
||||||
|
// copy values from the flatbuffer into the newly allocated chunk.
|
||||||
|
kTfLiteArrayType* array =
|
||||||
|
reinterpret_cast<kTfLiteArrayType*>(allocator->AllocateFromTail(
|
||||||
|
TfLiteIntArrayGetSizeInBytes(flatbuffer_array->Length()),
|
||||||
|
alignof(kTfLiteArrayType)));
|
||||||
|
if (array == nullptr) {
|
||||||
|
TF_LITE_REPORT_ERROR(
|
||||||
|
error_reporter,
|
||||||
|
"Failed to allocate %d bytes of memory to copy an array.",
|
||||||
|
TfLiteIntArrayGetSizeInBytes(flatbuffer_array->Length()));
|
||||||
|
return kTfLiteError;
|
||||||
|
}
|
||||||
|
array->size = flatbuffer_array->Length();
|
||||||
|
for (int i = 0; i < array->size; ++i) {
|
||||||
|
array->data[i] = flatbuffer_array->Get(i);
|
||||||
|
}
|
||||||
|
*result = array;
|
||||||
}
|
}
|
||||||
ret->size = flat_array->Length();
|
|
||||||
for (int64_t i = 0; i < static_cast<int64_t>(flat_array->Length()); i++) {
|
|
||||||
ret->data[i] = flat_array->Get(i);
|
|
||||||
}
|
|
||||||
*result = ret;
|
|
||||||
return kTfLiteOk;
|
return kTfLiteOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,27 +489,17 @@ TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
|
|||||||
TF_LITE_ENSURE_STATUS(BytesRequiredForTensor(
|
TF_LITE_ENSURE_STATUS(BytesRequiredForTensor(
|
||||||
flatbuffer_tensor, &result->bytes, &type_size, error_reporter));
|
flatbuffer_tensor, &result->bytes, &type_size, error_reporter));
|
||||||
|
|
||||||
// TODO(b/159043126): Cleanup endian casting by doing all endian casting in
|
|
||||||
// one spot:
|
|
||||||
if (flatbuffer_tensor.shape() == nullptr) {
|
if (flatbuffer_tensor.shape() == nullptr) {
|
||||||
// flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
|
// flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
|
||||||
// tensor.
|
// tensor.
|
||||||
result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
|
result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
|
||||||
} else if (!FLATBUFFERS_LITTLEENDIAN) {
|
|
||||||
// Big-endian architecture. Copy and byte-swap the little-endian shape
|
|
||||||
// data.
|
|
||||||
TF_LITE_ENSURE_STATUS(FlatBufferIntArrayToTfLiteIntArray(
|
|
||||||
allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims)));
|
|
||||||
} else {
|
} else {
|
||||||
// On little-endian machines, TfLiteIntArray happens to have the same
|
|
||||||
// memory layout as flatbuffers:Vector<int>, so we can reinterpret_cast the
|
|
||||||
// tensor shape vector and avoid a copy.
|
|
||||||
// TFLM doesn't allow reshaping the tensor which requires dynamic memory
|
// TFLM doesn't allow reshaping the tensor which requires dynamic memory
|
||||||
// allocation so it is safe to drop the const qualifier. In the future, if
|
// allocation so it is safe to drop the const qualifier. In the future, if
|
||||||
// we really want to update the tensor shape, we can always pass in a new
|
// we really want to update the tensor shape, we can always pass in a new
|
||||||
// TfLiteIntArray - especially we have to do so if the dimension is changed.
|
// TfLiteIntArray - especially we have to do so if the dimension is
|
||||||
result->dims = const_cast<TfLiteIntArray*>(
|
TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
|
||||||
reinterpret_cast<const TfLiteIntArray*>(flatbuffer_tensor.shape()));
|
allocator, error_reporter, flatbuffer_tensor.shape(), &(result->dims)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the quantization information from the serialized data.
|
// Copy the quantization information from the serialized data.
|
||||||
@ -531,9 +541,9 @@ TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
|
|||||||
return kTfLiteError;
|
return kTfLiteError;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/159043126): Check for big endian before casting flatbuffer values.
|
TF_LITE_ENSURE_STATUS(FlatBufferVectorToTfLiteTypeArray(
|
||||||
quantization->scale = const_cast<TfLiteFloatArray*>(
|
allocator, error_reporter, src_quantization->scale(),
|
||||||
reinterpret_cast<const TfLiteFloatArray*>(src_quantization->scale()));
|
&quantization->scale));
|
||||||
|
|
||||||
quantization->zero_point->size = channels;
|
quantization->zero_point->size = channels;
|
||||||
int* zero_point_data = quantization->zero_point->data;
|
int* zero_point_data = quantization->zero_point->data;
|
||||||
@ -815,13 +825,13 @@ TfLiteStatus MicroAllocator::PrepareNodeAndRegistrationDataFromFlatbuffer(
|
|||||||
(void**)(&builtin_data)));
|
(void**)(&builtin_data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disregard const qualifier to workaround with existing API.
|
TfLiteIntArray* inputs_array;
|
||||||
// TODO(b/159043126): Check for big endian before casting flatbuffer values.
|
TF_LITE_ENSURE_STATUS(internal::FlatBufferVectorToTfLiteTypeArray(
|
||||||
TfLiteIntArray* inputs_array = const_cast<TfLiteIntArray*>(
|
memory_allocator_, error_reporter_, op->inputs(), &inputs_array));
|
||||||
reinterpret_cast<const TfLiteIntArray*>(op->inputs()));
|
|
||||||
// TODO(b/159043126): Check for big endian before casting flatbuffer values.
|
TfLiteIntArray* outputs_array;
|
||||||
TfLiteIntArray* outputs_array = const_cast<TfLiteIntArray*>(
|
TF_LITE_ENSURE_STATUS(internal::FlatBufferVectorToTfLiteTypeArray(
|
||||||
reinterpret_cast<const TfLiteIntArray*>(op->outputs()));
|
memory_allocator_, error_reporter_, op->outputs(), &outputs_array));
|
||||||
|
|
||||||
TfLiteNode* node = &(node_and_registrations[i].node);
|
TfLiteNode* node = &(node_and_registrations[i].node);
|
||||||
*node = {};
|
*node = {};
|
||||||
|
Loading…
Reference in New Issue
Block a user