STT-tensorflow/tensorflow/lite/micro/micro_allocator_test.cc
2020-06-12 10:49:57 +02:00

526 lines
22 KiB
C++

/* Copyright 2019 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/micro/micro_allocator.h"
#include <cstdint>
#include "tensorflow/lite/micro/simple_memory_allocator.h"
#include "tensorflow/lite/micro/test_helpers.h"
#include "tensorflow/lite/micro/testing/micro_test.h"
namespace tflite {
namespace testing {
namespace {
constexpr int kExpectedAlignment = 4;
void VerifyMockTensor(TfLiteTensor* tensor, bool is_variable = false) {
TF_LITE_MICRO_EXPECT_EQ(kTfLiteInt32, tensor->type);
TF_LITE_MICRO_EXPECT_EQ(1, tensor->dims->size);
TF_LITE_MICRO_EXPECT_EQ(1, tensor->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(is_variable, tensor->is_variable);
TF_LITE_MICRO_EXPECT_EQ(4, tensor->bytes);
TF_LITE_MICRO_EXPECT_NE(nullptr, tensor->data.raw);
TF_LITE_MICRO_EXPECT_EQ(0,
(reinterpret_cast<std::uintptr_t>(tensor->data.raw) %
kExpectedAlignment));
}
void VerifyMockWeightTensor(TfLiteTensor* tensor) {
TF_LITE_MICRO_EXPECT_EQ(kTfLiteUInt8, tensor->type);
TF_LITE_MICRO_EXPECT_EQ(1, tensor->dims->size);
TF_LITE_MICRO_EXPECT_EQ(1, tensor->dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(1, tensor->bytes);
TF_LITE_MICRO_EXPECT_NE(nullptr, tensor->data.raw);
}
void EnsureUniqueVariableTensorBuffer(TfLiteContext* context,
const int variable_tensor_idx) {
for (int i = 0; i < context->tensors_size; i++) {
if (i != variable_tensor_idx) {
TF_LITE_MICRO_EXPECT_NE(context->tensors[variable_tensor_idx].data.raw,
context->tensors[i].data.raw);
}
}
}
void VerifyRegistrationAndNodeAllocation(
NodeAndRegistration* node_and_registration, size_t count) {
for (size_t i = 0; i < count; i++) {
TF_LITE_MICRO_EXPECT_NE(nullptr, node_and_registration[i].registration);
TF_LITE_MICRO_EXPECT_NE(nullptr, node_and_registration[i].node.inputs);
TF_LITE_MICRO_EXPECT_NE(nullptr, node_and_registration[i].node.outputs);
}
}
} // namespace
} // namespace testing
} // namespace tflite
TF_LITE_MICRO_TESTS_BEGIN
TF_LITE_MICRO_TEST(TestInitializeRuntimeTensor) {
const tflite::Model* model = tflite::testing::GetSimpleMockModel();
TfLiteContext context;
constexpr size_t arena_size = 1024;
uint8_t arena[arena_size];
tflite::SimpleMemoryAllocator* simple_allocator =
tflite::SimpleMemoryAllocator::Create(micro_test::reporter, arena,
arena_size);
const tflite::Tensor* tensor = tflite::testing::Create1dFlatbufferTensor(100);
const flatbuffers::Vector<flatbuffers::Offset<tflite::Buffer>>* buffers =
tflite::testing::CreateFlatbufferBuffers();
TfLiteTensor allocated_tensor;
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, tflite::internal::InitializeTfLiteTensorFromFlatbuffer(
simple_allocator, *tensor, buffers, micro_test::reporter,
&allocated_tensor));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteInt32, allocated_tensor.type);
TF_LITE_MICRO_EXPECT_EQ(1, allocated_tensor.dims->size);
TF_LITE_MICRO_EXPECT_EQ(100, allocated_tensor.dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(400, allocated_tensor.bytes);
TF_LITE_MICRO_EXPECT_EQ(nullptr, allocated_tensor.data.i32);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteArenaRw, allocated_tensor.allocation_type);
simple_allocator->~SimpleMemoryAllocator();
}
TF_LITE_MICRO_TEST(TestInitializeQuantizedTensor) {
const tflite::Model* model = tflite::testing::GetSimpleMockModel();
TfLiteContext context;
constexpr size_t arena_size = 1024;
uint8_t arena[arena_size];
tflite::SimpleMemoryAllocator* simple_allocator =
tflite::SimpleMemoryAllocator::Create(micro_test::reporter, arena,
arena_size);
const tflite::Tensor* tensor =
tflite::testing::CreateQuantizedFlatbufferTensor(100);
const flatbuffers::Vector<flatbuffers::Offset<tflite::Buffer>>* buffers =
tflite::testing::CreateFlatbufferBuffers();
TfLiteTensor allocated_tensor;
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, tflite::internal::InitializeTfLiteTensorFromFlatbuffer(
simple_allocator, *tensor, buffers, micro_test::reporter,
&allocated_tensor));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteInt32, allocated_tensor.type);
TF_LITE_MICRO_EXPECT_EQ(1, allocated_tensor.dims->size);
TF_LITE_MICRO_EXPECT_EQ(100, allocated_tensor.dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(400, allocated_tensor.bytes);
TF_LITE_MICRO_EXPECT_EQ(nullptr, allocated_tensor.data.i32);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteArenaRw, allocated_tensor.allocation_type);
simple_allocator->~SimpleMemoryAllocator();
}
TF_LITE_MICRO_TEST(TestMissingQuantization) {
const tflite::Model* model = tflite::testing::GetSimpleMockModel();
TfLiteContext context;
constexpr size_t arena_size = 1024;
uint8_t arena[arena_size];
tflite::SimpleMemoryAllocator* simple_allocator =
tflite::SimpleMemoryAllocator::Create(micro_test::reporter, arena,
arena_size);
const tflite::Tensor* tensor =
tflite::testing::CreateMissingQuantizationFlatbufferTensor(100);
const flatbuffers::Vector<flatbuffers::Offset<tflite::Buffer>>* buffers =
tflite::testing::CreateFlatbufferBuffers();
TfLiteTensor allocated_tensor;
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, tflite::internal::InitializeTfLiteTensorFromFlatbuffer(
simple_allocator, *tensor, buffers, micro_test::reporter,
&allocated_tensor));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteInt32, allocated_tensor.type);
TF_LITE_MICRO_EXPECT_EQ(1, allocated_tensor.dims->size);
TF_LITE_MICRO_EXPECT_EQ(100, allocated_tensor.dims->data[0]);
TF_LITE_MICRO_EXPECT_EQ(400, allocated_tensor.bytes);
TF_LITE_MICRO_EXPECT_EQ(nullptr, allocated_tensor.data.i32);
}
TF_LITE_MICRO_TEST(TestFailsWhenModelStartsTwice) {
const tflite::Model* model = tflite::testing::GetSimpleMockModel();
TfLiteContext context;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
constexpr size_t arena_size = 1024;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_NE(nullptr, allocator);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteError, allocator->StartModelAllocation(
model, &context, mock_resolver,
&node_and_registration));
}
TF_LITE_MICRO_TEST(TestFailsWhenModelFinishesBeforeStart) {
const tflite::Model* model = tflite::testing::GetSimpleMockModel();
TfLiteContext context;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
constexpr size_t arena_size = 1024;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_NE(nullptr, allocator);
TF_LITE_MICRO_EXPECT_EQ(kTfLiteError,
allocator->FinishModelAllocation(model, &context));
}
TF_LITE_MICRO_TEST(TestMockModelAllocation) {
const tflite::Model* model = tflite::testing::GetSimpleMockModel();
TfLiteContext context;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
constexpr size_t arena_size = 1024;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_NE(nullptr, allocator);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk,
allocator->FinishModelAllocation(model, &context));
TF_LITE_MICRO_EXPECT_EQ(4, context.tensors_size);
// NOTE: Tensor indexes match the values in GetSimpleMockModel().
tflite::testing::VerifyMockTensor(&context.tensors[0]);
tflite::testing::VerifyMockWeightTensor(&context.tensors[1]);
tflite::testing::VerifyMockTensor(&context.tensors[2]);
tflite::testing::VerifyMockTensor(&context.tensors[3]);
TF_LITE_MICRO_EXPECT_NE(context.tensors[1].data.raw,
context.tensors[0].data.raw);
TF_LITE_MICRO_EXPECT_NE(context.tensors[2].data.raw,
context.tensors[0].data.raw);
TF_LITE_MICRO_EXPECT_NE(context.tensors[1].data.raw,
context.tensors[2].data.raw);
TF_LITE_MICRO_EXPECT_NE(context.tensors[3].data.raw,
context.tensors[0].data.raw);
TF_LITE_MICRO_EXPECT_NE(context.tensors[3].data.raw,
context.tensors[1].data.raw);
TF_LITE_MICRO_EXPECT_NE(context.tensors[3].data.raw,
context.tensors[2].data.raw);
TF_LITE_MICRO_EXPECT_LE(allocator->used_bytes(), 760 + 100);
// SimpleMockModel has 2 operators:
tflite::testing::VerifyRegistrationAndNodeAllocation(node_and_registration,
/*count=*/2);
}
TF_LITE_MICRO_TEST(TestAllocationForModelsWithBranches) {
const tflite::Model* model = tflite::testing::GetSimpleModelWithBranch();
TfLiteContext context;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
constexpr size_t arena_size = 4096;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_NE(nullptr, allocator);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk,
allocator->FinishModelAllocation(model, &context));
uint8_t* start = context.tensors[0].data.uint8;
// Check test_helpers.cc BuildSimpleModelWithBranch for model structure.
// t0 is the first tensor, so place it in offset 0.
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[0].data.uint8 - start);
// bytes = 2 * 2 * 3 * sizeof(float32) = 48, same for other tensors.
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[0].bytes);
// t1 can't reuse any memory, as n0 requires both t0 and t1.
TF_LITE_MICRO_EXPECT_EQ(96, context.tensors[1].data.uint8 - start);
// t2 can't reuse any memory, as n1 requires both t0 and t2. Also n2 requires
// both t1 and t2.
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[2].data.uint8 - start);
// t3 reuses the same memory from t0 as t0 is not an input to any node.
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[3].data.uint8 - start);
// SimpleModelWithBranch has 3 operators:
tflite::testing::VerifyRegistrationAndNodeAllocation(node_and_registration,
/*count=*/3);
}
TF_LITE_MICRO_TEST(TestAllocationForComplexModelAllocation) {
const tflite::Model* model = tflite::testing::GetComplexMockModel();
TfLiteContext context;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
constexpr size_t arena_size = 2048;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_NE(nullptr, allocator);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk,
allocator->FinishModelAllocation(model, &context));
TF_LITE_MICRO_EXPECT_EQ(10, context.tensors_size);
// NOTE: Tensor indexes match the values in GetComplexMockModel().
tflite::testing::VerifyMockTensor(&context.tensors[0]);
tflite::testing::VerifyMockTensor(&context.tensors[1],
true /* is_variable */);
tflite::testing::VerifyMockWeightTensor(&context.tensors[2]);
tflite::testing::VerifyMockTensor(&context.tensors[3]);
tflite::testing::VerifyMockTensor(&context.tensors[4],
true /* is_variable */);
tflite::testing::VerifyMockWeightTensor(&context.tensors[5]);
tflite::testing::VerifyMockTensor(&context.tensors[6]);
tflite::testing::VerifyMockTensor(&context.tensors[7],
true /* is_variable */);
tflite::testing::VerifyMockWeightTensor(&context.tensors[8]);
tflite::testing::VerifyMockTensor(&context.tensors[9]);
// Ensure that variable tensors have unique address
tflite::testing::EnsureUniqueVariableTensorBuffer(&context, 1);
tflite::testing::EnsureUniqueVariableTensorBuffer(&context, 4);
tflite::testing::EnsureUniqueVariableTensorBuffer(&context, 7);
// ComplexMockModel has 3 operators:
tflite::testing::VerifyRegistrationAndNodeAllocation(node_and_registration,
/*count=*/3);
}
TF_LITE_MICRO_TEST(OfflinePlannerBranchesAllOnline) {
int version = 1;
int subgraph = 0;
constexpr int nbr_tensors = 4;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
const int32_t metadata_buffer[tflite::testing::kOfflinePlannerHeaderSize +
nbr_tensors] = {version, subgraph,
nbr_tensors, // header
// memory offsets:
-1, -1, -1, -1};
// The structure is identical to the one in
// TestAllocationForModelsWithBranches
int num_conns = 3;
tflite::testing::NodeConnection node_list[3] = {{
{0}, // input
{1} // output
},
{
{0}, // input
{2} // output
},
{
{1, 2}, // input1, input2
{3} // output
}};
const tflite::Model* model = tflite::testing::GetModelWithOfflinePlanning(
nbr_tensors, metadata_buffer, node_list, num_conns);
TfLiteContext context;
constexpr size_t arena_size = 4096;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk,
allocator->FinishModelAllocation(model, &context));
// Since all of the tensors are online planned and the model structure is
// identical to that in TestAllocationForModelsWithBranches,
// the offsets be should identical to that test.
uint8_t* start = context.tensors[0].data.uint8;
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[0].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[0].bytes);
TF_LITE_MICRO_EXPECT_EQ(96, context.tensors[1].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[2].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[3].data.uint8 - start);
}
TF_LITE_MICRO_TEST(OfflinePlannerBasic) {
constexpr int nbr_tensors = 4;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
const int32_t metadata_buffer[tflite::testing::kOfflinePlannerHeaderSize +
nbr_tensors] = {1, 0, nbr_tensors,
0, // t0
48, // t1
0, // t2
48}; // t3
int t0 = 0;
int t1 = 1;
int t2 = 2;
int t3 = 3;
int num_conns = 3;
tflite::testing::NodeConnection node_list[3] = {{
{t0}, // input
{t1} // output
},
{
{t1}, // input
{t2} // output
},
{
{t2}, // input
{t3} // output
}};
const tflite::Model* model = tflite::testing::GetModelWithOfflinePlanning(
nbr_tensors, metadata_buffer, node_list, num_conns);
TfLiteContext context;
constexpr size_t arena_size = 4096;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk,
allocator->FinishModelAllocation(model, &context));
uint8_t* start = context.tensors[0].data.uint8;
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[0].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[1].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[2].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[3].data.uint8 - start);
}
TF_LITE_MICRO_TEST(OfflinePlannerOverlappingAllocation) {
constexpr int nbr_tensors = 4;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
const int32_t metadata_buffer[tflite::testing::kOfflinePlannerHeaderSize +
nbr_tensors] = {
1, 0, nbr_tensors, // header: version, subgraph, nbr tensors
// memory offsets:
0, // t0
0, // t1
48, // t2
-1}; // t3
int t0 = 0;
int t1 = 1;
int t2 = 2;
int t3 = 3;
int num_conns = 2;
tflite::testing::NodeConnection node_list[2] = {
{
{t0, t1}, // input, scratch
{t2} // output
},
{
{t2}, // input
{t3} // output
},
};
const tflite::Model* model = tflite::testing::GetModelWithOfflinePlanning(
nbr_tensors, metadata_buffer, node_list, num_conns);
TfLiteContext context;
constexpr size_t arena_size = 4096;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk,
allocator->FinishModelAllocation(model, &context));
uint8_t* start = context.tensors[0].data.uint8;
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[0].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[1].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[2].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[3].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[0].bytes);
}
TF_LITE_MICRO_TEST(OfflinePlannerOfflineOnline) {
constexpr int nbr_tensors = 5;
tflite::testing::MockOpResolver mock_resolver;
tflite::NodeAndRegistration* node_and_registration;
const int32_t metadata_buffer[tflite::testing::kOfflinePlannerHeaderSize +
nbr_tensors] = {
1, 0, nbr_tensors, // header: version, subgraph, nbr tensors
// memory offsets:
0, // t0
48, // t1
-1, // t2
0, // t3
-1}; // t4
int t0 = 0;
int t1 = 1;
int t2 = 2;
int t3 = 3;
int t4 = 4;
int num_conns = 2;
tflite::testing::NodeConnection node_list[2] = {
{
{t0, t1}, // input, scratch
{t2}, // output
},
{
{t2}, // input
{t3, t4}, // output1, output2
},
};
const tflite::Model* model = tflite::testing::GetModelWithOfflinePlanning(
nbr_tensors, metadata_buffer, node_list, num_conns);
TfLiteContext context;
constexpr size_t arena_size = 4096;
uint8_t arena[arena_size];
tflite::MicroAllocator* allocator =
tflite::MicroAllocator::Create(arena, arena_size, micro_test::reporter);
TF_LITE_MICRO_EXPECT_EQ(
kTfLiteOk, allocator->StartModelAllocation(model, &context, mock_resolver,
&node_and_registration));
TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk,
allocator->FinishModelAllocation(model, &context));
uint8_t* start = context.tensors[0].data.uint8;
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[0].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[1].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(96, context.tensors[2].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(48, context.tensors[4].data.uint8 - start);
TF_LITE_MICRO_EXPECT_EQ(0, context.tensors[3].data.uint8 - start);
}
TF_LITE_MICRO_TESTS_END