Skip to content
Snippets Groups Projects
Commit a1b00e80 authored by Elizabeth's avatar Elizabeth
Browse files

Separated .cpp and .h files into different dirs

parent 9e890e90
No related branches found
No related tags found
No related merge requests found
cmake_minimum_required(VERSION 3.5)
set(libsrc profiler.cpp)
set(libsrc src/profiler.cpp)
set (CMAKE_CXX_STANDARD 11)
add_library(gpu_profiler STATIC ${libsrc})
target_include_directories(gpu_profiler PRIVATE include)
#include <atomic>
#include <chrono>
#include <cmath>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
// Reads power rails at runtime and computes the GPU and DDR energy within a window
// of time, which is delimitered by the calls to resume_profiler() and pause_profiler()
//
// IMPORTANT: Must call pause_profiler() to kill the profiler thread
//
// Public interface methods:
// void start_profiler();
// void resume_profiler();
// void pause_profiler();
// std::pair<double, double> get_time_energy() const;
// void reset()
// void pause_profiler();
class Profiler {
public:
Profiler();
~Profiler();
// Reinitializes boolean vars used for control flow and launches the profiler
// thread. DOES NOT reset other internal data structures.
void start_profiler();
// Resumes the profiling of whatever executable's currently running
// DOES NOT reset any data
void resume_profiler();
// Stops profiler by putting profiler thread to sleep
void pause_profiler();
// Gets the delta time and total GPU and DDR energy between the last two
// calls to resume_profiler and pause_profiler
//
// Returns this as a pair of <delta time in milliseconds, energy>
std::pair<double, double> get_time_energy() const;
// Resets all internal data structures, including the vector storing all power_readings.
void reset();
// Exit the profiler and kill the thread
// Must call start_profiler() to reuse this object after calling pause_profiler()
void stop_profiler();
private:
// Jetson's ARM cores' physical IDs. The two Denver cores are 1 and 2, and
// we can't use them.
const unsigned core0 = 0;
const unsigned core1 = 3;
const unsigned core2 = 4;
const unsigned core3 = 5;
// Power rails are mounted as files. Keeping the old power rail file names for possible future
// integrations
const std::string cpu_power_rail = "/sys/devices/3160000.i2c/i2c-0/0-0041/iio_device/in_power1_input";
const std::string gpu_power_rail = "/sys/devices/3160000.i2c/i2c-0/0-0040/iio_device/in_power0_input";
const std::string ddr_power_rail = "/sys/devices/3160000.i2c/i2c-0/0-0041/iio_device/in_power2_input";
const std::string soc_power_rail = "/sys/devices/3160000.i2c/i2c-0/0-0040/iio_device/in_power1_input";
const std::string sys_power_rail = "/sys/devices/3160000.i2c/i2c-0/0-0041/iio_device/in_power0_input";
// An individual power reading
struct PowerReading {
std::chrono::time_point<std::chrono::high_resolution_clock> time_;
double cpu_;
double gpu_;
double ddr_;
double soc_;
double sys_;
};
// Stores all power readings and is cleared only when reset() is called
std::vector<PowerReading> power_readings_;
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_;
// For reading the i2c buses via sysfs
std::ifstream cpu_stream_;
std::ifstream gpu_stream_;
std::ifstream ddr_stream_;
std::ifstream soc_stream_;
std::ifstream sys_stream_;
std::mutex mutex_;
std::condition_variable cond_var_;
bool should_run_profiler_; // True if we want to resume the profiling thread
std::atomic_bool should_stop_profiler_; // Quit profiling
std::thread profiler_thread_;
// Obtain's a single power reading from the GPU and DDR rails
void obtain_power_reading();
// Pins the given thread to the specified core
void pin_thread(std::thread &t, const unsigned core) const;
// Runs the profiler thread, keeping it alive by wrapping the functionality
// in an infinite loop
void run_profiler();
};
This diff is collapsed.
#include "profiler.h"
Profiler::Profiler() : should_run_profiler_(false), should_stop_profiler_(false) {
// Open all streams. Not done in start_profiler() function bc the streams
// should be strictly opened once
cpu_stream_.open(cpu_power_rail, std::ifstream::in);
gpu_stream_.open(gpu_power_rail, std::ifstream::in);
ddr_stream_.open(ddr_power_rail, std::ifstream::in);
soc_stream_.open(soc_power_rail, std::ifstream::in);
sys_stream_.open(sys_power_rail, std::ifstream::in);
if (!cpu_stream_.is_open() || !gpu_stream_.is_open() || !ddr_stream_.is_open()
|| !soc_stream_.is_open() || !sys_stream_.is_open()) {
std::cout << "Failed to open one of the power rails for reading\n";
exit(1);
}
}
Profiler::~Profiler() {
cpu_stream_.close();
gpu_stream_.close();
ddr_stream_.close();
soc_stream_.close();
sys_stream_.close();
}
// Reinitializes boolean vars used for control flow and launches the profiler
// thread. DOES NOT reset other internal data structures.
void Profiler::start_profiler(){
// Reinitialize in case the profiler object has been used before
should_run_profiler_ = false;
should_stop_profiler_ = false;
// Launch profiler thread
profiler_thread_ = std::thread(&Profiler::run_profiler, this);
pin_thread(profiler_thread_, core1);
}
// Resumes the profiling of whatever executable's currently running
// DOES NOT reset any data
void Profiler::resume_profiler() {
{
std::unique_lock<std::mutex> mutex_lock(mutex_);
if (should_run_profiler_){
std::cout << "WARNING: resume_profiler was already called\n";
}
should_run_profiler_ = true;
start_time_ = std::chrono::high_resolution_clock::now();
}
cond_var_.notify_one();
}
// Stops profiler by putting profiler thread to sleep
void Profiler::pause_profiler() {
{
std::unique_lock<std::mutex> mutex_lock(mutex_);
if (!should_run_profiler_){
std::cout << "WARNING: pause_profiler was already called\n";
}
should_run_profiler_ = false;
}
cond_var_.notify_one();
}
// Gets the delta time and total GPU and DDR energy between the last two
// calls to resume_profiler and pause_profiler
//
// Returns this as a pair of <delta time in milliseconds, energy>
std::pair<double, double> Profiler::get_time_energy() const {
double total_energy = 0.0;
std::chrono::time_point<std::chrono::high_resolution_clock> prev_time = start_time_;
for (auto reading : power_readings_) {
std::chrono::duration<double> duration = reading.time_ - prev_time;
total_energy += reading.gpu_ * duration.count();
total_energy += reading.ddr_ * duration.count();
prev_time = reading.time_;
}
double delta_time = std::chrono::duration<double, std::milli>(prev_time
- start_time_).count();
return std::make_pair(delta_time, total_energy);
}
// Resets all internal data structures, including the vector storing all power_readings.
void Profiler::reset() {
should_stop_profiler_ = false; // Can call reset after calling pause_profiler()
should_run_profiler_ = false; // Can call reset after calling resume
power_readings_.clear();
}
// Exit the profiler and kill the thread
// Must call start_profiler() to reuse this object after calling pause_profiler()
void Profiler::stop_profiler() {
std::cout << "Exiting profiler\n";
should_stop_profiler_ = true;
cond_var_.notify_one();
profiler_thread_.join();
}
// Obtain's a single power reading from the GPU and DDR rails
void Profiler::obtain_power_reading() {
PowerReading reading;
// The order matters here. All the reads have to happen together first
// and then all the seeks have to happen together at the end, otherwise
// there will be a significant time difference between the readings of
// the different rails.
reading.time_ = std::chrono::high_resolution_clock::now();
gpu_stream_ >> reading.gpu_;
ddr_stream_ >> reading.ddr_;
power_readings_.push_back(reading);
// Reset the input position of the files
gpu_stream_.seekg(0);
ddr_stream_.seekg(0);
}
// Pins the given thread to the specified core
void Profiler::pin_thread(std::thread &t, const unsigned core) const {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core, &cpuset);
if (pthread_setaffinity_np(t.native_handle(), sizeof(cpu_set_t), &cpuset) != 0)
std::cout << "Couldn't set thread affinity\n";
}
// Runs the profiler thread, keeping it alive by wrapping the functionality
// in an infinite loop
void Profiler::run_profiler(){
while (true){
if (should_stop_profiler_) {
break;
}
// Need to lock the mutex and check the condition var
{
std::unique_lock<std::mutex> mutex_lock(mutex_);
if (should_stop_profiler_) {
break;
}
// Wake the thread up when it's time to run the profiler or exit
// the profiler
cond_var_.wait(mutex_lock, [this]{return should_run_profiler_
|| should_stop_profiler_; });
}
if (should_stop_profiler_) {
break;
}
obtain_power_reading();
}
}
/*
// TESTS
void resume_pause_profiler(Profiler& profile_wrapper, unsigned long sleep_millis){
profile_wrapper.resume_profiler();
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_millis));
profile_wrapper.pause_profiler();
auto time_energy_pair = profile_wrapper.get_time_energy();
profile_wrapper.reset();
printf("time: %f, energy: %f\n", time_energy_pair.first, time_energy_pair.second);
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_millis));
}
int main(){
Profiler profile_wrapper;
profile_wrapper.start_profiler();
unsigned long sleep_millis = 500;
resume_pause_profiler(profile_wrapper, sleep_millis);
resume_pause_profiler(profile_wrapper, sleep_millis);
resume_pause_profiler(profile_wrapper, sleep_millis);
resume_pause_profiler(profile_wrapper, sleep_millis);
// IMPORTANT
profile_wrapper.stop_profiler();
return 0;
}
*/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment