Allow TFLite profiling without a build flag
Introduce a simple profiler interface for allowing client injection of an arbitrary profiler implementation at runtime, without requiring special build flags. PiperOrigin-RevId: 247044534
This commit is contained in:
parent
c1b4525808
commit
75bd1d5c92
@ -17,6 +17,7 @@ cc_library(
|
|||||||
"error_reporter.h",
|
"error_reporter.h",
|
||||||
"flatbuffer_conversions.h",
|
"flatbuffer_conversions.h",
|
||||||
"op_resolver.h",
|
"op_resolver.h",
|
||||||
|
"profiler.h",
|
||||||
],
|
],
|
||||||
copts = tflite_copts(),
|
copts = tflite_copts(),
|
||||||
deps = [
|
deps = [
|
||||||
|
85
tensorflow/lite/core/api/profiler.h
Normal file
85
tensorflow/lite/core/api/profiler.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/* Copyright 2017 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.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_CORE_API_PROFILER_H_
|
||||||
|
#define TENSORFLOW_LITE_CORE_API_PROFILER_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
|
||||||
|
// A simple utility for enabling profiled event tracing in TensorFlow Lite.
|
||||||
|
class Profiler {
|
||||||
|
public:
|
||||||
|
enum class EventType {
|
||||||
|
// Default event type, the metadata field has no special significance.
|
||||||
|
DEFAULT = 0,
|
||||||
|
// The event is an operator invocation and the event_metadata field is the
|
||||||
|
// index of operator node.
|
||||||
|
OPERATOR_INVOKE_EVENT = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~Profiler() {}
|
||||||
|
|
||||||
|
// Signals the beginning of an event, returning a handle to the profile event.
|
||||||
|
virtual uint32_t BeginEvent(const char* tag, EventType event_type,
|
||||||
|
uint32_t event_metadata) = 0;
|
||||||
|
|
||||||
|
// Signals an end to the specified profile event.
|
||||||
|
virtual void EndEvent(uint32_t event_handle) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Adds a profile event to `profiler` that begins with the construction
|
||||||
|
// of the object and ends when the object goes out of scope.
|
||||||
|
// The lifetime of tag should be at least the lifetime of `profiler`.
|
||||||
|
// `profiler` may be null, in which case nothing is profiled.
|
||||||
|
class ScopedProfile {
|
||||||
|
public:
|
||||||
|
ScopedProfile(Profiler* profiler, const char* tag,
|
||||||
|
Profiler::EventType event_type = Profiler::EventType::DEFAULT,
|
||||||
|
uint32_t event_metadata = 0)
|
||||||
|
: profiler_(profiler), event_handle_(0) {
|
||||||
|
if (profiler) {
|
||||||
|
event_handle_ = profiler_->BeginEvent(tag, event_type, event_metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedProfile() {
|
||||||
|
if (profiler_) {
|
||||||
|
profiler_->EndEvent(event_handle_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Profiler* const profiler_;
|
||||||
|
uint32_t event_handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScopedOperatorProfile : public ScopedProfile {
|
||||||
|
public:
|
||||||
|
ScopedOperatorProfile(Profiler* profiler, const char* tag, int node_index)
|
||||||
|
: ScopedProfile(profiler, tag, Profiler::EventType::OPERATOR_INVOKE_EVENT,
|
||||||
|
static_cast<uint32_t>(node_index)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#define TFLITE_VARNAME_UNIQ(name, ctr) name##ctr
|
||||||
|
#define TFLITE_SCOPED_TAGGED_OPERATOR_PROFILE(profiler, tag, node_index) \
|
||||||
|
tflite::ScopedOperatorProfile TFLITE_VARNAME_UNIQ(_profile_, __COUNTER__)( \
|
||||||
|
(profiler), (tag), (node_index))
|
||||||
|
#define TFLITE_SCOPED_OPERATOR_PROFILE(profiler, node_index) \
|
||||||
|
TFLITE_SCOPED_TAGGED_OPERATOR_PROFILE((profiler), "OpInvoke", (node_index))
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_CORE_API_PROFILER_H_
|
@ -700,7 +700,7 @@ TfLiteStatus Subgraph::Invoke() {
|
|||||||
TfLiteNode& node = nodes_and_registration_[node_index].first;
|
TfLiteNode& node = nodes_and_registration_[node_index].first;
|
||||||
const TfLiteRegistration& registration =
|
const TfLiteRegistration& registration =
|
||||||
nodes_and_registration_[node_index].second;
|
nodes_and_registration_[node_index].second;
|
||||||
SCOPED_OPERATOR_PROFILE(profiler_, node_index);
|
TFLITE_SCOPED_OPERATOR_PROFILE(profiler_, node_index);
|
||||||
|
|
||||||
// TODO(ycling): This is an extra loop through inputs to check if the data
|
// TODO(ycling): This is an extra loop through inputs to check if the data
|
||||||
// need to be copied from Delegate buffer to raw memory, which is often not
|
// need to be copied from Delegate buffer to raw memory, which is often not
|
||||||
|
@ -20,9 +20,9 @@ limitations under the License.
|
|||||||
|
|
||||||
#include "tensorflow/lite/allocation.h"
|
#include "tensorflow/lite/allocation.h"
|
||||||
#include "tensorflow/lite/c/c_api_internal.h"
|
#include "tensorflow/lite/c/c_api_internal.h"
|
||||||
|
#include "tensorflow/lite/core/api/profiler.h"
|
||||||
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
|
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
|
||||||
#include "tensorflow/lite/memory_planner.h"
|
#include "tensorflow/lite/memory_planner.h"
|
||||||
#include "tensorflow/lite/profiling/profiler.h"
|
|
||||||
#include "tensorflow/lite/util.h"
|
#include "tensorflow/lite/util.h"
|
||||||
|
|
||||||
namespace tflite {
|
namespace tflite {
|
||||||
@ -276,12 +276,12 @@ class Subgraph {
|
|||||||
// WARNING: This is an experimental API and subject to change.
|
// WARNING: This is an experimental API and subject to change.
|
||||||
TfLiteStatus ResetVariableTensors();
|
TfLiteStatus ResetVariableTensors();
|
||||||
|
|
||||||
void SetProfiler(profiling::Profiler* profiler) {
|
void SetProfiler(Profiler* profiler) {
|
||||||
profiler_ = profiler;
|
profiler_ = profiler;
|
||||||
context_->profiler = profiler;
|
context_->profiler = profiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
profiling::Profiler* GetProfiler() { return profiler_; }
|
Profiler* GetProfiler() { return profiler_; }
|
||||||
|
|
||||||
// Returns a pointer to vector of subgraphs.
|
// Returns a pointer to vector of subgraphs.
|
||||||
// WARNING: This is an experimental API and subject to change.
|
// WARNING: This is an experimental API and subject to change.
|
||||||
@ -527,7 +527,7 @@ class Subgraph {
|
|||||||
TfLiteExternalContext** external_contexts_;
|
TfLiteExternalContext** external_contexts_;
|
||||||
|
|
||||||
// Profiler for this interpreter instance.
|
// Profiler for this interpreter instance.
|
||||||
profiling::Profiler* profiler_ = nullptr;
|
Profiler* profiler_ = nullptr;
|
||||||
|
|
||||||
// A pointer to vector of subgraphs. The vector is owned by the interpreter.
|
// A pointer to vector of subgraphs. The vector is owned by the interpreter.
|
||||||
std::vector<std::unique_ptr<Subgraph>>* subgraphs_ = nullptr;
|
std::vector<std::unique_ptr<Subgraph>>* subgraphs_ = nullptr;
|
||||||
|
@ -154,11 +154,11 @@ cc_library(
|
|||||||
":delegate_data",
|
":delegate_data",
|
||||||
":util",
|
":util",
|
||||||
"@flatbuffers",
|
"@flatbuffers",
|
||||||
|
"//tensorflow/lite/core/api",
|
||||||
"//tensorflow/lite/c:c_api_internal",
|
"//tensorflow/lite/c:c_api_internal",
|
||||||
"//tensorflow/lite:kernel_api",
|
"//tensorflow/lite:kernel_api",
|
||||||
"//tensorflow/lite:string",
|
"//tensorflow/lite:string",
|
||||||
"//tensorflow/lite/kernels:kernel_util",
|
"//tensorflow/lite/kernels:kernel_util",
|
||||||
"//tensorflow/lite/profiling:profiler",
|
|
||||||
] + select({
|
] + select({
|
||||||
# TODO(b/111881878): The android_tensorflow_lib target pulls in the full
|
# TODO(b/111881878): The android_tensorflow_lib target pulls in the full
|
||||||
# set of core TensorFlow kernels. We may want to revisit this dependency
|
# set of core TensorFlow kernels. We may want to revisit this dependency
|
||||||
|
@ -24,10 +24,10 @@ limitations under the License.
|
|||||||
#include "tensorflow/lite/builtin_ops.h"
|
#include "tensorflow/lite/builtin_ops.h"
|
||||||
#include "tensorflow/lite/c/c_api_internal.h"
|
#include "tensorflow/lite/c/c_api_internal.h"
|
||||||
#include "tensorflow/lite/context_util.h"
|
#include "tensorflow/lite/context_util.h"
|
||||||
|
#include "tensorflow/lite/core/api/profiler.h"
|
||||||
#include "tensorflow/lite/delegates/flex/delegate_data.h"
|
#include "tensorflow/lite/delegates/flex/delegate_data.h"
|
||||||
#include "tensorflow/lite/delegates/flex/util.h"
|
#include "tensorflow/lite/delegates/flex/util.h"
|
||||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||||
#include "tensorflow/lite/profiling/profiler.h"
|
|
||||||
#include "tensorflow/lite/string.h"
|
#include "tensorflow/lite/string.h"
|
||||||
|
|
||||||
// Note: this is part of TF Lite's Flex delegation code which is to be
|
// Note: this is part of TF Lite's Flex delegation code which is to be
|
||||||
@ -529,8 +529,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
|||||||
|
|
||||||
// Execute the TensorFlow Ops sequentially.
|
// Execute the TensorFlow Ops sequentially.
|
||||||
for (auto& node_data : op_data->nodes) {
|
for (auto& node_data : op_data->nodes) {
|
||||||
SCOPED_TAGGED_OPERATOR_PROFILE(
|
TFLITE_SCOPED_TAGGED_OPERATOR_PROFILE(
|
||||||
reinterpret_cast<profiling::Profiler*>(context->profiler),
|
reinterpret_cast<Profiler*>(context->profiler),
|
||||||
node_data->name().c_str(), node_data->index());
|
node_data->name().c_str(), node_data->index());
|
||||||
|
|
||||||
auto status = ExecuteFlexOp(context, buffer_map, node_data.get());
|
auto status = ExecuteFlexOp(context, buffer_map, node_data.get());
|
||||||
|
@ -256,11 +256,11 @@ TfLiteStatus Interpreter::GetBufferHandle(int tensor_index,
|
|||||||
return kTfLiteOk;
|
return kTfLiteOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::SetProfiler(profiling::Profiler* profiler) {
|
void Interpreter::SetProfiler(Profiler* profiler) {
|
||||||
for (auto& subgraph : subgraphs_) subgraph->SetProfiler(profiler);
|
for (auto& subgraph : subgraphs_) subgraph->SetProfiler(profiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
profiling::Profiler* Interpreter::GetProfiler() {
|
Profiler* Interpreter::GetProfiler() {
|
||||||
return primary_subgraph().GetProfiler();
|
return primary_subgraph().GetProfiler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,9 +402,14 @@ class Interpreter {
|
|||||||
TfLiteBufferHandle* buffer_handle,
|
TfLiteBufferHandle* buffer_handle,
|
||||||
TfLiteDelegate** delegate);
|
TfLiteDelegate** delegate);
|
||||||
|
|
||||||
void SetProfiler(profiling::Profiler* profiler);
|
// Sets the profiler to tracing execution. The caller retains ownership
|
||||||
|
// of the profiler and must ensure its validity.
|
||||||
|
// WARNING: This is an experimental API and subject to change.
|
||||||
|
void SetProfiler(Profiler* profiler);
|
||||||
|
|
||||||
profiling::Profiler* GetProfiler();
|
// Gets the profiler used for op tracing.
|
||||||
|
// WARNING: This is an experimental API and subject to change.
|
||||||
|
Profiler* GetProfiler();
|
||||||
|
|
||||||
// The default capacity of `tensors_` vector.
|
// The default capacity of `tensors_` vector.
|
||||||
static constexpr int kTensorsReservedCapacity = 128;
|
static constexpr int kTensorsReservedCapacity = 128;
|
||||||
|
@ -10,16 +10,21 @@ common_copts = [
|
|||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "profiler",
|
name = "profiler",
|
||||||
hdrs = ["profiler.h"],
|
hdrs = [
|
||||||
|
"buffered_profiler.h",
|
||||||
|
"noop_profiler.h",
|
||||||
|
"profiler.h",
|
||||||
|
],
|
||||||
copts = common_copts,
|
copts = common_copts,
|
||||||
deps = [":profile_buffer"],
|
deps = [
|
||||||
|
":profile_buffer",
|
||||||
|
"//tensorflow/lite/core/api",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "profiler_test",
|
name = "profiler_test",
|
||||||
srcs = ["profiler_test.cc"],
|
srcs = ["profiler_test.cc"],
|
||||||
copts = ["-DTFLITE_PROFILING_ENABLED"],
|
|
||||||
defines = ["TFLITE_PROFILING_ENABLED"],
|
|
||||||
deps = [
|
deps = [
|
||||||
":profiler",
|
":profiler",
|
||||||
"//tensorflow/lite/testing:util",
|
"//tensorflow/lite/testing:util",
|
||||||
@ -31,7 +36,10 @@ cc_library(
|
|||||||
name = "profile_buffer",
|
name = "profile_buffer",
|
||||||
hdrs = ["profile_buffer.h"],
|
hdrs = ["profile_buffer.h"],
|
||||||
copts = common_copts,
|
copts = common_copts,
|
||||||
deps = [":time"],
|
deps = [
|
||||||
|
":time",
|
||||||
|
"//tensorflow/lite/core/api",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
@ -58,7 +66,7 @@ cc_library(
|
|||||||
hdrs = ["profile_summarizer.h"],
|
hdrs = ["profile_summarizer.h"],
|
||||||
copts = common_copts,
|
copts = common_copts,
|
||||||
deps = [
|
deps = [
|
||||||
":profiler",
|
":profile_buffer",
|
||||||
"//tensorflow/core:stats_calculator_portable",
|
"//tensorflow/core:stats_calculator_portable",
|
||||||
"//tensorflow/lite:framework",
|
"//tensorflow/lite:framework",
|
||||||
"//tensorflow/lite/schema:schema_fbs",
|
"//tensorflow/lite/schema:schema_fbs",
|
||||||
@ -83,8 +91,6 @@ cc_test(
|
|||||||
cc_test(
|
cc_test(
|
||||||
name = "profile_buffer_test",
|
name = "profile_buffer_test",
|
||||||
srcs = ["profile_buffer_test.cc"],
|
srcs = ["profile_buffer_test.cc"],
|
||||||
copts = ["-DTFLITE_PROFILING_ENABLED"],
|
|
||||||
defines = ["TFLITE_PROFILING_ENABLED"],
|
|
||||||
deps = [
|
deps = [
|
||||||
":profile_buffer",
|
":profile_buffer",
|
||||||
"//tensorflow/lite/testing:util",
|
"//tensorflow/lite/testing:util",
|
||||||
|
108
tensorflow/lite/profiling/buffered_profiler.h
Normal file
108
tensorflow/lite/profiling/buffered_profiler.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/* 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.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_PROFILING_BUFFERED_PROFILER_H_
|
||||||
|
#define TENSORFLOW_LITE_PROFILING_BUFFERED_PROFILER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/core/api/profiler.h"
|
||||||
|
#include "tensorflow/lite/profiling/profile_buffer.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace profiling {
|
||||||
|
|
||||||
|
// Controls whether profiling is enabled or disabled and collects profiles.
|
||||||
|
// TFLite is used on platforms that don't have posix threads, so the profiler is
|
||||||
|
// kept as simple as possible. It is designed to be used only on a single
|
||||||
|
// thread.
|
||||||
|
//
|
||||||
|
// Profiles are collected using Scoped*Profile objects that begin and end a
|
||||||
|
// profile event.
|
||||||
|
// An example usage is shown in the example below:
|
||||||
|
//
|
||||||
|
// Say Worker class has a DoWork method and we are interested in profiling
|
||||||
|
// the overall execution time for DoWork and time spent in Task1 and Task2
|
||||||
|
// functions.
|
||||||
|
//
|
||||||
|
// class Worker {
|
||||||
|
// public:
|
||||||
|
// void DoWork() {
|
||||||
|
// ScopedProfile(&controller, "DoWork");
|
||||||
|
// Task1();
|
||||||
|
// Task2();
|
||||||
|
// .....
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void Task1() {
|
||||||
|
// ScopedProfile(&controller, "Task1");
|
||||||
|
// ....
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void Task2() {
|
||||||
|
// ScopedProfile(&controller, "Task2");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Profiler profiler;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// We instrument the functions that need to be profiled.
|
||||||
|
//
|
||||||
|
// Profile can be collected by enable profiling and then getting profile
|
||||||
|
// events.
|
||||||
|
//
|
||||||
|
// void ProfileWorker() {
|
||||||
|
// Worker worker;
|
||||||
|
// worker.profiler.EnableProfiling();
|
||||||
|
// worker.DoWork();
|
||||||
|
// worker.profiler.DisableProfiling();
|
||||||
|
// // Profiling is complete, extract profiles.
|
||||||
|
// auto profile_events = worker.profiler.GetProfiles();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class BufferedProfiler : public tflite::Profiler {
|
||||||
|
public:
|
||||||
|
BufferedProfiler() : buffer_(1024, false) {}
|
||||||
|
|
||||||
|
uint32_t BeginEvent(const char* tag, EventType event_type,
|
||||||
|
uint32_t event_metadata) override {
|
||||||
|
return buffer_.BeginEvent(tag, event_type, event_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndEvent(uint32_t event_handle) override {
|
||||||
|
buffer_.EndEvent(event_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartProfiling() { buffer_.SetEnabled(true); }
|
||||||
|
void StopProfiling() { buffer_.SetEnabled(false); }
|
||||||
|
void Reset() { buffer_.Reset(); }
|
||||||
|
std::vector<const ProfileEvent*> GetProfileEvents() {
|
||||||
|
std::vector<const ProfileEvent*> profile_events;
|
||||||
|
profile_events.reserve(buffer_.Size());
|
||||||
|
for (size_t i = 0; i < buffer_.Size(); i++) {
|
||||||
|
profile_events.push_back(buffer_.At(i));
|
||||||
|
}
|
||||||
|
return profile_events;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ProfileBuffer* GetProfileBuffer() { return &buffer_; }
|
||||||
|
ProfileBuffer buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace profiling
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_PROFILING_BUFFERED_PROFILER_H_
|
43
tensorflow/lite/profiling/noop_profiler.h
Normal file
43
tensorflow/lite/profiling/noop_profiler.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* 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.
|
||||||
|
==============================================================================*/
|
||||||
|
#ifndef TENSORFLOW_LITE_PROFILING_NOOP_PROFILER_H_
|
||||||
|
#define TENSORFLOW_LITE_PROFILING_NOOP_PROFILER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/core/api/profiler.h"
|
||||||
|
#include "tensorflow/lite/profiling/profile_buffer.h"
|
||||||
|
|
||||||
|
namespace tflite {
|
||||||
|
namespace profiling {
|
||||||
|
|
||||||
|
// A noop version of profiler when profiling is disabled.
|
||||||
|
class NoopProfiler : public tflite::Profiler {
|
||||||
|
public:
|
||||||
|
NoopProfiler() {}
|
||||||
|
|
||||||
|
uint32_t BeginEvent(const char*, EventType, uint32_t) override { return 0; }
|
||||||
|
void EndEvent(uint32_t) override {}
|
||||||
|
|
||||||
|
void StartProfiling() {}
|
||||||
|
void StopProfiling() {}
|
||||||
|
void Reset() {}
|
||||||
|
std::vector<const ProfileEvent*> GetProfileEvents() { return {}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace profiling
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#endif // TENSORFLOW_LITE_PROFILING_NOOP_PROFILER_H_
|
@ -15,27 +15,27 @@ limitations under the License.
|
|||||||
#ifndef TENSORFLOW_LITE_PROFILING_PROFILE_BUFFER_H_
|
#ifndef TENSORFLOW_LITE_PROFILING_PROFILE_BUFFER_H_
|
||||||
#define TENSORFLOW_LITE_PROFILING_PROFILE_BUFFER_H_
|
#define TENSORFLOW_LITE_PROFILING_PROFILE_BUFFER_H_
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "tensorflow/lite/core/api/profiler.h"
|
||||||
#include "tensorflow/lite/profiling/time.h"
|
#include "tensorflow/lite/profiling/time.h"
|
||||||
|
|
||||||
namespace tflite {
|
namespace tflite {
|
||||||
namespace profiling {
|
namespace profiling {
|
||||||
|
|
||||||
|
constexpr uint32_t kInvalidEventHandle = static_cast<uint32_t>(~0) - 1;
|
||||||
|
|
||||||
// A profiling event.
|
// A profiling event.
|
||||||
struct ProfileEvent {
|
struct ProfileEvent {
|
||||||
// Describes the type of event.
|
// Describes the type of event.
|
||||||
// The event_metadata field may contain additional data for interpreting
|
// The event_metadata field may contain additional data for interpreting
|
||||||
// the event.
|
// the event.
|
||||||
enum class EventType {
|
using EventType = tflite::Profiler::EventType;
|
||||||
// Default event type, the metadata field has no special significance.
|
|
||||||
DEFAULT = 0,
|
|
||||||
// The event is an operator invocation and the event_metadata field is the
|
|
||||||
// index of operator node.
|
|
||||||
OPERATOR_INVOKE_EVENT = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
// Label of the event. This usually describes the event.
|
// Label of the event. This usually describes the event.
|
||||||
const char* tag;
|
const char* tag;
|
||||||
@ -49,17 +49,6 @@ struct ProfileEvent {
|
|||||||
// Extra data describing the details of the event.
|
// Extra data describing the details of the event.
|
||||||
uint32_t event_metadata;
|
uint32_t event_metadata;
|
||||||
};
|
};
|
||||||
} // namespace profiling
|
|
||||||
} // namespace tflite
|
|
||||||
|
|
||||||
#ifdef TFLITE_PROFILING_ENABLED
|
|
||||||
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace tflite {
|
|
||||||
namespace profiling {
|
|
||||||
constexpr uint32_t kInvalidEventHandle = static_cast<uint32_t>(~0) - 1;
|
|
||||||
|
|
||||||
// A ring buffer of profile events.
|
// A ring buffer of profile events.
|
||||||
// This class is not thread safe.
|
// This class is not thread safe.
|
||||||
@ -128,7 +117,7 @@ class ProfileBuffer {
|
|||||||
// Returns the profile event at the given index. If the index is invalid a
|
// Returns the profile event at the given index. If the index is invalid a
|
||||||
// nullptr is returned. The return event may get overwritten if more events
|
// nullptr is returned. The return event may get overwritten if more events
|
||||||
// are added to buffer.
|
// are added to buffer.
|
||||||
const struct ProfileEvent* const At(size_t index) const {
|
const struct ProfileEvent* At(size_t index) const {
|
||||||
size_t size = Size();
|
size_t size = Size();
|
||||||
if (index >= size) {
|
if (index >= size) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -145,7 +134,8 @@ class ProfileBuffer {
|
|||||||
uint32_t current_index_;
|
uint32_t current_index_;
|
||||||
std::vector<ProfileEvent> event_buffer_;
|
std::vector<ProfileEvent> event_buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace profiling
|
} // namespace profiling
|
||||||
} // namespace tflite
|
} // namespace tflite
|
||||||
#endif // TFLITE_PROFILING_ENABLED
|
|
||||||
#endif // TENSORFLOW_LITE_PROFILING_PROFILE_BUFFER_H_
|
#endif // TENSORFLOW_LITE_PROFILING_PROFILE_BUFFER_H_
|
||||||
|
@ -18,9 +18,9 @@ limitations under the License.
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "tensorflow/lite/interpreter.h"
|
|
||||||
#include "tensorflow/lite/profiling/profiler.h"
|
|
||||||
#include "tensorflow/core/util/stats_calculator.h"
|
#include "tensorflow/core/util/stats_calculator.h"
|
||||||
|
#include "tensorflow/lite/interpreter.h"
|
||||||
|
#include "tensorflow/lite/profiling/profile_buffer.h"
|
||||||
|
|
||||||
namespace tflite {
|
namespace tflite {
|
||||||
namespace profiling {
|
namespace profiling {
|
||||||
|
@ -33,7 +33,6 @@ namespace {
|
|||||||
|
|
||||||
const char* kOpName = "SimpleOpEval";
|
const char* kOpName = "SimpleOpEval";
|
||||||
|
|
||||||
#ifdef TFLITE_PROFILING_ENABLED
|
|
||||||
TfLiteStatus SimpleOpEval(TfLiteContext* context, TfLiteNode* node) {
|
TfLiteStatus SimpleOpEval(TfLiteContext* context, TfLiteNode* node) {
|
||||||
const TfLiteTensor* input1 = tflite::GetInput(context, node, /*index=*/0);
|
const TfLiteTensor* input1 = tflite::GetInput(context, node, /*index=*/0);
|
||||||
const TfLiteTensor* input2 = tflite::GetInput(context, node, /*index=*/1);
|
const TfLiteTensor* input2 = tflite::GetInput(context, node, /*index=*/1);
|
||||||
@ -69,7 +68,6 @@ TfLiteRegistration* RegisterSimpleOpWithProfilingDetails() {
|
|||||||
1};
|
1};
|
||||||
return ®istration;
|
return ®istration;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
class SimpleOpModel : public SingleOpModel {
|
class SimpleOpModel : public SingleOpModel {
|
||||||
public:
|
public:
|
||||||
@ -101,9 +99,8 @@ TEST(ProfileSummarizerTest, Empty) {
|
|||||||
EXPECT_GT(output.size(), 0);
|
EXPECT_GT(output.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TFLITE_PROFILING_ENABLED
|
|
||||||
TEST(ProfileSummarizerTest, Interpreter) {
|
TEST(ProfileSummarizerTest, Interpreter) {
|
||||||
Profiler profiler;
|
BufferedProfiler profiler;
|
||||||
SimpleOpModel m;
|
SimpleOpModel m;
|
||||||
m.Init(RegisterSimpleOp);
|
m.Init(RegisterSimpleOp);
|
||||||
auto interpreter = m.GetInterpreter();
|
auto interpreter = m.GetInterpreter();
|
||||||
@ -124,7 +121,7 @@ TEST(ProfileSummarizerTest, Interpreter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ProfileSummarizerTest, InterpreterPlusProfilingDetails) {
|
TEST(ProfileSummarizerTest, InterpreterPlusProfilingDetails) {
|
||||||
Profiler profiler;
|
BufferedProfiler profiler;
|
||||||
SimpleOpModel m;
|
SimpleOpModel m;
|
||||||
m.Init(RegisterSimpleOpWithProfilingDetails);
|
m.Init(RegisterSimpleOpWithProfilingDetails);
|
||||||
auto interpreter = m.GetInterpreter();
|
auto interpreter = m.GetInterpreter();
|
||||||
@ -145,8 +142,6 @@ TEST(ProfileSummarizerTest, InterpreterPlusProfilingDetails) {
|
|||||||
<< output;
|
<< output;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace profiling
|
} // namespace profiling
|
||||||
} // namespace tflite
|
} // namespace tflite
|
||||||
|
@ -15,168 +15,23 @@ limitations under the License.
|
|||||||
#ifndef TENSORFLOW_LITE_PROFILING_PROFILER_H_
|
#ifndef TENSORFLOW_LITE_PROFILING_PROFILER_H_
|
||||||
#define TENSORFLOW_LITE_PROFILING_PROFILER_H_
|
#define TENSORFLOW_LITE_PROFILING_PROFILER_H_
|
||||||
|
|
||||||
#include <vector>
|
#include "tensorflow/lite/profiling/buffered_profiler.h"
|
||||||
|
#include "tensorflow/lite/profiling/noop_profiler.h"
|
||||||
|
|
||||||
#include "tensorflow/lite/profiling/profile_buffer.h"
|
namespace tflite {
|
||||||
|
namespace profiling {
|
||||||
|
|
||||||
|
// TODO(b/131688504): Remove this and use runtime flags for profiler selection.
|
||||||
#ifdef TFLITE_PROFILING_ENABLED
|
#ifdef TFLITE_PROFILING_ENABLED
|
||||||
|
using Profiler = BufferedProfiler;
|
||||||
namespace tflite {
|
|
||||||
namespace profiling {
|
|
||||||
class ScopedProfile;
|
|
||||||
class ScopedOperatorProfile;
|
|
||||||
|
|
||||||
// Controls whether profiling is enabled or disabled and collects profiles.
|
|
||||||
// TFLite is used on platforms that don't have posix threads, so the profiler is
|
|
||||||
// kept as simple as possible. It is designed to be used only on a single
|
|
||||||
// thread.
|
|
||||||
//
|
|
||||||
// Profiles are collected using Scoped*Profile objects that begin and end a
|
|
||||||
// profile event.
|
|
||||||
// An example usage is shown in the example below:
|
|
||||||
//
|
|
||||||
// Say Worker class has a DoWork method and we are interested in profiling
|
|
||||||
// the overall execution time for DoWork and time spent in Task1 and Task2
|
|
||||||
// functions.
|
|
||||||
//
|
|
||||||
// class Worker {
|
|
||||||
// public:
|
|
||||||
// void DoWork() {
|
|
||||||
// ScopedProfile(&controller, "DoWork");
|
|
||||||
// Task1();
|
|
||||||
// Task2();
|
|
||||||
// .....
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void Task1() {
|
|
||||||
// ScopedProfile(&controller, "Task1");
|
|
||||||
// ....
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// void Task2() {
|
|
||||||
// ScopedProfile(&controller, "Task2");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Profiler profiler;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// We instrument the functions that need to be profiled.
|
|
||||||
//
|
|
||||||
// Profile can be collected by enable profiling and then getting profile
|
|
||||||
// events.
|
|
||||||
//
|
|
||||||
// void ProfileWorker() {
|
|
||||||
// Worker worker;
|
|
||||||
// worker.profiler.EnableProfiling();
|
|
||||||
// worker.DoWork();
|
|
||||||
// worker.profiler.DisableProfiling();
|
|
||||||
// // Profiling is complete, extract profiles.
|
|
||||||
// auto profile_events = worker.profiler.GetProfiles();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
class Profiler {
|
|
||||||
public:
|
|
||||||
Profiler() : buffer_(1024, false) {}
|
|
||||||
|
|
||||||
void StartProfiling() { buffer_.SetEnabled(true); }
|
|
||||||
void StopProfiling() { buffer_.SetEnabled(false); }
|
|
||||||
void Reset() { buffer_.Reset(); }
|
|
||||||
std::vector<const ProfileEvent*> GetProfileEvents() {
|
|
||||||
std::vector<const ProfileEvent*> profile_events;
|
|
||||||
profile_events.reserve(buffer_.Size());
|
|
||||||
for (size_t i = 0; i < buffer_.Size(); i++) {
|
|
||||||
profile_events.push_back(buffer_.At(i));
|
|
||||||
}
|
|
||||||
return profile_events;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class ScopedProfile;
|
|
||||||
friend class ScopedOperatorProfile;
|
|
||||||
ProfileBuffer* GetProfileBuffer() { return &buffer_; }
|
|
||||||
ProfileBuffer buffer_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScopedProfile {
|
|
||||||
public:
|
|
||||||
// Adds a profile event to profile that begins with the construction
|
|
||||||
// of object and ends when the object goes out of scope.
|
|
||||||
// The lifetime of tag should be at least the lifetime of profiler.
|
|
||||||
|
|
||||||
ScopedProfile(Profiler* profiler, const char* tag)
|
|
||||||
: buffer_(nullptr), event_handle_(0) {
|
|
||||||
if (profiler) {
|
|
||||||
buffer_ = profiler->GetProfileBuffer();
|
|
||||||
event_handle_ =
|
|
||||||
buffer_->BeginEvent(tag, ProfileEvent::EventType::DEFAULT, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~ScopedProfile() {
|
|
||||||
if (buffer_) {
|
|
||||||
buffer_->EndEvent(event_handle_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ProfileBuffer* buffer_;
|
|
||||||
int32_t event_handle_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ScopedOperatorProfile {
|
|
||||||
public:
|
|
||||||
// Adds a profile event to profile that begins with the construction
|
|
||||||
// of object and ends when the object goes out of scope.
|
|
||||||
// The lifetime of tag should be at least the lifetime of profiler.
|
|
||||||
ScopedOperatorProfile(Profiler* profiler, const char* tag, int node_index)
|
|
||||||
: buffer_(nullptr), event_handle_(0) {
|
|
||||||
if (profiler) {
|
|
||||||
buffer_ = profiler->GetProfileBuffer();
|
|
||||||
event_handle_ = buffer_->BeginEvent(
|
|
||||||
tag, ProfileEvent::EventType::OPERATOR_INVOKE_EVENT, node_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~ScopedOperatorProfile() {
|
|
||||||
if (buffer_) {
|
|
||||||
buffer_->EndEvent(event_handle_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ProfileBuffer* buffer_;
|
|
||||||
int32_t event_handle_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace profiling
|
|
||||||
} // namespace tflite
|
|
||||||
|
|
||||||
#define VARNAME_UNIQ(name, ctr) name##ctr
|
|
||||||
|
|
||||||
#define SCOPED_TAGGED_OPERATOR_PROFILE(profiler, tag, node_index) \
|
|
||||||
tflite::profiling::ScopedOperatorProfile VARNAME_UNIQ( \
|
|
||||||
_profile_, __COUNTER__)((profiler), (tag), (node_index))
|
|
||||||
#define SCOPED_OPERATOR_PROFILE(profiler, node_index) \
|
|
||||||
SCOPED_TAGGED_OPERATOR_PROFILE((profiler), "OpInvoke", (node_index))
|
|
||||||
#else
|
#else
|
||||||
|
using Profiler = NoopProfiler;
|
||||||
namespace tflite {
|
|
||||||
namespace profiling {
|
|
||||||
// A noop version of profiler when profiling is disabled.
|
|
||||||
class Profiler {
|
|
||||||
public:
|
|
||||||
Profiler() {}
|
|
||||||
void StartProfiling() {}
|
|
||||||
void StopProfiling() {}
|
|
||||||
void Reset() {}
|
|
||||||
std::vector<const ProfileEvent*> GetProfileEvents() { return {}; }
|
|
||||||
};
|
|
||||||
} // namespace profiling
|
|
||||||
} // namespace tflite
|
|
||||||
|
|
||||||
#define SCOPED_TAGGED_OPERATOR_PROFILE(profiler, tag, node_index)
|
|
||||||
#define SCOPED_OPERATOR_PROFILE(profiler, node_index)
|
|
||||||
|
|
||||||
#endif // TFLITE_PROFILING_ENABLED
|
#endif // TFLITE_PROFILING_ENABLED
|
||||||
|
|
||||||
|
} // namespace profiling
|
||||||
|
} // namespace tflite
|
||||||
|
|
||||||
|
#define SCOPED_TAGGED_OPERATOR_PROFILE TFLITE_SCOPED_TAGGED_OPERATOR_PROFILE
|
||||||
|
#define SCOPED_OPERATOR_PROFILE TFLITE_SCOPED_OPERATOR_PROFILE
|
||||||
|
|
||||||
#endif // TENSORFLOW_LITE_PROFILING_PROFILER_H_
|
#endif // TENSORFLOW_LITE_PROFILING_PROFILER_H_
|
||||||
|
@ -31,17 +31,17 @@ double GetDurationOfEventMs(const ProfileEvent* event) {
|
|||||||
return (event->end_timestamp_us - event->begin_timestamp_us) / 1e3;
|
return (event->end_timestamp_us - event->begin_timestamp_us) / 1e3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SleepForQuarterSecond(Profiler* profiler) {
|
void SleepForQuarterSecond(tflite::Profiler* profiler) {
|
||||||
ScopedProfile profile(profiler, "SleepForQuarter");
|
ScopedProfile profile(profiler, "SleepForQuarter");
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChildFunction(Profiler* profiler) {
|
void ChildFunction(tflite::Profiler* profiler) {
|
||||||
ScopedProfile profile(profiler, "Child");
|
ScopedProfile profile(profiler, "Child");
|
||||||
SleepForQuarterSecond(profiler);
|
SleepForQuarterSecond(profiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParentFunction(Profiler* profiler) {
|
void ParentFunction(tflite::Profiler* profiler) {
|
||||||
ScopedProfile profile(profiler, "Parent");
|
ScopedProfile profile(profiler, "Parent");
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++) {
|
||||||
ChildFunction(profiler);
|
ChildFunction(profiler);
|
||||||
@ -49,14 +49,14 @@ void ParentFunction(Profiler* profiler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ProfilerTest, NoProfilesAreCollectedWhenDisabled) {
|
TEST(ProfilerTest, NoProfilesAreCollectedWhenDisabled) {
|
||||||
Profiler profiler;
|
BufferedProfiler profiler;
|
||||||
ParentFunction(&profiler);
|
ParentFunction(&profiler);
|
||||||
auto profile_events = profiler.GetProfileEvents();
|
auto profile_events = profiler.GetProfileEvents();
|
||||||
EXPECT_EQ(0, profile_events.size());
|
EXPECT_EQ(0, profile_events.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ProfilingTest, ProfilesAreCollected) {
|
TEST(ProfilingTest, ProfilesAreCollected) {
|
||||||
Profiler profiler;
|
BufferedProfiler profiler;
|
||||||
profiler.StartProfiling();
|
profiler.StartProfiling();
|
||||||
ParentFunction(&profiler);
|
ParentFunction(&profiler);
|
||||||
profiler.StopProfiling();
|
profiler.StopProfiling();
|
||||||
@ -101,7 +101,7 @@ TEST(ProfilingTest, NullProfiler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ProfilingTest, ScopedProfile) {
|
TEST(ProfilingTest, ScopedProfile) {
|
||||||
Profiler profiler;
|
BufferedProfiler profiler;
|
||||||
profiler.StartProfiling();
|
profiler.StartProfiling();
|
||||||
{ SCOPED_OPERATOR_PROFILE(&profiler, 1); }
|
{ SCOPED_OPERATOR_PROFILE(&profiler, 1); }
|
||||||
profiler.StopProfiling();
|
profiler.StopProfiling();
|
||||||
@ -109,6 +109,15 @@ TEST(ProfilingTest, ScopedProfile) {
|
|||||||
EXPECT_EQ(1, profile_events.size());
|
EXPECT_EQ(1, profile_events.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ProfilingTest, NoopProfiler) {
|
||||||
|
NoopProfiler profiler;
|
||||||
|
profiler.StartProfiling();
|
||||||
|
{ SCOPED_OPERATOR_PROFILE(&profiler, 1); }
|
||||||
|
profiler.StopProfiling();
|
||||||
|
auto profile_events = profiler.GetProfileEvents();
|
||||||
|
EXPECT_EQ(0, profile_events.size());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace profiling
|
} // namespace profiling
|
||||||
} // namespace tflite
|
} // namespace tflite
|
||||||
|
Loading…
Reference in New Issue
Block a user