pipedalProfilePlugin - report lv1 cache misses.
This commit is contained in:
Vendored
+32
@@ -205,6 +205,38 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "(gdb) pipedalProfilePlugin",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
// Resolved by CMake Tools:
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
|
||||
"args": [ "--no-profile", "-w", "TooB NAM"],
|
||||
|
||||
"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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "(gdb) processCopyrights Launch",
|
||||
|
||||
@@ -3,3 +3,6 @@
|
||||
cmake --install build --prefix /usr --config Release -v
|
||||
# Done as an install action by the debian package.
|
||||
sudo /usr/bin/pipedalconfig --install
|
||||
# copy pipedalPluginProfile as well.
|
||||
sudo cp build/src/pipedalProfilePlugin /usr/bin/pipedalProfilePlugin
|
||||
chmod +X /usr/bin/pipedalProfilePlugin
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
class CacheMissCounter
|
||||
{
|
||||
private:
|
||||
int fd_l1;
|
||||
int fd_l2;
|
||||
bool affinity_set = false;
|
||||
cpu_set_t original_cpu_set;
|
||||
|
||||
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
||||
int cpu, int group_fd, unsigned long flags)
|
||||
{
|
||||
return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
|
||||
}
|
||||
|
||||
void setup_counter(int &fd, uint64_t config, const std::string &name)
|
||||
{
|
||||
struct perf_event_attr pe;
|
||||
memset(&pe, 0, sizeof(struct perf_event_attr));
|
||||
pe.type = PERF_TYPE_HW_CACHE;
|
||||
pe.size = sizeof(struct perf_event_attr);
|
||||
pe.config = config;
|
||||
pe.disabled = 1;
|
||||
pe.exclude_kernel = 1;
|
||||
pe.exclude_hv = 1;
|
||||
|
||||
fd = perf_event_open(&pe, 0, -1, -1, 0);
|
||||
}
|
||||
|
||||
public:
|
||||
CacheMissCounter() : fd_l1(-1), fd_l2(-1)
|
||||
{
|
||||
uint64_t l1_config = (PERF_COUNT_HW_CACHE_L1D << 0) |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16);
|
||||
uint64_t l2_config = (PERF_COUNT_HW_CACHE_LL << 0) |
|
||||
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16);
|
||||
|
||||
setup_counter(fd_l1, l1_config, "L1");
|
||||
setup_counter(fd_l2, l2_config, "L2");
|
||||
|
||||
int32_t cpu = 1;
|
||||
if (cpu >= 0)
|
||||
{
|
||||
if (pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &original_cpu_set) != 0)
|
||||
{
|
||||
throw std::runtime_error("Failed to get thread affinity");
|
||||
}
|
||||
|
||||
cpu_set_t cpu_set;
|
||||
CPU_ZERO(&cpu_set);
|
||||
CPU_SET(cpu, &cpu_set);
|
||||
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_set) != 0)
|
||||
{
|
||||
throw std::runtime_error("Failed to set thread affinity");
|
||||
}
|
||||
affinity_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
~CacheMissCounter()
|
||||
{
|
||||
if (fd_l1 != -1)
|
||||
close(fd_l1);
|
||||
if (fd_l2 != -1)
|
||||
close(fd_l2);
|
||||
if (affinity_set)
|
||||
{
|
||||
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &original_cpu_set);
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
ioctl(fd_l1, PERF_EVENT_IOC_RESET, 0);
|
||||
ioctl(fd_l2, PERF_EVENT_IOC_RESET, 0);
|
||||
ioctl(fd_l1, PERF_EVENT_IOC_ENABLE, 0);
|
||||
ioctl(fd_l2, PERF_EVENT_IOC_ENABLE, 0);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
ioctl(fd_l1, PERF_EVENT_IOC_DISABLE, 0);
|
||||
ioctl(fd_l2, PERF_EVENT_IOC_DISABLE, 0);
|
||||
}
|
||||
|
||||
long long get_l1_misses()
|
||||
{
|
||||
long long count;
|
||||
read(fd_l1, &count, sizeof(long long));
|
||||
return count;
|
||||
}
|
||||
|
||||
long long get_l2_misses()
|
||||
{
|
||||
if (fd_l2 == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
long long count;
|
||||
read(fd_l2, &count, sizeof(long long));
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
// // Example usage
|
||||
// int main() {
|
||||
// try {
|
||||
// CacheMissCounter counter;
|
||||
|
||||
// counter.start();
|
||||
|
||||
// // Your code to measure goes here
|
||||
// // For example, let's do some memory operations
|
||||
// volatile int* array = new int[1000000];
|
||||
// for (int i = 0; i < 1000000; i++) {
|
||||
// array[i] = i;
|
||||
// }
|
||||
// delete[] array;
|
||||
|
||||
// counter.stop();
|
||||
|
||||
// std::cout << "L1 cache misses: " << counter.get_l1_misses() << std::endl;
|
||||
// std::cout << "L2 cache misses: " << counter.get_l2_misses() << std::endl;
|
||||
|
||||
// } catch (const std::exception& e) {
|
||||
// std::cerr << "Error: " << e.what() << std::endl;
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
}
|
||||
+13
-6
@@ -134,6 +134,9 @@ if (ENABLE_BACKTRACE)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
|
||||
# set(CMAKE_ENABLE_EXPORTS 1)
|
||||
|
||||
@@ -351,15 +354,19 @@ set_target_properties(pipedaltest PROPERTIES EXCLUDE_FROM_ALL true)
|
||||
if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers.
|
||||
|
||||
|
||||
add_executable(profilePlugin
|
||||
profilePluginMain.cpp
|
||||
)
|
||||
target_link_libraries(profilePlugin PRIVATE profiler ${PIPEDAL_LIBS})
|
||||
|
||||
target_include_directories(profilePlugin PRIVATE ${PIPEDAL_INCLUDES}
|
||||
add_executable(pipedalProfilePlugin
|
||||
profilePluginMain.cpp ArmPerformanceCounters.hpp
|
||||
)
|
||||
target_link_libraries(pipedalProfilePlugin PRIVATE profiler ${PIPEDAL_LIBS})
|
||||
|
||||
target_include_directories(pipedalProfilePlugin PRIVATE ${PIPEDAL_INCLUDES}
|
||||
)
|
||||
if (${DEBIAN_ARCHITECTURE} MATCHES arm64)
|
||||
target_compile_definitions(pipedalProfilePlugin PRIVATE "PIPEDAL_AARCH64")
|
||||
endif()
|
||||
|
||||
|
||||
set_target_properties(profilePlugin PROPERTIES EXCLUDE_FROM_ALL true)
|
||||
|
||||
endif()
|
||||
add_executable(jsonTest
|
||||
|
||||
+73
-16
@@ -6,9 +6,11 @@
|
||||
#include "CommandLineParser.hpp"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include "ss.hpp"
|
||||
|
||||
// apt install google-perftools
|
||||
#include <gperftools/profiler.h>
|
||||
#include "ArmPerformanceCounters.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
@@ -79,6 +81,7 @@ private:
|
||||
struct ProfileOptions
|
||||
{
|
||||
std::string presetName;
|
||||
std::string presetFileName;
|
||||
float benchmark_seconds = 20;
|
||||
std::string outputFilename = "/tmp/profilePlugin.perf";
|
||||
bool noProfile = false;
|
||||
@@ -88,6 +91,11 @@ struct ProfileOptions
|
||||
|
||||
void profilePlugin(const ProfileOptions &profileOptions)
|
||||
{
|
||||
|
||||
#ifdef PIPEDAL_AARCH64
|
||||
CacheMissCounter cacheMissCounter;
|
||||
#endif
|
||||
|
||||
size_t nFrames = profileOptions.frameSize;
|
||||
Lv2Log::log_level(LogLevel::Info);
|
||||
|
||||
@@ -113,25 +121,54 @@ void profilePlugin(const ProfileOptions &profileOptions)
|
||||
// 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())
|
||||
bool presetValid = false;
|
||||
|
||||
if (profileOptions.presetName.length() != 0)
|
||||
{
|
||||
if (preset.name() == profileOptions.presetName)
|
||||
PresetIndex presetIndex;
|
||||
model.GetPresets(&presetIndex);
|
||||
|
||||
int64_t presetIndexId = 0;
|
||||
bool presetIndexFound = false;
|
||||
for (const auto &preset : presetIndex.presets())
|
||||
{
|
||||
presetIndexId = preset.instanceId();
|
||||
presetIndexFound = true;
|
||||
break;
|
||||
if (preset.name() == profileOptions.presetName)
|
||||
{
|
||||
presetIndexId = preset.instanceId();
|
||||
presetIndexFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!presetIndexFound)
|
||||
{
|
||||
throw std::runtime_error("Preset not found.");
|
||||
}
|
||||
model.LoadPreset(-1, presetIndexId);
|
||||
}
|
||||
else if (profileOptions.presetFileName.length() != 0)
|
||||
{
|
||||
std::ifstream f(profileOptions.presetFileName);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw std::runtime_error(SS("Unable to load preset file " << profileOptions.presetFileName << "."));
|
||||
}
|
||||
try
|
||||
{
|
||||
json_reader reader(f);
|
||||
Pedalboard pedalboard;
|
||||
reader.read(&pedalboard);
|
||||
model.SetPedalboard(-1, pedalboard);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
throw std::runtime_error(SS("Invalid file format: " << profileOptions.presetFileName << "."));
|
||||
}
|
||||
}
|
||||
if (!presetIndexFound)
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Preset not found.");
|
||||
throw std::runtime_error("You must specify either a preset name or a preset file.");
|
||||
}
|
||||
model.LoadPreset(-1, presetIndexId);
|
||||
|
||||
auto pedalboard = model.GetCurrentPedalboardCopy();
|
||||
cout << "uri: " << pedalboard.items()[0].uri() << endl;
|
||||
@@ -153,7 +190,6 @@ void profilePlugin(const ProfileOptions &profileOptions)
|
||||
|
||||
RingBufferSink ringBufferSink(writerRingbuffer);
|
||||
|
||||
|
||||
// run once to get memory allocations in NAM and ML out of the way.
|
||||
lv2Pedalboard->Run(inputBuffers, outputBuffers, nFrames, &ringBufferWriter);
|
||||
|
||||
@@ -184,6 +220,10 @@ void profilePlugin(const ProfileOptions &profileOptions)
|
||||
{
|
||||
ProfilerStart(profileOptions.outputFilename.c_str());
|
||||
}
|
||||
#ifdef PIPEDAL_AARCH64
|
||||
cacheMissCounter.start();
|
||||
#endif
|
||||
|
||||
auto startTime = std::chrono::system_clock::now();
|
||||
|
||||
size_t repetitions = static_cast<size_t>(48000 * profileOptions.benchmark_seconds / nFrames);
|
||||
@@ -193,12 +233,21 @@ void profilePlugin(const ProfileOptions &profileOptions)
|
||||
}
|
||||
|
||||
auto elapsedTime = std::chrono::high_resolution_clock::now() - startTime;
|
||||
|
||||
#ifdef PIPEDAL_AARCH64
|
||||
cacheMissCounter.stop();
|
||||
#endif
|
||||
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;
|
||||
#ifdef PIPEDAL_AARCH64
|
||||
std::cout << "L1 Cache Misses: " << cacheMissCounter.get_l1_misses() << endl;
|
||||
// non-functional on Raspberry PI OS.
|
||||
// std::cout << "L2 Cache Misses: " << cacheMissCounter.get_l2_misses() << endl;
|
||||
#endif
|
||||
|
||||
ringBufferSink.Close();
|
||||
|
||||
@@ -212,8 +261,10 @@ int main(int argc, char **argv)
|
||||
ProfileOptions profileOptions;
|
||||
bool help = false;
|
||||
CommandLineParser commandLineParser;
|
||||
std::string presetFileName;
|
||||
commandLineParser.AddOption("w", "wait-for-work", &profileOptions.waitForWork);
|
||||
commandLineParser.AddOption("", "no-profile", &profileOptions.noProfile);
|
||||
commandLineParser.AddOption("p", "preset-file", &presetFileName);
|
||||
commandLineParser.AddOption("o", "output", &profileOptions.outputFilename);
|
||||
commandLineParser.AddOption("s", "seconds", &profileOptions.benchmark_seconds);
|
||||
commandLineParser.AddOption("h", "help", &help);
|
||||
@@ -225,7 +276,7 @@ int main(int argc, char **argv)
|
||||
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 << "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;
|
||||
@@ -234,6 +285,8 @@ int main(int argc, char **argv)
|
||||
cout << "Options:" << endl;
|
||||
cout << " --no-profile:" << endl;
|
||||
cout << " do NOT generate a perf file." << endl;
|
||||
cout << " -p, --preset-file filename:" << endl;
|
||||
cout << " Load the specified preset file. " << endl;
|
||||
cout << " -o, --output filename:" << endl;
|
||||
cout << " The file to which profiler data is written. " << endl;
|
||||
cout << " Defaults to /tmp/profilePlugin.perf" << endl;
|
||||
@@ -241,12 +294,16 @@ int main(int argc, char **argv)
|
||||
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 << " -h, --help: display this message." << endl;
|
||||
cout << endl;
|
||||
return help ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
profileOptions.presetName = commandLineParser.Arguments()[0];
|
||||
if (commandLineParser.Arguments().size() == 1)
|
||||
{
|
||||
profileOptions.presetName = commandLineParser.Arguments()[0];
|
||||
}
|
||||
profileOptions.presetFileName = presetFileName;
|
||||
|
||||
profilePlugin(profileOptions);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user