aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Burk <philburk@mobileer.com>2023-11-22 13:58:19 -0800
committerGitHub <noreply@github.com>2023-11-22 13:58:19 -0800
commit0f945983d6b29f15c065f6c261b35548e80fbc50 (patch)
treee935799342750f53ecaf428ec62baa9b0cce0a7b
parentf6aa353e551186dec14773540a597c0b38d9484c (diff)
downloadoboe-0f945983d6b29f15c065f6c261b35548e80fbc50.tar.gz
OboeTester: add workload fader, remove benchmark (#1938)
This will make it easier to dial in a specific workload and also is more robust.
-rw-r--r--apps/OboeTester/app/src/main/java/com/mobileer/oboetester/DynamicWorkloadActivity.java109
-rw-r--r--apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestInputActivity.java2
-rw-r--r--apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivityBase.java9
-rw-r--r--apps/OboeTester/app/src/main/java/com/mobileer/oboetester/WorkloadView.java39
-rw-r--r--apps/OboeTester/app/src/main/res/layout/activity_dynamic_workload.xml9
5 files changed, 93 insertions, 75 deletions
diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/DynamicWorkloadActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/DynamicWorkloadActivity.java
index 917469aa..8c94a3b7 100644
--- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/DynamicWorkloadActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/DynamicWorkloadActivity.java
@@ -34,13 +34,23 @@ import java.util.Locale;
/**
* Demonstrate the behavior of a changing CPU load on underruns.
* Display the workload and the callback duration in a chart.
- * Enable PerformanceHints (ADPF).
+ * Enable or disable PerformanceHints (ADPF) using a checkbox.
+ * This might boost the CPU frequency when Oboe is taking too long to compute the next buffer.
+ * ADPF docs at: https://developer.android.com/reference/android/os/PerformanceHintManager
*/
public class DynamicWorkloadActivity extends TestOutputActivityBase {
- private static final double WORKLOAD_MAX = 500.0;
+ private static final int WORKLOAD_HIGH_MIN = 30;
+ private static final int WORKLOAD_HIGH_MAX = 150;
+ // When the CPU is completely saturated then the load will be above 1.0.
public static final double LOAD_RECOVERY_HIGH = 1.0;
+ // Use a slightly lower value for going low so that the comparator has hysteresis.
public static final double LOAD_RECOVERY_LOW = 0.95;
+ private static final float MARGIN_ABOVE_WORKLOAD_FOR_CPU = 1.2f;
+
+ // By default, set high workload to 70 voices, which is reasonable for most devices.
+ public static final double WORKLOAD_PROGRESS_FOR_70_VOICES = 0.53;
+
private Button mStopButton;
private Button mStartButton;
private TextView mResultView;
@@ -57,34 +67,25 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
private CheckBox mDrawAlwaysBox;
private int mCpuCount;
+ private static final int WORKLOAD_LOW = 1;
+ private int mWorkloadHigh; // this will get set later
+ private WorkloadView mDynamicWorkloadView;
+
// Periodically query the status of the streams.
protected class WorkloadUpdateThread {
public static final int SNIFFER_UPDATE_PERIOD_MSEC = 40;
public static final int SNIFFER_UPDATE_DELAY_MSEC = 300;
public static final int SNIFFER_TOGGLE_PERIOD_MSEC = 3000;
- public static final int REQUIRED_STABLE_MEASUREMENTS = 20;
-
- private static final double WORKLOAD_FILTER_COEFFICIENT = 0.9;
private static final int STATE_IDLE = 0;
- private static final int STATE_BENCHMARK_TARGET = 1;
- private static final int STATE_RUN_LOW = 2;
- private static final int STATE_RUN_HIGH = 3;
+ private static final int STATE_RUN_LOW = 1;
+ private static final int STATE_RUN_HIGH = 2;
private Handler mHandler;
- private int mCount;
-
- private double mCpuLoadBenchmark = 0.90; // Determine workload that will hit this CPU load.
- private double mCpuLoadHigh = 0.80; // Target CPU load during HIGH cycle.
- private double mWorkloadLow = 0.0;
- private double mWorkloadHigh = 0.0;
- private double mWorkloadCurrent = 1.0;
- private double mWorkloadBenchmark = 0.0;
+ private int mWorkloadCurrent = 1;
private int mState = STATE_IDLE;
private long mLastToggleTime = 0;
- private int mStableCount = 0;
- private boolean mArmLoadMonitor = false;
private long mRecoveryTimeBegin;
private long mRecoveryTimeEnd;
private long mStartTimeNanos;
@@ -93,8 +94,6 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
switch(state) {
case STATE_IDLE:
return "Idle";
- case STATE_BENCHMARK_TARGET:
- return "Benchmarking";
case STATE_RUN_LOW:
return "low";
case STATE_RUN_HIGH:
@@ -108,7 +107,7 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
private Runnable runnableCode = new Runnable() {
@Override
public void run() {
- double nextWorkload = 0.0;
+ int nextWorkload = mWorkloadCurrent;
AudioStreamBase stream = mAudioOutTester.getCurrentAudioStream();
float cpuLoad = stream.getCpuLoad();
float maxCpuLoad = stream.getAndResetMaxCpuLoad();
@@ -119,29 +118,11 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
switch (mState) {
case STATE_IDLE:
drawChartOnce = true; // clear old chart
- mState = STATE_BENCHMARK_TARGET;
- break;
- case STATE_BENCHMARK_TARGET:
- // prevent divide by zero
- double targetWorkload = (mWorkloadCurrent / Math.max(cpuLoad, 0.01)) * mCpuLoadBenchmark;
- targetWorkload = Math.min(WORKLOAD_MAX, targetWorkload);
- // low pass filter to find matching workload
- nextWorkload = (WORKLOAD_FILTER_COEFFICIENT * mWorkloadCurrent)
- + ((1.0 - WORKLOAD_FILTER_COEFFICIENT) * targetWorkload);
- if (Math.abs(cpuLoad - mCpuLoadBenchmark) < 0.04) {
- if (++mStableCount > REQUIRED_STABLE_MEASUREMENTS) {
- // Found the right workload.
- mWorkloadBenchmark = nextWorkload;
- mLastToggleTime = now;
- mState = STATE_RUN_LOW;
- mWorkloadLow = Math.max(1, (int)(nextWorkload * 0.02));
- mWorkloadHigh = (int)(nextWorkload * (mCpuLoadHigh / mCpuLoadBenchmark));
- mWorkloadTrace.setMax((float)(2.0 * nextWorkload));
- }
- }
+ mState = STATE_RUN_LOW;
+ mLastToggleTime = now;
break;
case STATE_RUN_LOW:
- nextWorkload = mWorkloadLow;
+ nextWorkload = WORKLOAD_LOW;
if ((now - mLastToggleTime) > SNIFFER_TOGGLE_PERIOD_MSEC) {
mLastToggleTime = now;
mState = STATE_RUN_HIGH;
@@ -171,6 +152,8 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
}
break;
}
+ stream.setWorkload((int) nextWorkload);
+ mWorkloadCurrent = nextWorkload;
// Update chart
float nowMicros = (System.nanoTime() - mStartTimeNanos) * 0.001f;
mMultiLineChart.addX(nowMicros);
@@ -184,15 +167,12 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
String recoveryTimeString = (mRecoveryTimeEnd <= mRecoveryTimeBegin) ?
"---" : ((mRecoveryTimeEnd - mRecoveryTimeBegin) + " msec");
String message =
- "#Voices: max = " + String.format(Locale.getDefault(), "%d", (int) mWorkloadBenchmark)
- + ", now = " + String.format(Locale.getDefault(), "%d", (int) nextWorkload)
+ "#Voices = " + (int) nextWorkload
+ "\nWorkState = " + stateToString(mState)
+ "\nCPU = " + String.format(Locale.getDefault(), "%6.3f%c", cpuLoad * 100, '%')
+ "\ncores = " + cpuMaskToString(cpuMask, mCpuCount)
+ "\nRecovery = " + recoveryTimeString;
postResult(message);
- stream.setWorkload((int)(nextWorkload));
- mWorkloadCurrent = nextWorkload;
mHandler.postDelayed(runnableCode, SNIFFER_UPDATE_PERIOD_MSEC);
}
@@ -202,9 +182,7 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
stop();
mStartTimeNanos = System.nanoTime();
mMultiLineChart.reset();
- mCount = 0;
- mStableCount = 0;
- mState = STATE_BENCHMARK_TARGET;
+ mState = STATE_IDLE;
mHandler = new Handler(Looper.getMainLooper());
// Start the initial runnable task by posting through the handler
mHandler.postDelayed(runnableCode, SNIFFER_UPDATE_DELAY_MSEC);
@@ -218,6 +196,10 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
}
+ private void setWorkloadHigh(int workloadHigh) {
+ mWorkloadHigh = workloadHigh;
+ }
+
/**
* This text will look best in a monospace font.
@@ -259,9 +241,12 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
mStartButton = (Button) findViewById(R.id.button_start);
mStopButton = (Button) findViewById(R.id.button_stop);
+ mDynamicWorkloadView = (WorkloadView) findViewById(R.id.dynamic_workload_view);
+ mWorkloadView.setVisibility(View.GONE);
+
// Add a row of checkboxes for setting CPU affinity.
mCpuCount = NativeEngine.getCpuCount();
- final int defaultCpuAffinity = 2;
+ final int defaultCpuAffinityMask = 0;
View.OnClickListener checkBoxListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -283,25 +268,26 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
mAffinityBoxes.add(checkBox);
checkBox.setText(cpuIndex + "");
checkBox.setOnClickListener(checkBoxListener);
- if (cpuIndex == defaultCpuAffinity) {
+ if (((1 << cpuIndex) & defaultCpuAffinityMask) != 0) {
checkBox.setChecked(true);
}
}
- NativeEngine.setCpuAffinityMask(1 << defaultCpuAffinity);
+ NativeEngine.setCpuAffinityMask(defaultCpuAffinityMask);
mMultiLineChart = (MultiLineChart) findViewById(R.id.multiline_chart);
mMaxCpuLoadTrace = mMultiLineChart.createTrace("CPU", Color.RED,
0.0f, 2.0f);
mWorkloadTrace = mMultiLineChart.createTrace("Work", Color.BLUE,
- 0.0f, (float)WORKLOAD_MAX);
+ 0.0f, (MARGIN_ABOVE_WORKLOAD_FOR_CPU * WORKLOAD_HIGH_MAX));
- // TODO remove when finished with ADPF experiments.
- mUseAltAdpfBox = (CheckBox) findViewById(R.id.use_alternative_adpf);
mPerfHintBox = (CheckBox) findViewById(R.id.enable_perf_hint);
+ // TODO remove when finished with ADPF experiments.
+ mUseAltAdpfBox = (CheckBox) findViewById(R.id.use_alternative_adpf);
mUseAltAdpfBox.setOnClickListener(buttonView -> {
CheckBox checkBox = (CheckBox) buttonView;
setUseAlternativeAdpf(checkBox.isChecked());
+ mPerfHintBox.setEnabled(!checkBox.isChecked());
});
mUseAltAdpfBox.setVisibility(View.GONE);
@@ -323,6 +309,16 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
mDrawChartAlways = checkBox.isChecked();
});
+ if (mDynamicWorkloadView != null) {
+ mDynamicWorkloadView.setWorkloadReceiver((w) -> {
+ setWorkloadHigh(w);
+ });
+
+ mDynamicWorkloadView.setLabel("High Workload");
+ mDynamicWorkloadView.setRange(WORKLOAD_HIGH_MIN, WORKLOAD_HIGH_MAX);
+ mDynamicWorkloadView.setFaderNormalizedProgress(WORKLOAD_PROGRESS_FOR_70_VOICES);
+ }
+
updateButtons(false);
updateEnabledWidgets();
hideSettingsViews(); // make more room
@@ -356,9 +352,6 @@ public class DynamicWorkloadActivity extends TestOutputActivityBase {
}
public void startTest(View view) {
- // Do not draw until the benchmark stage has finished.
- mDrawAlwaysBox.setChecked(false);
- mDrawChartAlways = false;
try {
openAudio();
} catch (IOException e) {
diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestInputActivity.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestInputActivity.java
index 5e2602b0..b4f2449c 100644
--- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestInputActivity.java
+++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestInputActivity.java
@@ -73,7 +73,7 @@ public class TestInputActivity extends TestAudioActivity {
mWorkloadView = (WorkloadView) findViewById(R.id.workload_view);
if (mWorkloadView != null) {
- mWorkloadView.setAudioStreamTester(mAudioInputTester);
+ mWorkloadView.setWorkloadReceiver((w) -> mAudioInputTester.setWorkload(w));
}
mCommunicationDeviceView = (CommunicationDeviceView) findViewById(R.id.comm_device_view);
diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivityBase.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivityBase.java
index 5c17e928..731351b9 100644
--- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivityBase.java
+++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/TestOutputActivityBase.java
@@ -26,7 +26,7 @@ abstract class TestOutputActivityBase extends TestAudioActivity {
AudioOutputTester mAudioOutTester;
private BufferSizeView mBufferSizeView;
- private WorkloadView mWorkloadView;
+ protected WorkloadView mWorkloadView;
@Override boolean isOutput() { return true; }
@@ -40,13 +40,14 @@ abstract class TestOutputActivityBase extends TestAudioActivity {
super.findAudioCommon();
mBufferSizeView = (BufferSizeView) findViewById(R.id.buffer_size_view);
mWorkloadView = (WorkloadView) findViewById(R.id.workload_view);
+ if (mWorkloadView != null) {
+ mWorkloadView.setWorkloadReceiver((w) -> mAudioOutTester.setWorkload(w));
+ }
}
@Override
public AudioOutputTester addAudioOutputTester() {
- AudioOutputTester audioOutTester = super.addAudioOutputTester();
- mWorkloadView.setAudioStreamTester(audioOutTester);
- return audioOutTester;
+ return super.addAudioOutputTester();
}
@Override
diff --git a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/WorkloadView.java b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/WorkloadView.java
index e1dc9d5e..74027c2c 100644
--- a/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/WorkloadView.java
+++ b/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/WorkloadView.java
@@ -18,6 +18,7 @@ package com.mobileer.oboetester;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.SeekBar;
@@ -27,13 +28,19 @@ import java.util.Locale;
public class WorkloadView extends LinearLayout {
- private AudioStreamTester mAudioStreamTester;
-
protected static final int FADER_PROGRESS_MAX = 1000; // must match layout
protected TextView mTextView;
protected SeekBar mSeekBar;
+
+ private String mLabel = "Workload";
protected ExponentialTaper mExponentialTaper;
+ public interface WorkloadReceiver {
+ void setWorkload(int workload);
+ }
+
+ WorkloadReceiver mWorkloadReceiver;
+
private SeekBar.OnSeekBarChangeListener mChangeListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@@ -66,12 +73,8 @@ public class WorkloadView extends LinearLayout {
initializeViews(context);
}
- public AudioStreamTester getAudioStreamTester() {
- return mAudioStreamTester;
- }
-
- public void setAudioStreamTester(AudioStreamTester audioStreamTester) {
- mAudioStreamTester = audioStreamTester;
+ public void setWorkloadReceiver(WorkloadReceiver workloadReceiver) {
+ mWorkloadReceiver = workloadReceiver;
}
void setFaderNormalizedProgress(double fraction) {
@@ -92,15 +95,21 @@ public class WorkloadView extends LinearLayout {
mTextView = (TextView) findViewById(R.id.textWorkload);
mSeekBar = (SeekBar) findViewById(R.id.faderWorkload);
mSeekBar.setOnSeekBarChangeListener(mChangeListener);
- mExponentialTaper = new ExponentialTaper(0.0, 100.0, 10.0);
+ setRange(0.0, 100.0);
//mSeekBar.setProgress(0);
}
+ void setRange(double dMin, double dMax) {
+ mExponentialTaper = new ExponentialTaper(dMin, dMax, 10.0);
+ }
+
private void setValueByPosition(int progress) {
int workload = (int) mExponentialTaper.linearToExponential(
((double)progress) / FADER_PROGRESS_MAX);
- mAudioStreamTester.setWorkload(workload);
- mTextView.setText("Workload = " + String.format(Locale.getDefault(), "%3d", workload));
+ if (mWorkloadReceiver != null) {
+ mWorkloadReceiver.setWorkload(workload);
+ }
+ mTextView.setText(getLabel() + " = " + String.format(Locale.getDefault(), "%3d", workload));
}
@Override
@@ -108,4 +117,12 @@ public class WorkloadView extends LinearLayout {
super.setEnabled(enabled);
mSeekBar.setEnabled(enabled);
}
+
+ public String getLabel() {
+ return mLabel;
+ }
+
+ public void setLabel(String label) {
+ this.mLabel = label;
+ }
}
diff --git a/apps/OboeTester/app/src/main/res/layout/activity_dynamic_workload.xml b/apps/OboeTester/app/src/main/res/layout/activity_dynamic_workload.xml
index 0723ff22..2212b5bf 100644
--- a/apps/OboeTester/app/src/main/res/layout/activity_dynamic_workload.xml
+++ b/apps/OboeTester/app/src/main/res/layout/activity_dynamic_workload.xml
@@ -38,7 +38,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8sp"
- android:text="Perf Hint" />
+ android:text="ADPF" />
<CheckBox
android:id="@+id/use_alternative_adpf"
@@ -90,6 +90,13 @@
</HorizontalScrollView>
+ <com.mobileer.oboetester.WorkloadView
+ android:id="@+id/dynamic_workload_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal" />
+
<TextView
android:id="@+id/resultView"
android:layout_width="match_parent"