amanusk / s-tui

Terminal-based CPU stress and monitoring utility
https://amanusk.github.io/s-tui/
GNU General Public License v2.0
4.06k stars 140 forks source link

Wrong power being read on Intel system (lower then reported by powertop) #104

Closed avdudchenko closed 4 years ago

avdudchenko commented 5 years ago

Hi, I observe that this app reads substantially lower power use then shown by powertop, or power statistic monitor. (for example powertop reports 7.33 W use while s-tui reports 2.2 W). Not sure if there is away to address this of this is expected behavior. This is on Ubuntu 18.04 running on lenovo x1 carbon gen 6th.

amanusk commented 5 years ago

Hi @avdudchenko, thanks for your interest in the project. This is expected behaviour. powertop monitors the battery of the system, and estimates power usage according to "how much battery power was used in a given time?" As a result, powertop will account for additional components that use power, such as the screen, RAM, SSD etc. On the other hand s-tui reads registers, present on Intel CPUs, which give a much more accurate report of the power used specifically by the CPU. The CPU and the screen are usually the most power consuming components of a laptop, so if you stress your CPU, you will see an increased power usage in both s-tui and powertop.

avdudchenko commented 5 years ago

amanusk, thanks. I figured that was the case but was not 100% sure on the behavior. Would it be possible to add an option to read total power use (from powertop for example). I'm sure most laptop users would like to see their total power use live in one place. (I might dig through the code and see if I can do it, but my experience with this type of the systems is limited)

P.S: Got it to work by modifying the RaplPowerSource.py. Total system power can be accessed by going to /sys/class/power_supply/BAT0/power_now (on my system at least), power stored in micrwatts. I then simply modified the init and get_power_usage functions to:

def init(self, package_number=0):

New Lines poitining to power file locating and enabling it reading

   self.useTOTALPOWER=True
    self.total_power='/sys/class/power_supply/BAT0/power_now'

   # No more changes in this functions
    self.package_number = package_number
    self.intel_rapl_package_energy_file = os.path.join(
        self.intel_rapl_folder,
        'intel-rapl:%d' % package_number,
        'energy_uj')
    self.intel_rapl_package_max_energy_file = os.path.join(
        self.intel_rapl_folder,
        'intel-rapl:%d' % package_number,
        'constraint_0_max_power_uw')
    if (not os.path.exists(self.intel_rapl_package_energy_file) or not
            os.path.exists(self.intel_rapl_package_max_energy_file)):
        self.is_available = False
        self.last_measurement_time = 0
        self.last_measurement_value = 0
        self.max_power = 0
        self.last_watts = 0
        return
    self.is_available = True
    self.last_measurement_time = time.time()
    self.last_measurement_value = self.read_power_measurement_file()
    self.max_power = 1
    self.last_watts = 0
    self.update()

def get_power_usage(self):

New lines here, reading power from file

    if self.useTOTALPOWER:
        current_measurement_value=self.read_measurement(self.total_power)/1000000.0
        watts_used=current_measurement_value
        current_measurement_time = time.time()
    else:
        if not self.is_available:
            return -1
        current_measurement_value = self.read_power_measurement_file()
        current_measurement_time = time.time()

        joule_used = ((current_measurement_value -
                       self.last_measurement_value) /
                          float(self.MICRO_JOULE_IN_JOULE))
        logging.info("current " + str(current_measurement_value) +
                     " last " + str(self.last_measurement_value))
        seconds_passed = current_measurement_time - self.last_measurement_time
        watts_used = joule_used / seconds_passed
        logging.info("Joule_Used " + str(joule_used) +
                     " seconds_passed " + str(seconds_passed))
   if watts_used > 0:
        # The information on joules used elapses every once in a while,
        # this might lead to negative readings.
        # To prevent this, we keep the last value until the next update
        self.last_watts = watts_used
        logging.info("Power reading elapsed")
    if watts_used > self.max_power:
        self.max_power = math.ceil(watts_used)
        logging.info("Max power updated " + str(self.max_power))
    self.last_measurement_value = current_measurement_value
    self.last_measurement_time = current_measurement_time

P.S I gave up editing this to get code to formating to work, sorry...I don't know why github code formating is so screwed up...

amanusk commented 5 years ago

Thanks @avdudchenko! (I didn't notice you edit) Do you have a branch where I can test your working code?

amanusk commented 4 years ago

Closing this, s-tui reading is supposed to be different than power top. You are welcome to open a PR to add power top in addition.