Update CPU monitor to use moving averages.
And improve accuracy a little. BUG=b/28560555 R=wzh@webrtc.org Review URL: https://codereview.webrtc.org/1951663002 . Cr-Commit-Position: refs/heads/master@{#12631}
This commit is contained in:
@ -14,19 +14,14 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
import org.appspot.apprtc.util.LooperExecutor;
|
import org.appspot.apprtc.util.LooperExecutor;
|
||||||
@ -75,35 +70,34 @@ import org.appspot.apprtc.util.LooperExecutor;
|
|||||||
|
|
||||||
class CpuMonitor {
|
class CpuMonitor {
|
||||||
private static final String TAG = "CpuMonitor";
|
private static final String TAG = "CpuMonitor";
|
||||||
private static final String DUMP_FILE = "cpu_log.txt";
|
private static final int MOVING_AVERAGE_SAMPLES = 10;
|
||||||
private static final int CPU_STAT_SAMPLE_PERIOD = 2000;
|
|
||||||
private static final int CPU_STAT_LOG_PERIOD = 6000;
|
private static final int CPU_STAT_SAMPLE_PERIOD_MS = 1000;
|
||||||
|
private static final int CPU_STAT_LOG_PERIOD_MS = 5000;
|
||||||
|
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
|
// User CPU usage at current frequency.
|
||||||
|
private final MovingAverage userCpuUsage;
|
||||||
|
// System CPU usage at current frequency.
|
||||||
|
private final MovingAverage systemCpuUsage;
|
||||||
|
// Total CPU usage relative to maximum frequency.
|
||||||
|
private final MovingAverage totalCpuUsage;
|
||||||
|
// CPU frequency in percentage from maximum.
|
||||||
|
private final MovingAverage frequencyScale;
|
||||||
|
|
||||||
private LooperExecutor executor;
|
private LooperExecutor executor;
|
||||||
private long lastStatLogTimeMs;
|
private long lastStatLogTimeMs;
|
||||||
private int iterations;
|
|
||||||
private double currentUserCpuUsage;
|
|
||||||
private double currentSystemCpuUsage;
|
|
||||||
private double currentTotalCpuUsage;
|
|
||||||
private double currentFrequencyScale = -1;
|
|
||||||
private double sumUserCpuUsage;
|
|
||||||
private double sumSystemCpuUsage;
|
|
||||||
private double sumFrequencyScale;
|
|
||||||
private double sumTotalCpuUsage;
|
|
||||||
private long[] cpuFreqMax;
|
private long[] cpuFreqMax;
|
||||||
private int cpusPresent;
|
private int cpusPresent;
|
||||||
private int actualCpusPresent;
|
private int actualCpusPresent;
|
||||||
private boolean initialized = false;
|
private boolean initialized;
|
||||||
|
private boolean cpuOveruse;
|
||||||
private String[] maxPath;
|
private String[] maxPath;
|
||||||
private String[] curPath;
|
private String[] curPath;
|
||||||
private double[] curFreqScales;
|
private double[] curFreqScales;
|
||||||
private ProcStat lastProcStat;
|
private ProcStat lastProcStat;
|
||||||
|
|
||||||
private static boolean dumpEnabled = false;
|
private static class ProcStat {
|
||||||
private static FileOutputStream fileWriter;
|
|
||||||
|
|
||||||
private class ProcStat {
|
|
||||||
final long userTime;
|
final long userTime;
|
||||||
final long systemTime;
|
final long systemTime;
|
||||||
final long idleTime;
|
final long idleTime;
|
||||||
@ -115,10 +109,55 @@ class CpuMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MovingAverage {
|
||||||
|
private final int size;
|
||||||
|
private double sum;
|
||||||
|
private double currentValue;
|
||||||
|
private double circBuffer[];
|
||||||
|
private int circBufferIndex;
|
||||||
|
|
||||||
|
public MovingAverage(int size) {
|
||||||
|
if (size <= 0) {
|
||||||
|
throw new AssertionError("Size value in MovingAverage ctor should be positive.");
|
||||||
|
}
|
||||||
|
this.size = size;
|
||||||
|
circBuffer = new double[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
Arrays.fill(circBuffer, 0);
|
||||||
|
circBufferIndex = 0;
|
||||||
|
sum = 0;
|
||||||
|
currentValue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValue(double value) {
|
||||||
|
sum -= circBuffer[circBufferIndex];
|
||||||
|
circBuffer[circBufferIndex++] = value;
|
||||||
|
currentValue = value;
|
||||||
|
sum += value;
|
||||||
|
if (circBufferIndex >= size) {
|
||||||
|
circBufferIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCurrent() {
|
||||||
|
return currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAverage() {
|
||||||
|
return sum / (double) size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public CpuMonitor(Context context) {
|
public CpuMonitor(Context context) {
|
||||||
Log.d(TAG, "CpuMonitor ctor.");
|
Log.d(TAG, "CpuMonitor ctor.");
|
||||||
appContext = context.getApplicationContext();
|
appContext = context.getApplicationContext();
|
||||||
lastStatLogTimeMs = 0;
|
userCpuUsage = new MovingAverage(MOVING_AVERAGE_SAMPLES);
|
||||||
|
systemCpuUsage = new MovingAverage(MOVING_AVERAGE_SAMPLES);
|
||||||
|
totalCpuUsage = new MovingAverage(MOVING_AVERAGE_SAMPLES);
|
||||||
|
frequencyScale = new MovingAverage(MOVING_AVERAGE_SAMPLES);
|
||||||
|
lastStatLogTimeMs = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
executor = new LooperExecutor();
|
executor = new LooperExecutor();
|
||||||
executor.requestStart();
|
executor.requestStart();
|
||||||
@ -149,67 +188,42 @@ class CpuMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void reset() {
|
||||||
|
if (executor != null) {
|
||||||
|
Log.d(TAG, "reset");
|
||||||
|
resetStat();
|
||||||
|
cpuOveruse = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized int getCpuUsageCurrent() {
|
public synchronized int getCpuUsageCurrent() {
|
||||||
return doubleToPercent(currentTotalCpuUsage);
|
return doubleToPercent(userCpuUsage.getCurrent() + systemCpuUsage.getCurrent());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getCpuUsageAverage() {
|
public synchronized int getCpuUsageAverage() {
|
||||||
return sumDoubleToPercent(sumTotalCpuUsage, iterations);
|
return doubleToPercent(userCpuUsage.getAverage() + systemCpuUsage.getAverage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int getCpuFrequencyScaleCurrent() {
|
public synchronized int getFrequencyScaleAverage() {
|
||||||
return doubleToPercent(currentFrequencyScale);
|
return doubleToPercent(frequencyScale.getAverage());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleCpuUtilizationTask() {
|
private void scheduleCpuUtilizationTask() {
|
||||||
executor.scheduleAtFixedRate(new Runnable() {
|
executor.scheduleAtFixedRate(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
logCpuUtilization();
|
cpuUtilizationTask();
|
||||||
}
|
}
|
||||||
}, CPU_STAT_SAMPLE_PERIOD);
|
}, CPU_STAT_SAMPLE_PERIOD_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkDump(String statString) {
|
private void cpuUtilizationTask() {
|
||||||
if (!dumpEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fileWriter == null) {
|
|
||||||
Log.d(TAG, "Start log dump");
|
|
||||||
String fileName = Environment.getExternalStorageDirectory().getAbsolutePath()
|
|
||||||
+ File.separator + DUMP_FILE;
|
|
||||||
try {
|
|
||||||
fileWriter = new FileOutputStream(fileName, false /* append */);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.e(TAG, "Can not open file.", e);
|
|
||||||
dumpEnabled = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Date date = Calendar.getInstance().getTime();
|
|
||||||
SimpleDateFormat df = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
|
|
||||||
String msg = df.format(date) + " " + TAG + ":" + statString + "\n";
|
|
||||||
try {
|
|
||||||
fileWriter.write(msg.getBytes());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Can not write to file.", e);
|
|
||||||
dumpEnabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logCpuUtilization() {
|
|
||||||
boolean logStatistics = false;
|
|
||||||
if (SystemClock.elapsedRealtime() - lastStatLogTimeMs >= CPU_STAT_LOG_PERIOD) {
|
|
||||||
lastStatLogTimeMs = SystemClock.elapsedRealtime();
|
|
||||||
logStatistics = true;
|
|
||||||
}
|
|
||||||
boolean cpuMonitorAvailable = sampleCpuUtilization();
|
boolean cpuMonitorAvailable = sampleCpuUtilization();
|
||||||
if (logStatistics && cpuMonitorAvailable) {
|
if (cpuMonitorAvailable
|
||||||
|
&& SystemClock.elapsedRealtime() - lastStatLogTimeMs >= CPU_STAT_LOG_PERIOD_MS) {
|
||||||
|
lastStatLogTimeMs = SystemClock.elapsedRealtime();
|
||||||
String statString = getStatString();
|
String statString = getStatString();
|
||||||
checkDump(statString);
|
|
||||||
Log.d(TAG, statString);
|
Log.d(TAG, statString);
|
||||||
resetStat();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,11 +265,11 @@ class CpuMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void resetStat() {
|
private synchronized void resetStat() {
|
||||||
sumUserCpuUsage = 0;
|
userCpuUsage.reset();
|
||||||
sumSystemCpuUsage = 0;
|
systemCpuUsage.reset();
|
||||||
sumFrequencyScale = 0;
|
totalCpuUsage.reset();
|
||||||
sumTotalCpuUsage = 0;
|
frequencyScale.reset();
|
||||||
iterations = 0;
|
lastStatLogTimeMs = SystemClock.elapsedRealtime();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getBatteryLevel() {
|
private int getBatteryLevel() {
|
||||||
@ -305,6 +319,7 @@ class CpuMonitor {
|
|||||||
// We have never found this CPU's max frequency. Attempt to read it.
|
// We have never found this CPU's max frequency. Attempt to read it.
|
||||||
long cpufreqMax = readFreqFromFile(maxPath[i]);
|
long cpufreqMax = readFreqFromFile(maxPath[i]);
|
||||||
if (cpufreqMax > 0) {
|
if (cpufreqMax > 0) {
|
||||||
|
Log.d(TAG, "Core " + i + ". Max frequency: " + cpufreqMax);
|
||||||
lastSeenMaxFreq = cpufreqMax;
|
lastSeenMaxFreq = cpufreqMax;
|
||||||
cpuFreqMax[i] = cpufreqMax;
|
cpuFreqMax[i] = cpufreqMax;
|
||||||
maxPath[i] = null; // Kill path to free its memory.
|
maxPath[i] = null; // Kill path to free its memory.
|
||||||
@ -347,12 +362,9 @@ class CpuMonitor {
|
|||||||
* incorrect only if the frequencies have peeked or dropped in between the
|
* incorrect only if the frequencies have peeked or dropped in between the
|
||||||
* invocations.
|
* invocations.
|
||||||
*/
|
*/
|
||||||
double newFrequencyScale = (double) cpuFreqCurSum / cpuFreqMaxSum;
|
double currentFrequencyScale = cpuFreqCurSum / (double) cpuFreqMaxSum;
|
||||||
double frequencyScale;
|
if (frequencyScale.getCurrent() > 0) {
|
||||||
if (currentFrequencyScale > 0) {
|
currentFrequencyScale = (frequencyScale.getCurrent() + currentFrequencyScale) * 0.5;
|
||||||
frequencyScale = (currentFrequencyScale + newFrequencyScale) * 0.5;
|
|
||||||
} else {
|
|
||||||
frequencyScale = newFrequencyScale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcStat procStat = readProcStat();
|
ProcStat procStat = readProcStat();
|
||||||
@ -365,24 +377,23 @@ class CpuMonitor {
|
|||||||
long diffIdleTime = procStat.idleTime - lastProcStat.idleTime;
|
long diffIdleTime = procStat.idleTime - lastProcStat.idleTime;
|
||||||
long allTime = diffUserTime + diffSystemTime + diffIdleTime;
|
long allTime = diffUserTime + diffSystemTime + diffIdleTime;
|
||||||
|
|
||||||
if (frequencyScale == 0 || allTime == 0) {
|
if (currentFrequencyScale == 0 || allTime == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update statistics.
|
// Update statistics.
|
||||||
currentFrequencyScale = frequencyScale;
|
frequencyScale.addValue(currentFrequencyScale);
|
||||||
sumFrequencyScale += frequencyScale;
|
|
||||||
|
|
||||||
currentUserCpuUsage = (double) diffUserTime / allTime;
|
double currentUserCpuUsage = diffUserTime / (double) allTime;
|
||||||
sumUserCpuUsage += currentUserCpuUsage;
|
userCpuUsage.addValue(currentUserCpuUsage);
|
||||||
|
|
||||||
currentSystemCpuUsage = (double) diffSystemTime / allTime;
|
double currentSystemCpuUsage = diffSystemTime / (double) allTime;
|
||||||
sumSystemCpuUsage += currentSystemCpuUsage;
|
systemCpuUsage.addValue(currentSystemCpuUsage);
|
||||||
|
|
||||||
currentTotalCpuUsage = (currentUserCpuUsage + currentSystemCpuUsage) * currentFrequencyScale;
|
double currentTotalCpuUsage =
|
||||||
sumTotalCpuUsage += currentTotalCpuUsage;
|
(currentUserCpuUsage + currentSystemCpuUsage) * currentFrequencyScale;
|
||||||
|
totalCpuUsage.addValue(currentTotalCpuUsage);
|
||||||
|
|
||||||
iterations++;
|
|
||||||
// Save new measurements for next round's deltas.
|
// Save new measurements for next round's deltas.
|
||||||
lastProcStat = procStat;
|
lastProcStat = procStat;
|
||||||
|
|
||||||
@ -393,38 +404,30 @@ class CpuMonitor {
|
|||||||
return (int) (d * 100 + 0.5);
|
return (int) (d * 100 + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int sumDoubleToPercent(double d, int iterations) {
|
private synchronized String getStatString() {
|
||||||
if (iterations > 0) {
|
|
||||||
return (int) (d * 100.0 / (double) iterations + 0.5);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getStatString() {
|
|
||||||
StringBuilder stat = new StringBuilder();
|
StringBuilder stat = new StringBuilder();
|
||||||
stat.append("CPU User: ")
|
stat.append("CPU User: ")
|
||||||
.append(doubleToPercent(currentUserCpuUsage)).append("/")
|
.append(doubleToPercent(userCpuUsage.getCurrent())).append("/")
|
||||||
.append(sumDoubleToPercent(sumUserCpuUsage, iterations)).append(" (")
|
.append(doubleToPercent(userCpuUsage.getAverage()))
|
||||||
.append(doubleToPercent(currentUserCpuUsage * currentFrequencyScale)).append(")")
|
|
||||||
.append(". System: ")
|
.append(". System: ")
|
||||||
.append(doubleToPercent(currentSystemCpuUsage)).append("/")
|
.append(doubleToPercent(systemCpuUsage.getCurrent())).append("/")
|
||||||
.append(sumDoubleToPercent(sumSystemCpuUsage, iterations)).append(" (")
|
.append(doubleToPercent(systemCpuUsage.getAverage()))
|
||||||
.append(doubleToPercent(currentSystemCpuUsage * currentFrequencyScale)).append(")")
|
.append(". Freq: ")
|
||||||
.append(". CPU freq %: ")
|
.append(doubleToPercent(frequencyScale.getCurrent())).append("/")
|
||||||
.append(doubleToPercent(currentFrequencyScale)).append("/")
|
.append(doubleToPercent(frequencyScale.getAverage()))
|
||||||
.append(sumDoubleToPercent(sumFrequencyScale, iterations))
|
.append(". Total usage: ")
|
||||||
.append(". Total CPU usage: ")
|
.append(doubleToPercent(totalCpuUsage.getCurrent())).append("/")
|
||||||
.append(doubleToPercent(currentTotalCpuUsage)).append("/")
|
.append(doubleToPercent(totalCpuUsage.getAverage()))
|
||||||
.append(sumDoubleToPercent(sumTotalCpuUsage, iterations))
|
|
||||||
.append(". Cores: ")
|
.append(". Cores: ")
|
||||||
.append(actualCpusPresent);
|
.append(actualCpusPresent);
|
||||||
stat.append("( ");
|
stat.append("( ");
|
||||||
for (int i = 0; i < cpusPresent; i++) {
|
for (int i = 0; i < cpusPresent; i++) {
|
||||||
stat.append(doubleToPercent(curFreqScales[i])).append(" ");
|
stat.append(doubleToPercent(curFreqScales[i])).append(" ");
|
||||||
}
|
}
|
||||||
stat.append("). Battery %: ")
|
stat.append("). Battery: ").append(getBatteryLevel());
|
||||||
.append(getBatteryLevel());
|
if (cpuOveruse) {
|
||||||
|
stat.append(". Overuse.");
|
||||||
|
}
|
||||||
return stat.toString();
|
return stat.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,13 +471,15 @@ class CpuMonitor {
|
|||||||
BufferedReader rdr = new BufferedReader(fin);
|
BufferedReader rdr = new BufferedReader(fin);
|
||||||
Scanner scanner = new Scanner(rdr);
|
Scanner scanner = new Scanner(rdr);
|
||||||
scanner.next();
|
scanner.next();
|
||||||
userTime = scanner.nextLong();
|
userTime = scanner.nextLong(); // user
|
||||||
long nice = scanner.nextLong();
|
long nice = scanner.nextLong(); // nice
|
||||||
userTime += nice;
|
userTime += nice;
|
||||||
systemTime = scanner.nextLong();
|
systemTime = scanner.nextLong(); // system
|
||||||
idleTime = scanner.nextLong();
|
idleTime = scanner.nextLong(); // idle
|
||||||
long ioWaitTime = scanner.nextLong();
|
long ioWaitTime = scanner.nextLong(); // iowait
|
||||||
userTime += ioWaitTime;
|
userTime += ioWaitTime;
|
||||||
|
long irqTime = scanner.nextLong() + scanner.nextLong(); // irq + softirq
|
||||||
|
systemTime += irqTime;
|
||||||
scanner.close();
|
scanner.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Problems parsing /proc/stat");
|
Log.e(TAG, "Problems parsing /proc/stat");
|
||||||
|
|||||||
@ -194,8 +194,10 @@ public class HudFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cpuMonitor != null) {
|
if (cpuMonitor != null) {
|
||||||
encoderStat.append("CPU%: ").append(cpuMonitor.getCpuUsageCurrent())
|
encoderStat.append("CPU%: ")
|
||||||
.append(". Freq: ").append(cpuMonitor.getCpuFrequencyScaleCurrent());
|
.append(cpuMonitor.getCpuUsageCurrent()).append("/")
|
||||||
|
.append(cpuMonitor.getCpuUsageAverage())
|
||||||
|
.append(". Freq: ").append(cpuMonitor.getFrequencyScaleAverage());
|
||||||
}
|
}
|
||||||
encoderStatView.setText(encoderStat.toString());
|
encoderStatView.setText(encoderStat.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user