diff options
author | Phil Burk <philburk@mobileer.com> | 2023-11-22 13:58:19 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-22 13:58:19 -0800 |
commit | 0f945983d6b29f15c065f6c261b35548e80fbc50 (patch) | |
tree | e935799342750f53ecaf428ec62baa9b0cce0a7b | |
parent | f6aa353e551186dec14773540a597c0b38d9484c (diff) | |
download | oboe-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.
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" |