Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a80649ad authored by Sanna Catherine de Treville Wager's avatar Sanna Catherine de Treville Wager
Browse files

Simplify processAndFlushTimeStampSeries

Instead first writing to a deque of short-term
histograms and copying the data over to long-term
histograms, write directly to a single deque
of histograms.

Test: dumpsys media.log
Change-Id: I05a283a54b84bc7d061c7fae7b671390c1690def
parent 424c4f5b
Loading
Loading
Loading
Loading
+36 −59
Original line number Original line Diff line number Diff line
@@ -54,16 +54,6 @@ PerformanceAnalysis::PerformanceAnalysis() {
    kPeriodMsCPU = static_cast<int>(kPeriodMs * kRatio);
    kPeriodMsCPU = static_cast<int>(kPeriodMs * kRatio);
}
}


// converts a time series into a map. key: buffer period length. value: count
static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
    // TODO allow buckets of variable resolution
    std::map<int, int> buckets;
    for (size_t i = 1; i < samples.size(); ++i) {
        ++buckets[deltaMs(samples[i - 1], samples[i])];
    }
    return buckets;
}

static int widthOf(int x) {
static int widthOf(int x) {
    int width = 0;
    int width = 0;
    while (x > 0) {
    while (x > 0) {
@@ -79,21 +69,41 @@ static int widthOf(int x) {
// small or large values and stores these as peaks, and flushes
// small or large values and stores these as peaks, and flushes
// the timestamp series from memory.
// the timestamp series from memory.
void PerformanceAnalysis::processAndFlushTimeStampSeries() {
void PerformanceAnalysis::processAndFlushTimeStampSeries() {
    if (mTimeStampSeries.empty()) {
        ALOGD("Timestamp series is empty");
        return;
    }

    // mHists is empty if program just started
    if (mHists.empty()) {
        mHists.emplace_front(static_cast<uint64_t>(mTimeStampSeries[0]),
                            std::map<int, int>());
    }

    // 1) analyze the series to store all outliers and their exact timestamps:
    // 1) analyze the series to store all outliers and their exact timestamps:
    storeOutlierData(mTimeStampSeries);
    storeOutlierData(mTimeStampSeries);


    // 2) detect peaks in the outlier series
    // 2) detect peaks in the outlier series
    detectPeaks();
    detectPeaks();


    // 3) compute its histogram, append to mRecentHists and clear the time series
    // if the current histogram has spanned its maximum time interval,
    mRecentHists.emplace_back(static_cast<timestamp>(mTimeStampSeries[0]),
    // insert a new empty histogram to the front of mHists
                              buildBuckets(mTimeStampSeries));
    if (deltaMs(mHists[0].first, mTimeStampSeries[0]) >= kMaxHistTimespanMs) {
    // do not let mRecentHists exceed capacity
        mHists.emplace_front(static_cast<uint64_t>(mTimeStampSeries[0]),
    // ALOGD("mRecentHists size: %d", static_cast<int>(mRecentHists.size()));
                             std::map<int, int>());
    if (mRecentHists.size() >= kRecentHistsCapacity) {
        // When memory is full, delete oldest histogram
        //  ALOGD("popped back mRecentHists");
        if (mHists.size() >= kHistsCapacity) {
        mRecentHists.pop_front();
            mHists.resize(kHistsCapacity);
        }
    }

    // 3) add current time intervals to histogram
    for (size_t i = 1; i < mTimeStampSeries.size(); ++i) {
        ++mHists[0].second[deltaMs(
                mTimeStampSeries[i - 1], mTimeStampSeries[i])];
    }
    }

    // clear the timestamps
    mTimeStampSeries.clear();
    mTimeStampSeries.clear();
}
}


@@ -116,48 +126,14 @@ void PerformanceAnalysis::logTsEntry(int64_t ts) {
    mTimeStampSeries.push_back(ts);
    mTimeStampSeries.push_back(ts);
    // if length of the time series has reached kShortHistSize samples,
    // if length of the time series has reached kShortHistSize samples,
    // analyze the data and flush the timestamp series from memory
    // analyze the data and flush the timestamp series from memory
    if (mTimeStampSeries.size() >= kShortHistSize) {
    if (mTimeStampSeries.size() >= kHistSize) {
        processAndFlushTimeStampSeries();
        processAndFlushTimeStampSeries();
    }
    }
}
}


// When the short-term histogram array mRecentHists has reached capacity,
// TODO: move this someplace
// merge histograms for data compression and store them in mLongTermHists
// static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt";
// clears mRecentHists
//    writeToFile(mOutlierData, mLongTermHists, kName, false);
// TODO: have logTsEntry write directly to mLongTermHists, discard mRecentHists,
// start a new histogram when a peak occurs
void PerformanceAnalysis::processAndFlushRecentHists() {

    // Buckets is used to aggregate short-term histograms.
    Histogram buckets;
    timestamp startingTs = mRecentHists[0].first;

    for (const auto &shortHist: mRecentHists) {
        // If the time between starting and ending timestamps has reached the maximum,
        // add the current histogram (buckets) to the long-term histogram buffer,
        // clear buckets, and start a new long-term histogram aggregation process.
        if (deltaMs(startingTs, shortHist.first) >= kMaxHistTimespanMs) {
            mLongTermHists.emplace_back(startingTs, std::move(buckets));
            buckets.clear();
            startingTs = shortHist.first;
            // When memory is full, delete oldest histogram
            // TODO use a circular buffer
            if (mLongTermHists.size() >= kLongTermHistsCapacity) {
                mLongTermHists.pop_front();
            }
        }

        // add current histogram to buckets
        for (const auto &countPair : shortHist.second) {
            buckets[countPair.first] += countPair.second;
        }
    }
    mRecentHists.clear();
    // TODO: decide when/where to call writeToFile
    // TODO: add a thread-specific extension to the file name
    static const char* const kName = (const char *) "/data/misc/audioserver/sample_results.txt";
    writeToFile(mOutlierData, mLongTermHists, kName, false);
}


// Given a series of outlier intervals (mOutlier data),
// Given a series of outlier intervals (mOutlier data),
// looks for changes in distribution (peaks), which can be either positive or negative.
// looks for changes in distribution (peaks), which can be either positive or negative.
@@ -267,13 +243,14 @@ void PerformanceAnalysis::testFunction() {
// TODO consider changing all ints to uint32_t or uint64_t
// TODO consider changing all ints to uint32_t or uint64_t
// TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis
// TODO: move this to ReportPerformance, probably make it a friend function of PerformanceAnalysis
void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
void PerformanceAnalysis::reportPerformance(String8 *body, int maxHeight) {
    if (mRecentHists.size() < 1) {
    if (mHists.empty()) {
        ALOGD("reportPerformance: mRecentHists is empty");
        ALOGD("reportPerformance: mHists is empty");
        return;
        return;
    }
    }
    ALOGD("reportPerformance: hists size %d", static_cast<int>(mHists.size()));
    // TODO: more elaborate data analysis
    // TODO: more elaborate data analysis
    std::map<int, int> buckets;
    std::map<int, int> buckets;
    for (const auto &shortHist: mRecentHists) {
    for (const auto &shortHist: mHists) {
        for (const auto &countPair : shortHist.second) {
        for (const auto &countPair : shortHist.second) {
            buckets[countPair.first] += countPair.second;
            buckets[countPair.first] += countPair.second;
        }
        }
+12 −17
Original line number Original line Diff line number Diff line
@@ -43,16 +43,17 @@ public:
    // the timestamp series from memory.
    // the timestamp series from memory.
    void processAndFlushTimeStampSeries();
    void processAndFlushTimeStampSeries();


    // Given a series of audio processing wakeup timestamps,
    // compresses and and analyzes the data, and flushes
    // the timestamp series from memory.
    void processAndFlushTimeStampSeriesOld();

    // Called when an audio on/off event is read from the buffer,
    // Called when an audio on/off event is read from the buffer,
    // e.g. EVENT_AUDIO_STATE.
    // e.g. EVENT_AUDIO_STATE.
    // calls flushTimeStampSeries on the data up to the event,
    // calls flushTimeStampSeries on the data up to the event,
    // effectively discarding the idle audio time interval
    // effectively discarding the idle audio time interval
    void handleStateChange();
    void handleStateChange();


    // When the short-term histogram array mRecentHists has reached capacity,
    // merges histograms for data compression and stores them in mLongTermHists
    void processAndFlushRecentHists();

    // Writes wakeup timestamp entry to log and runs analysis
    // Writes wakeup timestamp entry to log and runs analysis
    // TODO: make this thread safe. Each thread should have its own instance
    // TODO: make this thread safe. Each thread should have its own instance
    // of PerformanceAnalysis.
    // of PerformanceAnalysis.
@@ -90,16 +91,11 @@ private:
    // a peak is a moment at which the average outlier interval changed significantly
    // a peak is a moment at which the average outlier interval changed significantly
    std::deque<timestamp> mPeakTimestamps;
    std::deque<timestamp> mPeakTimestamps;


    // TODO: turn these into circular buffers for better data flow
    // stores stores buffer period histograms with timestamp of first sample
    // FIFO of small histograms
    // TODO use a circular buffer
    // stores fixed-size short buffer period histograms with timestamp of first sample
    std::deque<std::pair<timestamp, Histogram>> mHists;
    std::deque<std::pair<timestamp, Histogram>> mRecentHists;

    // FIFO of small histograms
    // stores fixed-size long-term buffer period histograms with timestamp of first sample
    std::deque<std::pair<timestamp, Histogram>> mLongTermHists;


    // vector of timestamps, collected from NBLog for a (TODO) specific thread
    // vector of timestamps, collected from NBLog for a specific thread
    // when a vector reaches its maximum size, the data is processed and flushed
    // when a vector reaches its maximum size, the data is processed and flushed
    std::vector<timestamp_raw> mTimeStampSeries;
    std::vector<timestamp_raw> mTimeStampSeries;


@@ -119,12 +115,11 @@ private:
    static const int kStddevThreshold = 5;
    static const int kStddevThreshold = 5;


    // capacity allocated to data structures
    // capacity allocated to data structures
    // TODO: adjust all of these values
    // TODO: make these values longer when testing is finished
    static const int kRecentHistsCapacity = 100; // number of short-term histograms stored in memory
    static const int kHistsCapacity = 20; // number of short-term histograms stored in memory
    static const int kShortHistSize = 50; // number of samples in a short-term histogram
    static const int kHistSize = 1000; // max number of samples stored in a histogram
    static const int kOutlierSeriesSize = 100; // number of values stored in outlier array
    static const int kOutlierSeriesSize = 100; // number of values stored in outlier array
    static const int kPeakSeriesSize = 100; // number of values stored in peak array
    static const int kPeakSeriesSize = 100; // number of values stored in peak array
    static const int kLongTermHistsCapacity = 20; // number of long-term histogram stored in memory
    // maximum elapsed time between first and last timestamp of a long-term histogram
    // maximum elapsed time between first and last timestamp of a long-term histogram
    static const int kMaxHistTimespanMs = 5 * kMsPerSec;
    static const int kMaxHistTimespanMs = 5 * kMsPerSec;