| /* |
| * 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 |