aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/contrib/lite
diff options
context:
space:
mode:
authorGravatar A. Unique TensorFlower <gardener@tensorflow.org>2018-06-12 14:09:31 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2018-06-12 14:14:02 -0700
commit85c518b8d306204cd7111f321a4b7b204fc554f4 (patch)
tree4bccd9f59fbec5c18ea8036265f289b5064dfc24 /tensorflow/contrib/lite
parent52af244989e4eb0505943023014c0a06610a32c9 (diff)
Handle zero-sized TFLite tensor allocations
PiperOrigin-RevId: 200277562
Diffstat (limited to 'tensorflow/contrib/lite')
-rw-r--r--tensorflow/contrib/lite/arena_planner_test.cc7
-rw-r--r--tensorflow/contrib/lite/interpreter_test.cc15
-rw-r--r--tensorflow/contrib/lite/simple_memory_arena.cc16
-rw-r--r--tensorflow/contrib/lite/simple_memory_arena.h3
-rw-r--r--tensorflow/contrib/lite/simple_memory_arena_test.cc41
5 files changed, 74 insertions, 8 deletions
diff --git a/tensorflow/contrib/lite/arena_planner_test.cc b/tensorflow/contrib/lite/arena_planner_test.cc
index a8a8755e2c..16171df10a 100644
--- a/tensorflow/contrib/lite/arena_planner_test.cc
+++ b/tensorflow/contrib/lite/arena_planner_test.cc
@@ -209,11 +209,8 @@ TEST_F(ArenaPlannerTest, ZeroSizedTensors) {
TestGraph graph({1}, {{{1}, {2}, {}}}, {2});
(*graph.tensors())[1].bytes = 0;
SetGraph(&graph);
- // TODO(ahentz): this is currently broken because the arena finds two
- // allocations with the same offset and returns an error.
- ASSERT_FALSE(planner_->ExecuteAllocations(0, 10) == kTfLiteOk);
- // EXPECT_EQ(GetOffset(1), 0);
- // EXPECT_EQ(GetOffset(2), GetOffsetAfter(1));
+ ASSERT_EQ(planner_->ExecuteAllocations(0, 10), kTfLiteOk);
+ EXPECT_EQ((*graph_->tensors())[1].data.raw, nullptr);
}
TEST_F(ArenaPlannerTest, SimpleGraph) {
diff --git a/tensorflow/contrib/lite/interpreter_test.cc b/tensorflow/contrib/lite/interpreter_test.cc
index 453c1ada1c..4c78466480 100644
--- a/tensorflow/contrib/lite/interpreter_test.cc
+++ b/tensorflow/contrib/lite/interpreter_test.cc
@@ -211,7 +211,7 @@ TEST(BasicInterpreter, CheckArenaAllocation) {
TfLiteRegistration reg = {nullptr, nullptr, nullptr, nullptr};
std::vector<int> sizes{2048, 4096, 1023, 2047, 1021,
- 2047, 1023, 2046, 1021, 2048};
+ 2047, 1023, 2046, 0, 2048};
for (int i = 0; i < sizes.size(); ++i) {
interpreter.SetTensorParametersReadWrite(i, kTfLiteUInt8, "", {sizes[i]},
quant);
@@ -228,6 +228,7 @@ TEST(BasicInterpreter, CheckArenaAllocation) {
ASSERT_EQ(interpreter.tensor(0)->data.raw, interpreter.tensor(4)->data.raw);
ASSERT_EQ(interpreter.tensor(1)->data.raw, interpreter.tensor(7)->data.raw);
+ ASSERT_EQ(interpreter.tensor(8)->data.raw, nullptr);
ASSERT_LT(interpreter.tensor(4)->data.raw, interpreter.tensor(1)->data.raw);
ASSERT_LT(interpreter.tensor(6)->data.raw, interpreter.tensor(1)->data.raw);
@@ -314,6 +315,18 @@ TEST(BasicInterpreter, ResizingTensors) {
EXPECT_EQ(tensor->bytes, 8 * sizeof(float));
ASSERT_EQ(interpreter.AllocateTensors(), kTfLiteOk);
+ ASSERT_EQ(interpreter.ResizeInputTensor(t, {}), kTfLiteOk);
+ EXPECT_EQ(tensor->bytes, 1 * sizeof(float));
+ ASSERT_EQ(interpreter.AllocateTensors(), kTfLiteOk);
+
+ ASSERT_EQ(interpreter.ResizeInputTensor(t, {0}), kTfLiteOk);
+ EXPECT_EQ(tensor->bytes, 0);
+ ASSERT_EQ(interpreter.AllocateTensors(), kTfLiteOk);
+
+ ASSERT_EQ(interpreter.ResizeInputTensor(t, {1, 2, 0}), kTfLiteOk);
+ EXPECT_EQ(tensor->bytes, 0);
+ ASSERT_EQ(interpreter.AllocateTensors(), kTfLiteOk);
+
// TODO(ahentz): We shouldn't have to force reallocation, but
// ResizeInputTensor doesn't realloc dynamic tensors. Also note that
// TfLiteTensorRealloc(tensor->bytes, tensor) is a no-op.
diff --git a/tensorflow/contrib/lite/simple_memory_arena.cc b/tensorflow/contrib/lite/simple_memory_arena.cc
index 2f2004f56b..4eaf6f1bfe 100644
--- a/tensorflow/contrib/lite/simple_memory_arena.cc
+++ b/tensorflow/contrib/lite/simple_memory_arena.cc
@@ -36,6 +36,12 @@ TfLiteStatus SimpleMemoryArena::Allocate(TfLiteContext* context,
ArenaAlloc* new_alloc) {
TF_LITE_ENSURE(context, alignment < arena_alignment_);
+ if (size == 0) {
+ new_alloc->offset = 0;
+ new_alloc->size = 0;
+ return kTfLiteOk;
+ }
+
size_t current_top = 0;
if (!allocs_.empty()) {
@@ -75,6 +81,10 @@ TfLiteStatus SimpleMemoryArena::Allocate(TfLiteContext* context,
TfLiteStatus SimpleMemoryArena::Deallocate(TfLiteContext* context,
const ArenaAlloc& alloc) {
+ if (alloc.size == 0) {
+ return kTfLiteOk;
+ }
+
int erased_allocs_count = 0;
auto it = allocs_.begin();
while (it != allocs_.end()) {
@@ -122,7 +132,11 @@ TfLiteStatus SimpleMemoryArena::ResolveAlloc(TfLiteContext* context,
char** output_ptr) {
TF_LITE_ENSURE(context, committed_);
TF_LITE_ENSURE(context, output_ptr != nullptr);
- *output_ptr = underlying_buffer_aligned_ptr_ + alloc.offset;
+ if (alloc.size == 0) {
+ *output_ptr = nullptr;
+ } else {
+ *output_ptr = underlying_buffer_aligned_ptr_ + alloc.offset;
+ }
return kTfLiteOk;
}
diff --git a/tensorflow/contrib/lite/simple_memory_arena.h b/tensorflow/contrib/lite/simple_memory_arena.h
index 5faf78b59e..f738315cf2 100644
--- a/tensorflow/contrib/lite/simple_memory_arena.h
+++ b/tensorflow/contrib/lite/simple_memory_arena.h
@@ -39,7 +39,8 @@ struct ArenaAlloc {
// This small class is responsible for allocating, deallocating and reusing
// dynamic memory from a common underlying buffer. The arena can be used in
// scenarios when the pattern of memory allocations and deallocations is
-// repetitive, e.g. running NN inference in multiple iterations.
+// repetitive, e.g. running NN inference in multiple iterations. Note that
+// zero-sized allocations are explicitly allowed, and will resolve to null.
class SimpleMemoryArena {
public:
explicit SimpleMemoryArena(size_t arena_alignment)
diff --git a/tensorflow/contrib/lite/simple_memory_arena_test.cc b/tensorflow/contrib/lite/simple_memory_arena_test.cc
index 4444f642eb..60d4d5e768 100644
--- a/tensorflow/contrib/lite/simple_memory_arena_test.cc
+++ b/tensorflow/contrib/lite/simple_memory_arena_test.cc
@@ -43,6 +43,47 @@ TEST(SimpleMemoryArenaTest, BasicArenaOperations) {
EXPECT_EQ(allocs[5].offset, 1024);
}
+TEST(SimpleMemoryArenaTest, BasicZeroAlloc) {
+ TfLiteContext context;
+ SimpleMemoryArena arena(64);
+ ArenaAlloc alloc;
+
+ // Zero-sized allocs should have a 0 offset and size.
+ ASSERT_EQ(arena.Allocate(&context, 32, 0, &alloc), kTfLiteOk);
+ EXPECT_EQ(alloc.offset, 0);
+ EXPECT_EQ(alloc.size, 0);
+
+ // Deallocation of zero-sized allocs should always succeed (even redundantly).
+ ASSERT_EQ(arena.Deallocate(&context, alloc), kTfLiteOk);
+ ASSERT_EQ(arena.Deallocate(&context, alloc), kTfLiteOk);
+
+ // The zero-sized alloc should resolve to null.
+ char* resolved_ptr = nullptr;
+ ASSERT_EQ(arena.Commit(&context), kTfLiteOk);
+ ASSERT_EQ(arena.ResolveAlloc(&context, alloc, &resolved_ptr), kTfLiteOk);
+ EXPECT_EQ(resolved_ptr, nullptr);
+}
+
+TEST(SimpleMemoryArenaTest, InterleavedZeroAlloc) {
+ TfLiteContext context;
+ SimpleMemoryArena arena(64);
+ ArenaAlloc allocs[4];
+
+ // Interleave some zero and non-zero-sized allocations and deallocations.
+ ASSERT_EQ(arena.Allocate(&context, 32, 2047, &allocs[0]), kTfLiteOk);
+ ASSERT_EQ(arena.Allocate(&context, 32, 0, &allocs[1]), kTfLiteOk);
+ ASSERT_EQ(arena.Allocate(&context, 32, 1023, &allocs[2]), kTfLiteOk);
+ ASSERT_EQ(arena.Deallocate(&context, allocs[1]), kTfLiteOk);
+ ASSERT_EQ(arena.Deallocate(&context, allocs[2]), kTfLiteOk);
+ ASSERT_EQ(arena.Allocate(&context, 32, 2047, &allocs[3]), kTfLiteOk);
+
+ // Deallocation of a zero-sized alloc should not impact the allocator offsets.
+ EXPECT_EQ(allocs[0].offset, 0);
+ EXPECT_EQ(allocs[1].offset, 0);
+ EXPECT_EQ(allocs[2].offset, 2048);
+ EXPECT_EQ(allocs[3].offset, 2048);
+}
+
TEST(SimpleMemoryArenaTest, TestAfterClear) {
TfLiteContext context;
SimpleMemoryArena arena(64);