From a41947dda31a22daa37fa06f88657b291e687006 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Mon, 10 Mar 2025 17:23:56 -0700 Subject: [PATCH 1/2] Add BenchMark test harness Generate notes using the specified number of voices until the CPU time per note stabilizes or we timeout. --- source/SynthMark.h | 1 + source/tools/AudioSinkBase.h | 15 ++++ source/tools/BenchMarkHarness.h | 113 +++++++++++++++++++++++++++ source/tools/NativeTest.h | 93 ++++++++++++++++------ source/tools/SynthMarkCommand.cpp | 11 ++- source/tools/TestHarnessBase.h | 10 ++- source/tools/TestHarnessParameters.h | 4 + source/tools/VirtualAudioSink.h | 2 +- source/tools/VoiceMarkHarness.h | 1 - 9 files changed, 222 insertions(+), 28 deletions(-) create mode 100644 source/tools/BenchMarkHarness.h diff --git a/source/SynthMark.h b/source/SynthMark.h index e12a69a..e3b28f4 100644 --- a/source/SynthMark.h +++ b/source/SynthMark.h @@ -67,6 +67,7 @@ constexpr int64_t SYNTHMARK_MICROS_PER_SECOND = 1000 * 1000; constexpr int64_t SYNTHMARK_NANOS_PER_MICROSECOND = 1000; constexpr int64_t SYNTHMARK_NANOS_PER_MILLISECOND = 1000 * 1000; constexpr int64_t SYNTHMARK_NANOS_PER_SECOND = 1000 * 1000 * 1000; +constexpr double SYNTHMARK_SECONDS_PER_NANO = 1.0e-9; typedef float synth_float_t; diff --git a/source/tools/AudioSinkBase.h b/source/tools/AudioSinkBase.h index 9ba0136..110c411 100644 --- a/source/tools/AudioSinkBase.h +++ b/source/tools/AudioSinkBase.h @@ -231,6 +231,19 @@ class AudioSinkBase mAdpfEnabled = enabled; } + bool isRealTime() const { + return mRealTime; + } + + /** + * If true then the audio sink will run at the specified audio rate. + * If false then the audio sink will run as fast as possible. + * @param realTime + */ + void setRealTime(bool realTime) { + mRealTime = realTime; + } + static const char *schedulerToString(int scheduler) { scheduler = scheduler & 0xFFFF; // clear high flags like SCHED_RESET_ON_FORK switch(scheduler) { @@ -287,6 +300,8 @@ class AudioSinkBase int32_t mDefaultBufferSizeInBursts = kBufferSizeInBursts; int32_t mBufferCapacityInFrames = 4 * 1024; + bool mRealTime = false; + private: IAudioSinkCallback *mCallback = NULL; int64_t mFramesWritten = 0; diff --git a/source/tools/BenchMarkHarness.h b/source/tools/BenchMarkHarness.h new file mode 100644 index 0000000..8b0fdf4 --- /dev/null +++ b/source/tools/BenchMarkHarness.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2018 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 SYNTHMARK_BENCHMARK_HARNESS_H +#define SYNTHMARK_BENCHMARK_HARNESS_H + +#include +#include +#include + +#include "AudioSinkBase.h" +#include "SynthMark.h" +#include "synth/Synthesizer.h" +#include "tools/LogTool.h" +#include "tools/TestHarnessBase.h" +#include "tools/TimingAnalyzer.h" +#include "TestHarnessParameters.h" + + +/** + * Play a specified number of voices on a Synthesizer + * as fast as possible and measure the time it takes. + */ +class BenchMarkHarness : public TestHarnessBase { +public: + BenchMarkHarness(AudioSinkBase *audioSink, SynthMarkResult *result, LogTool &logTool) + : TestHarnessBase(audioSink, result, logTool) + { + std::stringstream testName; + testName << "BenchMark"; + mTestName = testName.str(); + audioSink->setRealTime(false); // run as fast as possible + } + + virtual ~BenchMarkHarness() { + } + + virtual void onBeginMeasurement() override { + mResult->setTestName(mTestName); + mLogTool.log("---- Starting %s ----\n", mTestName.c_str()); + mBeatCount = 0; + mTestStartTimeNanos = HostTools::getNanoTime(); + } + + double reportSpeed() { + double elapsedTimeSeconds = mTimer.getTotalTime() * SYNTHMARK_SECONDS_PER_NANO; + int32_t framesWritten = getFramesWritten(); + double virtualTime = (framesWritten - mLastFramesWritten) / (double) getSampleRate(); + double fractionRealTime = elapsedTimeSeconds / virtualTime; + mLogTool.log("%2d: %5.3f%% of real-time\n", + mBeatCount, fractionRealTime * 100); + mLastFramesWritten = framesWritten; + return fractionRealTime; + } + + virtual int32_t onBeforeNoteOn() override { + if (mBeatCount > 0) { + double fractionRealTime = reportSpeed(); + mStableCount += (fractionRealTime > (mMinFractionRealTime * 0.99)) ? 1 : 0; + mMinFractionRealTime = std::min(mMinFractionRealTime, fractionRealTime); + } + mTimer.reset(); + mBeatCount++; + int64_t now = HostTools::getNanoTime(); + bool timeOut = (now - mTestStartTimeNanos) > (5 * SYNTHMARK_NANOS_PER_SECOND); + return ((mStableCount < 2) && !timeOut) ? 0 : 1; + } + + int32_t getMaxVoices() { + return (int32_t) (getNumVoices() / mMinFractionRealTime); + } + + virtual void onEndMeasurement() override { + int8_t resultCode = SYNTHMARK_RESULT_SUCCESS; + std::stringstream resultMessage; + std::stringstream cpuPart; + if (mAudioSink->getActualCpu() >= 0) { + cpuPart << ".cpu." << mAudioSink->getActualCpu(); + } + resultMessage << "percent.realtime" << cpuPart.str() + << ".voices." << getNumVoices() + << " = " << (mMinFractionRealTime * 100) << "%%" << std::endl; + resultMessage << "max.voices" << cpuPart.str() + << " = " << getMaxVoices() << std::endl; + mResult->setResultCode(resultCode); + resultMessage << mCpuAnalyzer.dump(); + + mResult->setMeasurement(mMinFractionRealTime); + mResult->appendMessage(resultMessage.str()); + } + +private: + double mMinFractionRealTime = 1.0e10; // start very high + int32_t mBeatCount = 0; + int32_t mLastFramesWritten = 0; + int32_t mStableCount = 0; + int64_t mTestStartTimeNanos = 0; +}; + +#endif //SYNTHMARK_BENCHMARK_HARNESS_H diff --git a/source/tools/NativeTest.h b/source/tools/NativeTest.h index dba7689..6892c02 100644 --- a/source/tools/NativeTest.h +++ b/source/tools/NativeTest.h @@ -26,16 +26,17 @@ #include "../SynthMark.h" #include "../synth/Synthesizer.h" #include "AutomatedTestSuite.h" +#include "ClockRampHarness.h" #include "JitterMarkHarness.h" #include "LatencyMarkHarness.h" #include "LogTool.h" #include "Params.h" +#include "BenchMarkHarness.h" #include "TimingAnalyzer.h" -#include "VirtualAudioSink.h" -#include "VoiceMarkHarness.h" #include "TestHarnessParameters.h" #include "UtilizationMarkHarness.h" -#include "ClockRampHarness.h" +#include "VirtualAudioSink.h" +#include "VoiceMarkHarness.h" #define NATIVETEST_ERROR -1 #define NATIVETEST_SUCCESS 0 @@ -64,8 +65,9 @@ typedef enum { NATIVETEST_ID_LATENCYMARK = 3, NATIVETEST_ID_UTILIZATIONMARK = 4, NATIVETEST_ID_VOICEMARK = 5, + NATIVETEST_ID_BENCHMARK = 6, - NATIVETEST_ID_COUNT = 6, + NATIVETEST_ID_COUNT = 7, } native_test_t; @@ -134,13 +136,11 @@ class NativeTestUnit { HostThreadFactory *mHostThreadFactory = NULL; }; -class CommonNativeTestUnit : public NativeTestUnit { +class BaseNativeTestUnit : public NativeTestUnit { public: - CommonNativeTestUnit(std::string title, LogTool &logTool) + BaseNativeTestUnit(std::string title, LogTool &logTool) : NativeTestUnit(title, logTool) { - } - void createAudioSink() { int audioLevel = mParams.getValueFromInt(PARAMS_AUDIO_LEVEL); if (audioLevel == AudioSinkBase::AUDIO_LEVEL_OUTPUT) { @@ -171,17 +171,6 @@ class CommonNativeTestUnit : public NativeTestUnit { &vFramesPerBurst, kParamsDefaultIndexFramesPerBurst); - ParamInteger paramAudioLevel(PARAMS_AUDIO_LEVEL, "Audio Level: 1=callback, 2=audible", - AudioSinkBase::AUDIO_LEVEL_CALLBACK, - AudioSinkBase::AUDIO_LEVEL_CALLBACK, - AudioSinkBase::AUDIO_LEVEL_OUTPUT); - - ParamInteger paramAdpfEnabled(PARAMS_ADPF_ENABLED, "ADPF: 0=off, 1=on",0, 0, 1); - - ParamInteger paramNoteOnDelay(PARAMS_NOTE_ON_DELAY, "Note On Delay Seconds", 0, 0, 300); - // Touch boost usually dies down in a few seconds. - ParamInteger paramTestStartDelay(PARAMS_TEST_START_DELAY, "Test Start Delay Seconds", 4, 0, 20); - std::vector vDurations = DEFAULT_TEST_DURATIONS; ParamFloat paramNumSeconds(PARAMS_NUM_SECONDS, "Number of Seconds", &vDurations, 3); @@ -189,10 +178,6 @@ class CommonNativeTestUnit : public NativeTestUnit { mParams.addParam(¶mSamplesPerFrame); mParams.addParam(¶mFramesPerRender); mParams.addParam(¶mFramesPerBurst); - mParams.addParam(¶mAudioLevel); - mParams.addParam(¶mAdpfEnabled); - mParams.addParam(¶mNoteOnDelay); - mParams.addParam(¶mTestStartDelay); mParams.addParam(¶mNumSeconds); #ifndef __APPLE__ @@ -281,6 +266,33 @@ class CommonNativeTestUnit : public NativeTestUnit { SynthMarkResult mResult; }; +class CommonNativeTestUnit : public BaseNativeTestUnit { +public: + CommonNativeTestUnit(std::string title, LogTool &logTool) + : BaseNativeTestUnit(title, logTool) { + } + + int init() override { + int result = BaseNativeTestUnit::init(); + //Register more parameters + ParamInteger paramAudioLevel(PARAMS_AUDIO_LEVEL, "Audio Level: 1=callback, 2=audible", + AudioSinkBase::AUDIO_LEVEL_CALLBACK, + AudioSinkBase::AUDIO_LEVEL_CALLBACK, + AudioSinkBase::AUDIO_LEVEL_OUTPUT); + + ParamInteger paramAdpfEnabled(PARAMS_ADPF_ENABLED, "ADPF: 0=off, 1=on",0, 0, 1); + + ParamInteger paramNoteOnDelay(PARAMS_NOTE_ON_DELAY, "Note On Delay Seconds", 0, 0, 300); + // Touch boost usually dies down in a few seconds. + ParamInteger paramTestStartDelay(PARAMS_TEST_START_DELAY, "Test Start Delay Seconds", 4, 0, 20); + + mParams.addParam(¶mAudioLevel); + mParams.addParam(¶mAdpfEnabled); + mParams.addParam(¶mNoteOnDelay); + mParams.addParam(¶mTestStartDelay); + return result; + } +}; //============================ // Custom Tests //============================ @@ -425,6 +437,38 @@ class TestJitterMark : public ChangingVoiceTestUnit { protected: }; +class TestBenchMark : public BaseNativeTestUnit { +public: + TestBenchMark(LogTool &logTool) + : BaseNativeTestUnit("BenchMark", logTool) { + + } + + int init() override { + int err = BaseNativeTestUnit::init(); + if (err != SYNTHMARK_RESULT_SUCCESS) { + return err; + } + + std::vector vNumVoices = { 64 }; + ParamInteger paramNumVoices(PARAMS_NUM_VOICES, + "Number of Voices", + &vNumVoices, + 0); + mParams.addParam(¶mNumVoices); + + return err; + } + + int run() override { + createAudioSink(); + BenchMarkHarness harness(mAudioSink.get(), &mResult, mLogTool); + + return BaseNativeTestUnit::runTestHarness((TestHarnessBase &) harness); + } +protected: +}; + class TestClockRamp : public ChangingVoiceTestUnit { public: TestClockRamp(LogTool &logTool) @@ -476,6 +520,7 @@ class NativeTest { , mTestUtilizationMark(mLog) , mTestClockRamp(mLog) , mTestAutomatedSuite(mLog) + , mTestBenchMark(mLog) { initTests(); setHostThreadFactory(&mHostThreadFactoryOwned); @@ -611,6 +656,7 @@ class NativeTest { case NATIVETEST_ID_UTILIZATIONMARK: pTestUnit = &mTestUtilizationMark; break; case NATIVETEST_ID_CLOCKRAMP: pTestUnit = &mTestClockRamp; break; case NATIVETEST_ID_AUTOMATED: pTestUnit = &mTestAutomatedSuite; break; + case NATIVETEST_ID_BENCHMARK: pTestUnit = &mTestBenchMark; break; } return pTestUnit; } @@ -624,6 +670,7 @@ class NativeTest { TestUtilizationMark mTestUtilizationMark; TestClockRamp mTestClockRamp; TestAutomatedSuite mTestAutomatedSuite; + TestBenchMark mTestBenchMark; LogTool mLog; HostThreadFactory mHostThreadFactoryOwned; diff --git a/source/tools/SynthMarkCommand.cpp b/source/tools/SynthMarkCommand.cpp index 07dc9f9..c4ae270 100644 --- a/source/tools/SynthMarkCommand.cpp +++ b/source/tools/SynthMarkCommand.cpp @@ -30,6 +30,7 @@ #include "tools/JitterMarkHarness.h" #include "tools/ITestHarness.h" #include "tools/LatencyMarkHarness.h" +#include "tools/BenchMarkHarness.h" #include "tools/TimingAnalyzer.h" #if defined(__ANDROID__) #include "tools/RealAudioSink.h" @@ -51,7 +52,7 @@ static void usage(const char *name) { printf("%s -t{test} -n{numVoices} -d{noteOnDelay} -p{percentCPU} -r{sampleRate}" " -s{seconds} -b{burstSize} -c{cpuAffinity}\n", name); printf(" -t{test}, v=voice, l=latency, j=jitter, u=utilization" - ", s=series_util, c=clock_ramp, a=automated, default is %c\n", + ", s=series_util, c=clock_ramp, a=automated, b=benchmark, default is %c\n", kDefaultTestCode); printf(" -a{audioLevel} 0 = normal thread, 1 = audio callback (default), 2 = audio output\n"); @@ -351,6 +352,14 @@ int synthmark_command_main(int argc, char **argv) } break; + case 'b': + { + BenchMarkHarness *speedHarness = new BenchMarkHarness(audioSink.get(), &result, logTool); + speedHarness->setNumVoicesHigh(numVoicesHigh); + harness = speedHarness; + } + break; + default: printf(TEXT_ERROR "unrecognized testCode = %c\n", testCode); usage(argv[0]); diff --git a/source/tools/TestHarnessBase.h b/source/tools/TestHarnessBase.h index d3d3778..822f59d 100644 --- a/source/tools/TestHarnessBase.h +++ b/source/tools/TestHarnessBase.h @@ -73,7 +73,10 @@ class TestHarnessBase : public TestHarnessParameters, public IAudioSinkCallback // Customize the test by defining these virtual methods. virtual void onBeginMeasurement() {} - // TODO Use Result type return value. + /** + * + * @return negative error code or zero to continue or 1 to stop + */ virtual int32_t onBeforeNoteOn() { return 0; } virtual void onEndMeasurement() {} @@ -110,7 +113,10 @@ class TestHarnessBase : public TestHarnessParameters, public IAudioSinkCallback mNoteCounter++; } else { result = onBeforeNoteOn(); - if (result < 0) { + if (result == 1) { + mLogTool.log("%s() test is finished\n", __func__); + return IAudioSinkCallback::Result::Finished; + } else if (result < 0) { mLogTool.log("%s() onBeforeNoteOn() returned %d\n", __func__, result); mResult->setResultCode(result); return IAudioSinkCallback::Result::Finished; diff --git a/source/tools/TestHarnessParameters.h b/source/tools/TestHarnessParameters.h index 2d51cdd..b73bd9e 100644 --- a/source/tools/TestHarnessParameters.h +++ b/source/tools/TestHarnessParameters.h @@ -130,6 +130,10 @@ class TestHarnessParameters : public ITestHarness { return mAudioSink->getSampleRate(); } + int32_t getFramesWritten() { + return mAudioSink->getFramesWritten(); + } + protected: int32_t mNumVoices = 8; int32_t mDelayNotesOn = 0; diff --git a/source/tools/VirtualAudioSink.h b/source/tools/VirtualAudioSink.h index a56440f..fa08ae1 100644 --- a/source/tools/VirtualAudioSink.h +++ b/source/tools/VirtualAudioSink.h @@ -125,7 +125,7 @@ class VirtualAudioSink : public AudioSinkBase } // If there is not enough room then sleep until the hardware reads another burst. - if (availableRoom < mFramesPerBurst) { + if (availableRoom < mFramesPerBurst && mRealTime) { HostCpuManager::getInstance()->sleepAndTuneCPU(mNextHardwareReadTimeNanos); updateHardwareSimulator(); } else { diff --git a/source/tools/VoiceMarkHarness.h b/source/tools/VoiceMarkHarness.h index 13d252f..6d86d63 100644 --- a/source/tools/VoiceMarkHarness.h +++ b/source/tools/VoiceMarkHarness.h @@ -29,7 +29,6 @@ #include "tools/TimingAnalyzer.h" #include "TestHarnessParameters.h" - constexpr int kMinimumVoiceCount = 4; constexpr int kMinimumNoteOnCount = 1; From 678daa5f8097c19e454d3d2585e72c1de8a491d0 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 22 May 2025 10:30:57 -0700 Subject: [PATCH 2/2] WIP Use new BenchmarkHarness to find the CPU clusters Fixes #144 --- source/tools/AutomatedTestSuite.h | 160 +++++++++++++++++------------- 1 file changed, 90 insertions(+), 70 deletions(-) diff --git a/source/tools/AutomatedTestSuite.h b/source/tools/AutomatedTestSuite.h index 186a585..2d2ea89 100644 --- a/source/tools/AutomatedTestSuite.h +++ b/source/tools/AutomatedTestSuite.h @@ -27,18 +27,18 @@ #include "tools/VirtualAudioSink.h" #include "tools/VoiceMarkHarness.h" #include "tools/TestHarnessParameters.h" +#include "BenchMarkHarness.h" /** * Run an automated test, analyze the results and print a report. - * 1) Determine what CPUs are available - * 2) Run VoiceMark on each CPU to determine big vs little CPUs - * 3) Run LatencyMark with a light load, a heavy load, then an alternating load. - * 4) Print report with analysis. + * 1) Determine what CPUs are available. + * 2) Run a quick BenchMark on each CPU to identify, little, medium, big and huge CPUs. + * 3) Use LatencyMark to detect various problems. + * 4) With a light load on a small core to detect preemption from bad drivers. + * 6) With a dynamic load on a small core to detect slow response frequency scaling CPU governor. + * 5) With moderate loads with no CPU affinity to detect problems with CPU migration. + * 6) With a dynamic load to detect slow response from CPU governor. * - * The current code: - * assumes that the CPU numbering depends on their capacity, - * assumes that both lowCpu and highCpu are online, - * assume the existence of only two architectures, homogeneous or BIG.little. * TODO handle more architectures * TODO Measure latency without CPU affinity */ @@ -70,7 +70,7 @@ class AutomatedTestSuite : public TestHarnessParameters { mLogTool.log("\nSynthMark Version " SYNTHMARK_VERSION_TEXT "\n"); mLogTool.log("\n-------- CPU Performance ------------\n"); - int32_t err = measureLowHighCpuPerformance(sampleRate, framesPerBurst, 10); + int32_t err = measureCpuSpeeds(sampleRate, framesPerBurst, 10); if (err) return err; mLogTool.log("\n-------- LATENCY ------------\n"); @@ -147,87 +147,107 @@ struct LatencyResult { mResult->appendMessage(resultMessage.str()); } - virtual int32_t measureLowHighCpuPerformance(int32_t sampleRate, - int32_t framesPerBurst, - int32_t numSeconds) { - std::stringstream resultMessage; - int numCPUs = HostTools::getCpuCount(); - SynthMarkResult result1; - VoiceMarkHarness *harness = new VoiceMarkHarness(mAudioSink, &result1, mLogTool); - harness->setTargetCpuLoad(kMaxUtilization); - harness->setInitialVoiceCount(mNumVoices); - harness->setDelayNoteOnSeconds(mDelayNotesOn); - harness->setThreadType(mThreadType); + std::vector>> findClustersWithIndices(const std::vector& data, int threshold) { + std::vector> indexedData; + for (size_t i = 0; i < data.size(); ++i) { + indexedData.emplace_back(data[i], static_cast(i)); // Store value and index + } - // TODO This is hack way to choose CPUs for BIG.little architectures. - // TODO Test each CPU or come up with something better. - mLogTool.log("Try to find BIG/LITTLE CPUs\n"); - int lowCpu = (1 * numCPUs) / 4; - mAudioSink->setRequestedCpu(lowCpu); - mLogTool.log("Run low VoiceMark with CPU #%d\n", lowCpu); - int32_t err = harness->runTest(sampleRate, framesPerBurst, numSeconds); - if (err) { - delete harness; - return err; + std::sort(indexedData.begin(), indexedData.end()); // Sort based on values + + std::vector>> clusters; + std::vector> currentCluster; + + if (!indexedData.empty()) { + currentCluster.push_back(indexedData[0]); + clusters.push_back(currentCluster); + currentCluster.clear(); + } + + for (size_t i = 1; i < indexedData.size(); ++i) { + if (std::abs(indexedData[i].first - indexedData[i - 1].first) > threshold) { + clusters.push_back(currentCluster); + currentCluster.clear(); + } + if(currentCluster.empty()){ + clusters.back().push_back(indexedData[i]); + } + else{ + currentCluster.push_back(indexedData[i]); + } + } + if(!currentCluster.empty()){ + clusters.push_back(currentCluster); } + if(clusters[0].empty()){ + clusters.erase(clusters.begin()); + } + + return clusters; + } - double voiceMarkLowIndex = result1.getMeasurement(); - mLogTool.log("low VoiceMark_%d = %5.1f\n", kMaxUtilizationPercent, voiceMarkLowIndex); - double voiceMarkHighIndex = voiceMarkLowIndex; - - int highCpu = (3 * numCPUs) / 4; - // If there are more than 2 CPUs than measure one each from top and bottom half in - // case they are heterogeneous. - if (lowCpu != highCpu) { - mAudioSink->setRequestedCpu(highCpu); - mLogTool.log("Run high VoiceMark with CPU #%d\n", highCpu); - err = harness->runTest(sampleRate, framesPerBurst, numSeconds); + // Measure all of the CPU speeds. + virtual int32_t measureCpuSpeeds(int32_t sampleRate, + int32_t framesPerBurst, + int32_t numSeconds) { + std::stringstream resultMessage; + int numCPUs = HostTools::getCpuCount(); + std::vector cpuMaxVoices; + int32_t maxVoicesPossible = 0; + SynthMarkResult result1; + for (int cpuIndex = 0; cpuIndex < numCPUs; cpuIndex++) { + BenchMarkHarness harness(mAudioSink, &result1, mLogTool); + harness.setNumVoices(getNumVoices()); + harness.setDelayNoteOnSeconds(mDelayNotesOn); + harness.setThreadType(mThreadType); + + mAudioSink->setRequestedCpu(cpuIndex); + mLogTool.log("Run BenchMark with CPU #%d\n", cpuIndex); + int32_t err = harness.runTest(sampleRate, framesPerBurst, numSeconds); if (err) { - delete harness; return err; } - voiceMarkHighIndex = result1.getMeasurement(); - mLogTool.log("high VoiceMark_%d = %5.1f\n", kMaxUtilizationPercent, voiceMarkHighIndex); - double averageVoiceMark = 0.5 * (voiceMarkHighIndex + voiceMarkLowIndex); - double threshold = kHighLowThreshold * averageVoiceMark; + int32_t maxVoicesPerCpu = harness.getMaxVoices(); + mLogTool.log("max_voices_%d = %5.1f\n", cpuIndex, maxVoicesPerCpu); + cpuMaxVoices.push_back(maxVoicesPerCpu); + maxVoicesPossible = std::max(maxVoicesPossible, maxVoicesPerCpu); + // Give CPU time to settle down before the next benchmark. + HostTools::sleepForNanoseconds(500 * SYNTHMARK_NANOS_PER_MILLISECOND); + } + + // Figure out CPU clusters from the benchmarks. + const int threshold = 30; + std::vector>> clusters = findClustersWithIndices(cpuMaxVoices, threshold); - mHaveBigLittle = (abs(voiceMarkHighIndex - voiceMarkLowIndex) > threshold); + for (size_t i = 0; i < clusters.size(); ++i) { + mLogTool.log("Cluster[#%d]: ", i); + for (const auto& pair : clusters[i]) { + mLogTool.log("( %d, %d) " , pair.first, pair.second); + } + mLogTool.log("\n"); + std::cout << std::endl; } + mHaveBigLittle = clusters.size() > 1; + + const auto littleCluster = clusters[0]; + const auto& pair = littleCluster[littleCluster.size() - 1]; + mLittleCpu = pair.second; + mVoiceMarkLittle = pair.first; if (mHaveBigLittle) { - if (voiceMarkHighIndex > voiceMarkLowIndex) { - mLittleCpu = lowCpu; - mVoiceMarkLittle = voiceMarkLowIndex; - mBigCpu = highCpu; - mVoiceMarkBig = voiceMarkHighIndex; - } else { - mLittleCpu = highCpu; - mVoiceMarkLittle = voiceMarkHighIndex; - mBigCpu = lowCpu; - mVoiceMarkBig = voiceMarkLowIndex; - } - resultMessage << "# The CPU seems to be heterogeneous. Assume BIG-little.\n"; + resultMessage << "# The CPU seems to be heterogeneous.\n"; resultMessage << "cpu.little = " << mLittleCpu << std::endl; - resultMessage << "cpu.big = " << mBigCpu << std::endl; } else { - mLittleCpu = highCpu; - mVoiceMarkLittle = voiceMarkHighIndex; - mBigCpu = highCpu; - mVoiceMarkBig = voiceMarkHighIndex; resultMessage << "# The CPU seems to be homogeneous.\n"; resultMessage << "cpu = " << mLittleCpu << std::endl; } - resultMessage << "voice.mark." << cpuToBigLittle(lowCpu) << " = " << voiceMarkLowIndex << std::endl; - if (lowCpu != highCpu) { - resultMessage << "voice.mark." << cpuToBigLittle(highCpu) << " = " << voiceMarkHighIndex << std::endl; - } + resultMessage << "max.voices.little = " << mVoiceMarkLittle << std::endl; - // std::cout << result1.getResultMessage(); + //std::cout << result1.getResultMessage(); mResult->appendMessage(resultMessage.str()); - delete harness; return SYNTHMARK_RESULT_SUCCESS; }