Profiling tools
This commit is contained in:
Vendored
+32
@@ -172,6 +172,38 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "(gdb) profilePlugin Launch",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
// Resolved by CMake Tools:
|
||||||
|
"program": "${command:cmake.launchTargetPath}",
|
||||||
|
|
||||||
|
"args": [ "Nam_Profile", "--no-profile","-w" ],
|
||||||
|
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"environment": [
|
||||||
|
{
|
||||||
|
"name": "PATH",
|
||||||
|
"value": "$PATH:${command:cmake.launchTargetDirectory}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OTHER_VALUE",
|
||||||
|
"value": "Something something"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ Run the following commands to install dependent libraries required by the PiPeda
|
|||||||
sudo apt install -y authbind
|
sudo apt install -y authbind
|
||||||
sudo apt install -y libavahi-client-dev
|
sudo apt install -y libavahi-client-dev
|
||||||
sudo apt install -y libnm-dev libicu-dev
|
sudo apt install -y libnm-dev libicu-dev
|
||||||
sudo apt install -y libsdbus-c++-dev libzip-dev
|
sudo apt install -y libsdbus-c++-dev libzip-dev google-perftools
|
||||||
|
|
||||||
|
|
||||||
### Installing Sources
|
### Installing Sources
|
||||||
|
|||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
${SCRIPT_DIR}/../build/src/profilePlugin -w ToobNam_Profile -o /tmp/ToobNam.perf &&\
|
||||||
|
google-pprof --text ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/ToobAmp.lv2/ToobAmp.so /tmp/ToobNam.perf >./ToobNam.txt
|
||||||
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
${SCRIPT_DIR}/../build/src/profilePlugin -w Nam_Profile -o /tmp/nam.perf &&\
|
||||||
|
google-pprof --text ${SCRIPT_DIR}/../build/src/profilePlugin --add_lib /usr/lib/lv2/neural_amp_modeler.lv2/neural_amp_modeler.so /tmp/nam.perf >./nam.txt
|
||||||
@@ -310,6 +310,14 @@ target_link_libraries(pipedaltest PRIVATE PiPedalCommon)
|
|||||||
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}
|
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_executable(profilePlugin
|
||||||
|
profilePluginMain.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(profilePlugin PRIVATE profiler ${PIPEDAL_LIBS})
|
||||||
|
|
||||||
|
target_include_directories(profilePlugin PRIVATE ${PIPEDAL_INCLUDES}
|
||||||
|
)
|
||||||
|
|
||||||
add_executable(jsonTest
|
add_executable(jsonTest
|
||||||
testMain.cpp
|
testMain.cpp
|
||||||
jsonTest.cpp
|
jsonTest.cpp
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ namespace pipedal
|
|||||||
{
|
{
|
||||||
if (shortOption.length() != 0)
|
if (shortOption.length() != 0)
|
||||||
{
|
{
|
||||||
auto option = "-" + longOption;
|
auto option = "-" + shortOption;
|
||||||
options.push_back(new BooleanOption(option, pResult, true));
|
options.push_back(new BooleanOption(option, pResult, true));
|
||||||
options.push_back(new BooleanOption(option + "+", pResult, true));
|
options.push_back(new BooleanOption(option + "+", pResult, true));
|
||||||
options.push_back(new BooleanOption(option + "-", pResult, false));
|
options.push_back(new BooleanOption(option + "-", pResult, false));
|
||||||
|
|||||||
+18
-3
@@ -492,7 +492,7 @@ void PiPedalModel::FirePedalboardChanged(int64_t clientId, bool loadAudioThread)
|
|||||||
if (loadAudioThread)
|
if (loadAudioThread)
|
||||||
{
|
{
|
||||||
// notify the audio thread.
|
// notify the audio thread.
|
||||||
if (audioHost->IsOpen())
|
if (audioHost && audioHost->IsOpen())
|
||||||
{
|
{
|
||||||
LoadCurrentPedalboard();
|
LoadCurrentPedalboard();
|
||||||
|
|
||||||
@@ -2070,6 +2070,21 @@ void PiPedalModel::CheckForResourceInitialization(Pedalboard &pedalboard)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Pedalboard &PiPedalModel::GetPedalboard()
|
||||||
|
{
|
||||||
|
return this->pedalboard;
|
||||||
|
}
|
||||||
|
std::shared_ptr<Lv2Pedalboard> PiPedalModel::GetLv2Pedalboard()
|
||||||
|
{
|
||||||
|
// test only.
|
||||||
|
Lv2PedalboardErrorList errorMessages;
|
||||||
|
std::shared_ptr<Lv2Pedalboard> lv2Pedalboard{this->pluginHost.CreateLv2Pedalboard(this->pedalboard, errorMessages)};
|
||||||
|
if (errorMessages.size() != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(errorMessages[0].message);
|
||||||
|
}
|
||||||
|
return lv2Pedalboard;
|
||||||
|
}
|
||||||
bool PiPedalModel::LoadCurrentPedalboard()
|
bool PiPedalModel::LoadCurrentPedalboard()
|
||||||
{
|
{
|
||||||
Lv2PedalboardErrorList errorMessages;
|
Lv2PedalboardErrorList errorMessages;
|
||||||
@@ -2174,8 +2189,8 @@ UpdateStatus PiPedalModel::GetUpdateStatus()
|
|||||||
void PiPedalModel::UpdateNow(const std::string &updateUrl)
|
void PiPedalModel::UpdateNow(const std::string &updateUrl)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||||
std::filesystem::path fileName,signatureName;
|
std::filesystem::path fileName, signatureName;
|
||||||
updater.DownloadUpdate(updateUrl,&fileName,&signatureName);
|
updater.DownloadUpdate(updateUrl, &fileName, &signatureName);
|
||||||
|
|
||||||
adminClient.InstallUpdate(fileName);
|
adminClient.InstallUpdate(fileName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,6 +251,9 @@ namespace pipedal
|
|||||||
|
|
||||||
|
|
||||||
void SetPedalboard(int64_t clientId, Pedalboard &pedalboard);
|
void SetPedalboard(int64_t clientId, Pedalboard &pedalboard);
|
||||||
|
Pedalboard&GetPedalboard();
|
||||||
|
std::shared_ptr<Lv2Pedalboard> GetLv2Pedalboard();
|
||||||
|
|
||||||
void UpdateCurrentPedalboard(int64_t clientId, Pedalboard &pedalboard);
|
void UpdateCurrentPedalboard(int64_t clientId, Pedalboard &pedalboard);
|
||||||
|
|
||||||
void GetPresets(PresetIndex *pResult);
|
void GetPresets(PresetIndex *pResult);
|
||||||
|
|||||||
+3
-3
@@ -740,9 +740,9 @@ namespace pipedal
|
|||||||
size_t maxBufferSize = 1024;
|
size_t maxBufferSize = 1024;
|
||||||
size_t maxAtomBufferSize = 16 * 1024;
|
size_t maxAtomBufferSize = 16 * 1024;
|
||||||
bool hasMidiInputChannel;
|
bool hasMidiInputChannel;
|
||||||
int numberOfAudioInputChannels = 2;
|
int numberOfAudioInputChannels = 1;
|
||||||
int numberOfAudioOutputChannels = 2;
|
int numberOfAudioOutputChannels = 1;
|
||||||
double sampleRate = 0;
|
double sampleRate = 48000;
|
||||||
|
|
||||||
std::string vst3CachePath;
|
std::string vst3CachePath;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
#include "PiPedalModel.hpp"
|
||||||
|
#include "PiPedalConfiguration.hpp"
|
||||||
|
#include "Pedalboard.hpp"
|
||||||
|
#include "Lv2Log.hpp"
|
||||||
|
#include "RingBufferReader.hpp"
|
||||||
|
#include "CommandLineParser.hpp"
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
// apt install google-perftools
|
||||||
|
#include <gperftools/profiler.h>
|
||||||
|
|
||||||
|
using namespace pipedal;
|
||||||
|
using namespace std;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
// discards data from the ring buffer without discarding it.
|
||||||
|
|
||||||
|
using WriterRingbuffer = RingBuffer<false, true>;
|
||||||
|
|
||||||
|
class RingBufferSink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RingBufferSink(WriterRingbuffer &writerRingBuffer)
|
||||||
|
: writerRingbuffer(writerRingBuffer)
|
||||||
|
{
|
||||||
|
thread = std::make_unique<std::thread>(
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
ThreadProc();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
void Close()
|
||||||
|
{
|
||||||
|
if (!closed)
|
||||||
|
{
|
||||||
|
closed = true;
|
||||||
|
writerRingbuffer.close();
|
||||||
|
terminateThread = true;
|
||||||
|
thread->join();
|
||||||
|
thread = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~RingBufferSink()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ThreadProc()
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> dataVector(1024);
|
||||||
|
uint8_t *data = dataVector.data();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
RingBufferStatus status = writerRingbuffer.readWait_for(std::chrono::milliseconds(10));
|
||||||
|
if (status == RingBufferStatus::Closed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (status == RingBufferStatus::Ready)
|
||||||
|
{
|
||||||
|
size_t available = writerRingbuffer.readSpace();
|
||||||
|
while (available != 0)
|
||||||
|
{
|
||||||
|
size_t thisTime = std::min(dataVector.size(), available);
|
||||||
|
writerRingbuffer.read(thisTime, data);
|
||||||
|
available -= thisTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool closed = false;
|
||||||
|
std::atomic<bool> terminateThread{false};
|
||||||
|
|
||||||
|
std::unique_ptr<std::thread> thread;
|
||||||
|
WriterRingbuffer &writerRingbuffer;
|
||||||
|
};
|
||||||
|
struct ProfileOptions
|
||||||
|
{
|
||||||
|
std::string presetName;
|
||||||
|
float benchmark_seconds = 20;
|
||||||
|
std::string outputFilename = "/tmp/profilePlugin.perf";
|
||||||
|
bool noProfile = false;
|
||||||
|
bool waitForWork = false;
|
||||||
|
size_t frameSize = 64;
|
||||||
|
};
|
||||||
|
|
||||||
|
void profilePlugin(const ProfileOptions &profileOptions)
|
||||||
|
{
|
||||||
|
size_t nFrames = profileOptions.frameSize;
|
||||||
|
Lv2Log::log_level(LogLevel::Info);
|
||||||
|
|
||||||
|
/*** Initialize the model */
|
||||||
|
PiPedalModel model;
|
||||||
|
fs::path doc_root = "/etc/pipedal/config";
|
||||||
|
PiPedalConfiguration configuration;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
configuration.Load(doc_root, "");
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
std::stringstream s;
|
||||||
|
s << "Unable to read configuration from '" << (doc_root / "config.json") << "'. (" << e.what() << ")";
|
||||||
|
throw std::runtime_error(s.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Init(configuration);
|
||||||
|
|
||||||
|
model.LoadLv2PluginInfo();
|
||||||
|
|
||||||
|
// model.Load(); don't start audio.
|
||||||
|
|
||||||
|
/* *** Load the preset. */
|
||||||
|
PresetIndex presetIndex;
|
||||||
|
model.GetPresets(&presetIndex);
|
||||||
|
|
||||||
|
int64_t presetIndexId = 0;
|
||||||
|
bool presetIndexFound = false;
|
||||||
|
for (const auto &preset : presetIndex.presets())
|
||||||
|
{
|
||||||
|
if (preset.name() == profileOptions.presetName)
|
||||||
|
{
|
||||||
|
presetIndexId = preset.instanceId();
|
||||||
|
presetIndexFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!presetIndexFound)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Preset not found.");
|
||||||
|
}
|
||||||
|
model.LoadPreset(-1, presetIndexId);
|
||||||
|
|
||||||
|
auto pedalboard = model.GetCurrentPedalboardCopy();
|
||||||
|
cout << "uri: " << pedalboard.items()[0].uri() << endl;
|
||||||
|
/* *** Get and prepare the audio thread pedalboard. */
|
||||||
|
auto lv2Pedalboard = model.GetLv2Pedalboard();
|
||||||
|
|
||||||
|
lv2Pedalboard->Activate();
|
||||||
|
|
||||||
|
std::vector<float> inputBufferVector;
|
||||||
|
std::vector<float> outputBufferVector;
|
||||||
|
inputBufferVector.resize(nFrames);
|
||||||
|
outputBufferVector.resize(nFrames);
|
||||||
|
|
||||||
|
float *inputBuffers[2]{inputBufferVector.data(), nullptr};
|
||||||
|
float *outputBuffers[2] = {inputBufferVector.data(), nullptr};
|
||||||
|
|
||||||
|
WriterRingbuffer writerRingbuffer;
|
||||||
|
RealtimeRingBufferWriter ringBufferWriter(&writerRingbuffer);
|
||||||
|
|
||||||
|
RingBufferSink ringBufferSink(writerRingbuffer);
|
||||||
|
|
||||||
|
/* *** Pump the plugin for a bit if it is expected to do work on the scheduler thread when initializing */
|
||||||
|
if (profileOptions.waitForWork)
|
||||||
|
{
|
||||||
|
using clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
// idle, pumping the plugin occasionally to allow inital scheduler work to complete.
|
||||||
|
auto waitStart = clock::now();
|
||||||
|
auto waitDuration = std::chrono::duration_cast<clock::duration>(std::chrono::seconds(3));
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
lv2Pedalboard->Run(inputBuffers, outputBuffers, nFrames, &ringBufferWriter);
|
||||||
|
|
||||||
|
auto elapsed = clock::now() - waitStart;
|
||||||
|
if (elapsed >= waitDuration)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // the scheduler thread runs.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *** Run the benchmark */
|
||||||
|
if (!profileOptions.noProfile)
|
||||||
|
{
|
||||||
|
ProfilerStart(profileOptions.outputFilename.c_str());
|
||||||
|
}
|
||||||
|
auto startTime = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
size_t repetitions = static_cast<size_t>(48000 * profileOptions.benchmark_seconds / nFrames);
|
||||||
|
for (size_t i = 0; i < repetitions; ++i)
|
||||||
|
{
|
||||||
|
lv2Pedalboard->Run(inputBuffers, outputBuffers, nFrames, &ringBufferWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto elapsedTime = std::chrono::high_resolution_clock::now() - startTime;
|
||||||
|
if (!profileOptions.noProfile)
|
||||||
|
{
|
||||||
|
ProfilerStop();
|
||||||
|
}
|
||||||
|
std::chrono::milliseconds us = std::chrono::duration_cast<chrono::milliseconds>(elapsedTime);
|
||||||
|
std::cout << "Ellapsed time: " << (us.count() / 1000.0) << "s" << endl;
|
||||||
|
|
||||||
|
ringBufferSink.Close();
|
||||||
|
|
||||||
|
lv2Pedalboard->Deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProfileOptions profileOptions;
|
||||||
|
bool help = false;
|
||||||
|
CommandLineParser commandLineParser;
|
||||||
|
commandLineParser.AddOption("w", "wait-for-work", &profileOptions.waitForWork);
|
||||||
|
commandLineParser.AddOption("", "no-profile", &profileOptions.noProfile);
|
||||||
|
commandLineParser.AddOption("o", "output", &profileOptions.outputFilename);
|
||||||
|
commandLineParser.AddOption("s", "seconds", &profileOptions.benchmark_seconds);
|
||||||
|
commandLineParser.AddOption("h", "help", &help);
|
||||||
|
|
||||||
|
commandLineParser.Parse(argc, (const char **)argv);
|
||||||
|
|
||||||
|
if (commandLineParser.Arguments().size() != 1 || help)
|
||||||
|
{
|
||||||
|
cout << "profilePlugin - Generate profiling data for LV2 Plugins" << endl;
|
||||||
|
cout << "Copyright (c) 2024 Robin E. R. Davies" << endl;
|
||||||
|
cout << endl;
|
||||||
|
cout << "Syntax: profilePlugin preset_name [options...]" << endl;
|
||||||
|
cout << " where preset_name is the name of a PiPedal preset." << endl;
|
||||||
|
cout << endl;
|
||||||
|
cout << " A google-perf profile capture will be written to " << endl;
|
||||||
|
cout << " /tmp/profilePlugin.perf" << endl;
|
||||||
|
cout << endl;
|
||||||
|
cout << "Options:" << endl;
|
||||||
|
cout << " --no-profile:" << endl;
|
||||||
|
cout << " do NOT generate a perf file." << endl;
|
||||||
|
cout << " -o, --output filename:" << endl;
|
||||||
|
cout << " The file to which profiler data is written. " << endl;
|
||||||
|
cout << " Defaults to /tmp/profilePlugin.perf" << endl;
|
||||||
|
cout << " -w, --wait-for-work: " << endl;
|
||||||
|
cout << " Assume that the plugin will load data on the LV2 scheduler thread." << endl;
|
||||||
|
cout << " -s, --seconds time_in_seconds: " << endl;
|
||||||
|
cout << " The number of seconds of audio to process." << endl;
|
||||||
|
cout << " -h, --help: display this message." << endl;
|
||||||
|
cout << endl;
|
||||||
|
return help ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
profileOptions.presetName = commandLineParser.Arguments()[0];
|
||||||
|
|
||||||
|
profilePlugin(profileOptions);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
cerr << "Error: " << e.what() << endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user