Skip to content
Snippets Groups Projects
profiler.h 3.69 KiB
#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();
};