blob: d9cf2c6567f3be00697373b037b1a54f25c09c89 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* 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 ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TELEMETRY_STATSD_H
#define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TELEMETRY_STATSD_H
#include <android-base/thread_annotations.h>
#include <array>
#include <condition_variable>
#include <limits>
#include <map>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "Telemetry.h"
namespace android::nn::telemetry {
using ModelArchHash = std::array<uint8_t, BYTE_SIZE_OF_MODEL_ARCH_HASH>;
constexpr int64_t kSumTimeDefault = 0;
constexpr int64_t kMinTimeDefault = std::numeric_limits<int64_t>::max();
constexpr int64_t kMaxTimeDefault = std::numeric_limits<int64_t>::min();
// For CompilationCompleted: isExecution = false, executionMode = SYNC, errorCode = 0
// For CompilationFailed: isExecution = false, executionMode = SYNC, errorCode != 0
// For ExecutionCompleted: isExecution = true, errorCode = 0, fallbackToCpuFromError = false
// For ExecutionFailed: isExecution = true, errorCode != 0, fallbackToCpuFromError = false
struct AtomKey {
bool isExecution;
ModelArchHash modelArchHash;
std::string deviceId;
ExecutionMode executionMode;
int32_t errorCode;
DataClass inputDataClass;
DataClass outputDataClass;
bool fallbackToCpuFromError;
bool introspectionEnabled;
bool cacheEnabled;
bool hasControlFlow;
bool hasDynamicTemporaries;
};
bool operator==(const AtomKey& lhs, const AtomKey& rhs);
bool operator<(const AtomKey& lhs, const AtomKey& rhs);
// For CompilationCompleted, all timings except compilationTimeMillis omitted
// For CompilationFailed, all timings omitted
// For ExecutionCompleted, compilationTimeMillis timing omitted
// For ExecutionFailed, all timings omitted
struct AtomValue {
int32_t count = 0;
// AccumulatedTiming stores all the information needed to calculate the average, min, max, and
// standard deviation of all the accumulated timings. When count == 0, AccumulatedTiming is
// ignored. When count > 0:
// * average = sumTime / count
// * minimum = minTime
// * maximum = maxTime
// * variance = sumSquaredTime / count - average * average
// * standard deviation = sqrt(variance)
// * sample standard deviation = sqrt(variance * count / (count - 1))
struct AccumulatedTiming {
int64_t sumTime = kSumTimeDefault;
int64_t minTime = kMinTimeDefault;
int64_t maxTime = kMaxTimeDefault;
// Sum of each squared timing, e.g.: t1^2 + t2^2 + ... + tn^2
int64_t sumSquaredTime = kSumTimeDefault;
int32_t count = 0;
};
AccumulatedTiming compilationTimeMillis;
AccumulatedTiming durationRuntimeMicros;
AccumulatedTiming durationDriverMicros;
AccumulatedTiming durationHardwareMicros;
};
void combineAtomValues(AtomValue* acculatedValue, const AtomValue& value);
// Atom type to be sent to Statsd Telemetry
using Atom = std::pair<AtomKey, AtomValue>;
// Helper class to locally aggregate and retrieve telemetry atoms.
class AtomAggregator {
public:
bool empty() const;
void push(Atom&& atom);
// Precondition: !empty()
Atom pop();
private:
std::map<AtomKey, AtomValue> mAggregate;
// Pointer to keys of mAggregate to ensure atoms are logged in a fair order. Using pointers into
// a std::map is guaranteed to work because references to elements are guaranteed to be valid
// until that element is erased.
std::queue<const AtomKey*> mOrder;
};
using LoggerFn = std::function<void(Atom&&)>;
// AsyncLogger minimizes the call to `write`, so that the calling thread which handles the
// compilation or execution is not slowed down by writing to statsd. Instead, AsyncLogger
// contains a dedicated thread that will handle logging to statsd in the background.
// This class is thread-safe.
class AsyncLogger {
public:
AsyncLogger(LoggerFn logger, Duration loggingQuietPeriodDuration);
AsyncLogger(const AsyncLogger&) = delete;
AsyncLogger(AsyncLogger&&) = delete;
AsyncLogger& operator=(const AsyncLogger&) = delete;
AsyncLogger& operator=(AsyncLogger&&) = delete;
~AsyncLogger();
void write(Atom&& atom);
private:
enum class Result {
SUCCESS,
TEARDOWN,
};
// Precondition: output != nullptr
// Precondition: output->empty()
Result takeAll(std::vector<Atom>* output, bool blockUntilDataIsAvailable);
Result sleepFor(Duration duration);
mutable std::mutex mMutex;
mutable std::condition_variable mNotEmptyOrTeardown;
mutable std::vector<Atom> mChannel GUARDED_BY(mMutex);
mutable bool mTeardown GUARDED_BY(mMutex) = false;
std::thread mThread;
};
// Create an Atom from a diagnostic info object.
Atom createAtomFrom(const DiagnosticCompilationInfo* info);
Atom createAtomFrom(const DiagnosticExecutionInfo* info);
// Log an Atom to statsd from a diagnostic info object.
void logCompilationToStatsd(const DiagnosticCompilationInfo* info);
void logExecutionToStatsd(const DiagnosticExecutionInfo* info);
} // namespace android::nn::telemetry
#endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TELEMETRY_STATSD_H