NNAPI Usage telemetry
Added basic data collection in the NNAPI runtime in preparation for
statsd and clearcut based telemetry. Added two types of data collection:
one for compilation, and one for execution.
There's missing functionallity that will be addressed in further CLs:
- Extraction of package/binary name from PackageManager.
- Apex version added to atoms.
- Model hash generation
- Execution and compilation time measurements.
Bug: 173392665
Test: mma, run TestTelemetry.*
Change-Id: I6bea18b66ebcd91bc73971912441e42c9c6d6a47
diff --git a/runtime/ExecutionBuilder.cpp b/runtime/ExecutionBuilder.cpp
index f5b74b6..7bbf566 100644
--- a/runtime/ExecutionBuilder.cpp
+++ b/runtime/ExecutionBuilder.cpp
@@ -44,6 +44,7 @@
#include "Manager.h"
#include "ModelArgumentInfo.h"
#include "ModelBuilder.h"
+#include "Telemetry.h"
#include "TypeManager.h"
namespace android {
@@ -585,12 +586,12 @@
return mOutputsFullySpecified.value();
}
-int ExecutionBuilder::prepareForCompute(const char* name) {
+int ExecutionBuilder::prepareForCompute(const char* name, ExecutionMode mode) {
if (!checkAndSetComputationState(name)) {
return ANEURALNETWORKS_BAD_STATE;
}
if (int n = getValidationResultCode(); n != ANEURALNETWORKS_NO_ERROR) {
- return finishComputation(n, {});
+ return finishComputation(n, {}, mode);
}
return ANEURALNETWORKS_NO_ERROR;
}
@@ -972,7 +973,8 @@
int ExecutionBuilder::computeFenced(const std::vector<int>& waitFor,
uint64_t timeoutDurationAfterFence, int* syncFence) {
CHECK(syncFence != nullptr);
- NN_RETURN_IF_ERROR(prepareForCompute("startComputeWithDependencies"));
+ NN_RETURN_IF_ERROR(
+ prepareForCompute("startComputeWithDependencies", ExecutionMode::ASYNC_WITH_DEPS));
if (timeoutDurationAfterFence > 0) {
if (!mCompilation->mExplicitDeviceList || (mCompilation->mDevices.size() != 1)) {
LOG(ERROR)
@@ -980,13 +982,13 @@
"duration on an ANeuralNetworksExecution "
"created from an ANeuralNetworksCompilation that was not created by "
"ANeuralNetworksCompilation_createForDevices with numDevices = 1";
- return finishComputation(ANEURALNETWORKS_BAD_DATA, {});
+ return finishComputation(ANEURALNETWORKS_BAD_DATA, {}, ExecutionMode::ASYNC_WITH_DEPS);
}
}
if (!areOutputsFullySpecified()) {
LOG(ERROR) << "ANeuralNetworksExecution_startComputeWithDependencies"
" not all outputs have fully specified dimensions";
- return finishComputation(ANEURALNETWORKS_BAD_DATA, {});
+ return finishComputation(ANEURALNETWORKS_BAD_DATA, {}, ExecutionMode::ASYNC_WITH_DEPS);
}
// Unlike ExecutionBuilder::compute, we do not need to reset output dimensions here because
@@ -1002,7 +1004,7 @@
if (result != ANEURALNETWORKS_NO_ERROR) {
// TODO(miaowang): support dynamic output shape only with memory domain.
// For now just return empty output shapes.
- result = finishComputation(result, {});
+ result = finishComputation(result, {}, ExecutionMode::ASYNC_WITH_DEPS);
}
return result;
}
@@ -1018,7 +1020,10 @@
}
const char* name = burstBuilder ? "burstCompute" : synchronous ? "compute" : "startCompute";
- NN_RETURN_IF_ERROR(prepareForCompute(name));
+ const ExecutionMode mode = burstBuilder
+ ? ExecutionMode::BURST
+ : synchronous ? ExecutionMode::SYNC : ExecutionMode::ASYNC;
+ NN_RETURN_IF_ERROR(prepareForCompute(name, mode));
// Validate input memory dimensions. We need to do the validation in every computation because
// the memory dimensions may change between computations.
@@ -1026,7 +1031,7 @@
if (p.state() == ModelArgumentInfo::MEMORY) {
const RuntimeMemory* memory = mMemories[p.locationAndLength().poolIndex];
if (!memory->getValidator().validateInputDimensions(p.dimensions())) {
- return finishComputation(ANEURALNETWORKS_OP_FAILED, {});
+ return finishComputation(ANEURALNETWORKS_OP_FAILED, {}, mode);
}
}
}
@@ -1049,7 +1054,7 @@
if (mMeasureTiming) {
mTimingWithoutFencedExecutionCallback = timing;
}
- return finishComputation(n, outputShapes);
+ return finishComputation(n, outputShapes, mode);
} else /* asynchronous */ {
// TODO: For asynchronous execution, entire plan-based-path should run in an
// asynchronous thread -- take the asynchronous thread logic out of
@@ -1066,8 +1071,8 @@
// abstracted in the NN API as an "event".
auto executionCallback = std::make_shared<ExecutionCallback>();
executionCallback->setOnFinish(
- [this](ErrorStatus error, const std::vector<OutputShape>& outputShapes) {
- return finishComputation(error, outputShapes);
+ [this, mode](ErrorStatus error, const std::vector<OutputShape>& outputShapes) {
+ return finishComputation(error, outputShapes, mode);
});
const auto asyncStartCompute = [this, deadline, executionCallback] {
const auto [n, outputShapes, timing] = computeInternal(deadline, nullptr);
@@ -1149,7 +1154,8 @@
return true;
}
-int ExecutionBuilder::finishComputation(int result, const std::vector<OutputShape>& outputShapes) {
+int ExecutionBuilder::finishComputation(int result, const std::vector<OutputShape>& outputShapes,
+ ExecutionMode mode) {
const auto status = convertResultCodeToErrorStatus(result);
if (!updateOutputShapes(status, outputShapes) || !updateMemories()) {
result = ANEURALNETWORKS_OP_FAILED;
@@ -1178,6 +1184,7 @@
CHECK(mState != State::COMPLETED) << "ExecutionBuilder::finishComputation is called twice";
mState = State::COMPLETED;
}
+ telemetry::onExecutionFinish(this, mode, result);
return result;
}