Skip to content

Commit a8d13ff

Browse files
authored
Update kernel porting documentation (#3144)
@tensorflow/micro Updates/corrections to the FAQ section Add tensor inspection to the FAQ section bug=fixes #3143
1 parent ed237a8 commit a8d13ff

File tree

1 file changed

+104
-23
lines changed

1 file changed

+104
-23
lines changed

tensorflow/lite/micro/docs/porting_reference_ops.md

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,17 @@ https://github.com/ekalinin/github-markdown-toc#auto-insert-and-update-toc
4242
* [Notes](#notes)
4343
* [Frequently Asked Questions](#frequently-asked-questions)
4444
* [Can I use malloc/free or new/delete in my operator code?](#can-i-use-mallocfree-or-newdelete-in-my-operator-code)
45-
* [Can I use static variable allocation in my operator code?](#can-i-use-static-variable-allocation-in-my-operator-code)
45+
* [Can I use global/static variable constructors in my operator code](#can-i-use-globalstatic-variable-constructors-in-my-operator-code)
4646
* [How do I allocate persistent memory?](#how-do-i-allocate-persistent-memory)
4747
* [When am I allowed to allocate persistent memory?](#when-am-i-allowed-to-allocate-persistent-memory)
4848
* [How do I allocate/use temporary memory?](#how-do-i-allocateuse-temporary-memory)
4949
* [When can I allocate/use temporary memory?](#when-can-i-allocateuse-temporary-memory)
50-
* [Can I resize my input/output tensors?](#can-i-resize-my-inputoutput-tensors)
50+
* [Can I resize the tensors of my model?](#can-i-resize-the-tensors-of-my-model)
5151
* [Can I change the shape of tensors in my operator code?](#can-i-change-the-shape-of-tensors-in-my-operator-code)
5252
* [When can I change the shape of tensors in my operator code?](#when-can-i-change-the-shape-of-tensors-in-my-operator-code)
5353
* [Can I modify a TfLiteTensor or TfLiteEvalTensor?](#can-i-modify-a-tflitetensor-or-tfliteevaltensor)
54+
* [When can I inspect tensor data?](#when-can-i-inspect-tensor-data)
55+
* [How do I fix optimized kernel unit test failures?](#how-do-i-fix-optimized-kernel-unit-test-failures)
5456

5557
<!-- Added by: advaitjain, at: Thu 16 Sep 2021 11:49:51 AM PDT -->
5658

@@ -278,11 +280,17 @@ differences are actually significant or just stylistic.
278280
## Can I use malloc/free or new/delete in my operator code?
279281
No. All memory allocation in TensorFlow Lite Micro (TFLM) is done using C++
280282
stack based automatic allocation, or through specialized TFLM persistent
281-
and temporary allocation methods.
283+
and temporary allocation methods. The only use of `new` in TFLM is through the
284+
use of [placement new](https://en.cppreference.com/w/cpp/language/new.html#Placement_new).
282285
283-
## Can I use static variable allocation in my operator code?
284-
No. This is due to the call ordering of C++ static constructors being
285-
platform/compiler dependent.
286+
## Can I use global/static variable constructors in my operator code?
287+
Global constructors are not allowed. This is due to the call ordering of C++
288+
global constructors being platform/compiler dependent. Some platforms do not
289+
support global constructors at all, and will silently fail.
290+
291+
Static variables that use constructors are safe to declare within methods. This
292+
is because the static variable executes its call-once sequence only when the
293+
encapsulating method is called.
286294
287295
## How do I allocate persistent memory?
288296
Use `TfLiteContext::AllocatePersistentBuffer` to allocate persistent memory.
@@ -306,9 +314,9 @@ Use the `TfLiteContext::RequestScratchBufferInArena` and
306314
`TfLiteContext::GetScratchBuffer` methods. The temporary memory is shared
307315
between all operators, and is only valid for your operator within the scope
308316
of your operator's `Invoke` method. Do not attempt to use temporary memory
309-
to share data between operator invocations. Temporary memory is to be used
310-
only as pre-allocated storage during the execution scope of your operator's
311-
`Invoke` method.
317+
to share data between operator invocations. Scratch buffer temporary memory is
318+
to be used only as pre-allocated storage during the execution scope of your
319+
operator's `Invoke` method.
312320

313321
An example code snippet looks like ([add_n.cc](../kernels/add_n.cc)):
314322
```C++
@@ -321,7 +329,7 @@ if (output->type == kTfLiteFloat32) {
321329
context, scratch_size, &scratch_index));
322330
node->user_data =
323331
reinterpret_cast<decltype(node->user_data)>(scratch_index);
324-
}
332+
}
325333
```
326334
And to use the buffer:
327335
```C++
@@ -330,24 +338,59 @@ int scratch_index =
330338
void* scratch_buffer = context->GetScratchBuffer(context, scratch_index);
331339
```
332340
341+
If temporary memory is needed for a calculation during the operator's `Prepare`
342+
method, use `MicroContext::AllocateTempBuffer`. A matching call to
343+
`MicroContext::DeallocateTempBuffer` must be made prior to returning from your
344+
operator's `Prepare` method.
345+
346+
An example code snippet ([depthwise_conv_vision.cc](../kernels/xtensa/depthwise_conv_vision.cc)):
347+
```C++
348+
TfLiteTensor filter_int8;
349+
350+
if (filter->type == kTfLiteInt4) {
351+
const size_t bytes_unpacked = filter->bytes * 2;
352+
filter_int8.data.data = micro_context->AllocateTempBuffer(
353+
bytes_unpacked, tflite::MicroArenaBufferAlignment());
354+
filter_int8.dims = filter->dims;
355+
filter_int8.type = kTfLiteInt8;
356+
tflite::tensor_utils::UnpackDenseInt4IntoInt8(
357+
GetTensorData<int8_t>(filter), GetTensorShape(filter).FlatSize(),
358+
GetTensorData<int8_t>(&filter_int8));
359+
}
360+
361+
...
362+
363+
if (filter->type == kTfLiteInt4) {
364+
micro_context->DeallocateTempBuffer(GetTensorData<uint8_t>(&filter_int8));
365+
}
366+
```
367+
333368
## When can I allocate/use temporary memory?
334369
The `TfLiteContext::RequestScratchBufferInArena` method is available only within
335370
the scope of your operator's `Prepare` method.
336371
The `TfLiteContext::GetScratchBuffer` method is available only within
337372
the scope of your operator's `Invoke` method.
338373
339-
## Can I resize my input/output tensors?
340-
No. The storage space for each input/output tensor is a fixed, calculated value
341-
determined at the time the TensorFlow Lite (TfLite) model converter is executed.
342-
During the `Init` phase of the `tflite::MicroInterpreter` all tensor storage is
343-
allocated by the `tflite::MicroInterpreter` instance, using the calculated values
344-
of the model converter.
374+
The `MicroContext::AllocateTempBuffer` and `MicroContext::DeallocateTempBuffer`
375+
methods are only available within the scope of your operator's `Prepare` method.
376+
377+
## Can I resize the tensors of my model?
378+
No. The storage space for each `input`/`output`/`const` tensor is a fixed,
379+
calculated value determined at the time the TensorFlow Lite (TfLite) model
380+
converter is executed.
381+
All tensor storage is allocated by the `tflite::MicroInterpreter` instance,
382+
using the tensor shape values of the TfLite model converter.
383+
384+
TFLM does not support dynamic tensors. A dynamic tensor is one whose storage
385+
(and shape) can change during inference.
386+
345387
For more information see: [Memory Allocation Overview](online_memory_allocation_overview.md)
346388
347389
## Can I change the shape of tensors in my operator code?
348-
Yes. The new shape must not exceed the storage space indicated by the old shape.
349-
Because tensor shape values may live in memory that is not directly writable
350-
(ex. Flash, EEPROM, ROM), a special method must be called before modification
390+
Yes. The new shape must be exactly equal to the storage space indicated by the
391+
old shape.
392+
Because tensor shape values may live in non-volatile memory that is not directly
393+
writable (ex. Flash, ROM), a special method must be called before modification
351394
is attempted. The `tflite::micro::CreateWritableTensorDimsWithCopy` method will
352395
move the tensor shape values to guaranteed persistent writable memory.
353396
@@ -378,8 +421,46 @@ structures. Your code should not modify these data structures. The only
378421
directly allowed modification of tensors is to change their data values, or
379422
their shape values.
380423
424+
## When can I inspect tensor data?
425+
Tensor data can always be inspected within the scope of your operator's `Invoke`
426+
method.
427+
428+
Within the scope of your operator's `Prepare` method, use
429+
`tflite::IsConstantTensor` before inspecting the tensor data.
430+
Only when `tflite::IsConstantTensor` returns `true`, does the tensor have valid
431+
data.
432+
381433
## How do I fix optimized kernel unit test failures?
382-
Kernel unit tests for all optimizated kernels should pass. By default kernel unit
383-
tests for the newly added op may fail for optimized kernels as they may not have the
384-
correct references. In this case, we should let the optimized kernels fall back
385-
to the newly added reference kernels. For example, refer to this [this commit](https://github.com/tensorflow/tflite-micro/pull/1274/commits/d36c9dd598dcbf352f2c60463fd0d4153703a1cd).
434+
Kernel unit tests for all optimized kernels should pass.
435+
It should be noted that optimized kernels may not handle all tensor types or
436+
operator parameters.
437+
In this case, the optimized kernel should fallback on calling the reference
438+
kernel methods. The following code snippet example can be found [here](https://github.com/tensorflow/tflite-micro/blob/a1eb0480ed9f98e9ea5f5fb9b8e3da98ec512caf/tensorflow/lite/micro/kernels/cmsis_nn/softmax.cc#L98C1-L123C6):
439+
```
440+
case kTfLiteFloat32: {
441+
tflite::reference_ops::Softmax(
442+
op_data.softmax_params, tflite::micro::GetTensorShape(input),
443+
tflite::micro::GetTensorData<float>(input),
444+
tflite::micro::GetTensorShape(output),
445+
tflite::micro::GetTensorData<float>(output));
446+
return kTfLiteOk;
447+
}
448+
case kTfLiteInt8: {
449+
if (output->type == kTfLiteInt8) {
450+
arm_softmax_s8(tflite::micro::GetTensorData<int8_t>(input),
451+
op_data.num_rows, op_data.row_size,
452+
op_data.softmax_params.input_multiplier,
453+
op_data.softmax_params.input_left_shift,
454+
op_data.softmax_params.diff_min,
455+
tflite::micro::GetTensorData<int8_t>(output));
456+
} else {
457+
arm_softmax_s8_s16(tflite::micro::GetTensorData<int8_t>(input),
458+
op_data.num_rows, op_data.row_size,
459+
op_data.softmax_params.input_multiplier,
460+
op_data.softmax_params.input_left_shift,
461+
op_data.softmax_params.diff_min,
462+
tflite::micro::GetTensorData<int16_t>(output));
463+
}
464+
return kTfLiteOk;
465+
}
466+
```

0 commit comments

Comments
 (0)