Add support for Intel GPU statistics
Arjan van de Ven
PATCH 4/4 Add support for Intel GPU statistics
Quoted 1 line
From
7bc1ffe1c7d6793bafb9a51d52f797bf51911097Mon Sep 17 00:00:00 2001
From: Arjan van de Ven <arjan(a)linux.intel.com>
Date: Sun, 5 Aug 2012 10:04:02 -0700
Subject: [PATCH 4/4] Add support for Intel GPU statistics
As of the 3.5 kernel, the Intel GPUs report their C states (power gating) via sysfs. This patch will show them as part of the C state tab, arranged like a core inside package 0 (which matches physical topology)
--- src/Makefile.am | 2 +- src/cpu/cpu.cpp | 38 ++++++++++++++++ src/cpu/intel_cpus.h | 23 ++++++++++ src/cpu/intel_gpu.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 1 deletions(-) create mode 100644 src/cpu/intel_gpu.cpp
src/Makefile.am +1 -1
@@ -30,7 +30,7 @@ powertop_SOURCES = parameters/persistent.cpp parameters/learn.cpp parameters/par calibrate/calibrate.h measurement/measurement.cpp measurement/power_supply.cpp \ measurement/measurement.h measurement/acpi.cpp measurement/sysfs.h measurement/sysfs.cpp \ measurement/acpi.h measurement/extech.cpp measurement/power_supply.h measurement/extech.h \ - main.cpp css.h powertop.css + main.cpp css.h powertop.css cpu/intel_gpu.cpp powertop_CXXFLAGS = -fno-omit-frame-pointer -fstack-protector -Wall -Wshadow -Wformat $(NCURSES_CFLAGS) $(PCIUTILS_CFLAGS) $(LIBNL_CFLAGS) $(GLIB2_CFLAGS)
src/cpu/cpu.cpp +38 -0
@@ -123,6 +123,16 @@ static class abstract_cpu * new_core(int core, int cpu, char * vendor, int famil return ret; } +static class abstract_cpu * new_i965_gpu(void) +{ + class abstract_cpu *ret = NULL; + + ret = new class i965_core; + ret->childcount = 0; + + return ret; +} + static class abstract_cpu * new_cpu(int number, char * vendor, int family, int model) { class abstract_cpu * ret = NULL;
@@ -218,6 +228,27 @@ static void handle_one_cpu(unsigned int number, char *vendor, int family, int mo all_cpus[number] = cpu; } +static void handle_i965_gpu(void) +{ + ifstream file; + unsigned int core_number = 0; + class abstract_cpu *package; + + + package = system_level.children[0]; + + core_number = package->children.size(); + + if (package->children.size() <= core_number) + package->children.resize(core_number + 1, NULL); + + if (!package->children[core_number]) { + package->children[core_number] = new_i965_gpu(); + package->childcount++; + } +} + + void enumerate_cpus(void) { ifstream file;
@@ -288,6 +319,13 @@ void enumerate_cpus(void) file.close(); + file.open("/sys/class/drm/card0/power/rc6_residency_ms", ios::in); + + if (file) { + handle_i965_gpu(); + file.close(); + } + perf_events = new perf_power_bundle(); if (!perf_events->add_event("power:cpu_idle")){
src/cpu/intel_cpus.h +23 -0
@@ -137,3 +137,26 @@ public:extern int has_c2c7_res; + +class i965_core: public cpu_core +{ +private: +uint64_t rc6_before, rc6_after; +uint64_t rc6p_before, rc6p_after; +uint64_t rc6pp_before, rc6pp_after; + +struct timeval before; +struct timeval after; + +public: + virtual void measurement_start(void); + virtual void measurement_end(void); + virtual int can_collapse(void) { return 0;}; + + virtual char * fill_pstate_line(int line_nr, char *buffer); + virtual char * fill_pstate_name(int line_nr, char *buffer); + virtual char * fill_cstate_line(int line_nr, char *buffer, const char *separator); + virtual int has_pstate_level(int level) { return 0; }; + virtual int has_pstates(void) { return 0; }; + +};
src/cpu/intel_gpu.cpp new file +116 -0
116 lines not on this page · raw message
Signature · 1 line
1.7.7.6
Sergey Senozhatsky
Hello,
src/cpu/cpu.cpp static void handle_one_cpu(unsigned int number, char *vendor, int family, int mo · line 233
Quoted 4 lines · Arjan van de Ven
src/cpu/cpu.cpp +3
-0+static void handle_i965_gpu(void) +{ + ifstream file;
seems to be unused
Quoted 22 lines
src/cpu/cpu.cpp +18
-0+ unsigned int core_number = 0; + class abstract_cpu *package; + + + package = system_level.children[0]; + + core_number = package->children.size(); + + if (package->children.size() <= core_number) + package->children.resize(core_number + 1, NULL); + + if (!package->children[core_number]) { + package->children[core_number] = new_i965_gpu(); + package->childcount++; + } +} + + void enumerate_cpus(void) { ifstream file;
src/cpu/cpu.cpp void enumerate_cpus(void) · line 328
Quoted 10 lines
src/cpu/cpu.cpp +7
-0@@ -288,6 +319,13 @@ void enumerate_cpus(void) file.close(); + file.open("/sys/class/drm/card0/power/rc6_residency_ms", ios::in); + + if (file) { + handle_i965_gpu(); + file.close(); + } +
side note: just wonder how much C++ stream with its heavy buffering, etc. is slower than stat(). I'll review and if it makes sense will probably prepare simple stat() wrapper to lib.cpp
Quoted 113 lines
src/cpu/cpu.cpp +0
-0perf_events = new perf_power_bundle(); if (!perf_events->add_event("power:cpu_idle")){src/cpu/intel_cpus.h +23
-0@@ -137,3 +137,26 @@ public:extern int has_c2c7_res; + +class i965_core: public cpu_core +{ +private: +uint64_t rc6_before, rc6_after; +uint64_t rc6p_before, rc6p_after; +uint64_t rc6pp_before, rc6pp_after; + +struct timeval before; +struct timeval after; + +public: + virtual void measurement_start(void); + virtual void measurement_end(void); + virtual int can_collapse(void) { return 0;}; + + virtual char * fill_pstate_line(int line_nr, char *buffer); + virtual char * fill_pstate_name(int line_nr, char *buffer); + virtual char * fill_cstate_line(int line_nr, char *buffer, const char *separator); + virtual int has_pstate_level(int level) { return 0; }; + virtual int has_pstates(void) { return 0; }; + +};src/cpu/intel_gpu.cpp new file +73
-0@@ -0,0 +1,116 @@ +/* + * Copyright 2012, Intel Corporation + * + * This file is part of PowerTOP + * + * This program file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program in a file named COPYING; if not, write to the + * Free Software Foundation, Inc, + * 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * or just google for it. + * + * Authors: + * Arjan van de Ven <arjan(a)linux.intel.com> + */ +#include "cpu.h" +#include <iostream> +#include <fstream> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/time.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include "../lib.h" +#include "../parameters/parameters.h" +#include "../display.h" + +void i965_core::measurement_start(void) +{ + ifstream file; + + gettimeofday(&before, NULL); + rc6_before = read_sysfs("/sys/class/drm/card0/power/rc6_residency_ms", NULL); + rc6p_before = read_sysfs("/sys/class/drm/card0/power/rc6p_residency_ms", NULL); + rc6pp_before = read_sysfs("/sys/class/drm/card0/power/rc6pp_residency_ms", NULL); + + update_cstate("gpu c0", "Active", 0, 0, 1, 0); + update_cstate("gpu rc6", "RC6", 0, rc6_before, 1, 1); + update_cstate("gpu rc6p", "RC6p", 0, rc6p_before, 1, 2); + update_cstate("gpu rc6pp", "RC6pp", 0, rc6pp_before, 1, 3); +} + +char * i965_core::fill_cstate_line(int line_nr, char *buffer, const char *separator) +{ + buffer[0] = 0; + double ratio, d = -1.0, time_delta; + + if (line_nr == LEVEL_HEADER) { + sprintf(buffer,_(" GPU ")); + return buffer; + } + + buffer[0] = 0; + + time_delta = 1000000 * (after.tv_sec - before.tv_sec) + after.tv_usec - before.tv_usec; + ratio = 100000.0/time_delta; + + if (line_nr == 0)
src/cpu/intel_gpu.cpp line 83
Quoted 10 lines
src/cpu/intel_gpu.cpp new file +10
-0+ d = 100.0 - ratio * (rc6_after + rc6p_after + rc6pp_after - rc6_before - rc6p_before - rc6pp_before); + if (line_nr == 1) + d = ratio * (rc6_after - rc6_before); + if (line_nr == 2) + d = ratio * (rc6p_after - rc6p_before); + if (line_nr == 3) + d = ratio * (rc6pp_after - rc6pp_before); + if (line_nr >= 4 || line_nr < 0) + return buffer; +
small side note /* someone will do it anyway :-) */:
how about
if (line_nr == 0) d = 100.0 - ratio * (rc6_after + rc6p_after + rc6pp_after - rc6_before - rc6p_before - rc6pp_before); else if (line_nr == 1) d = ratio * (rc6_after - rc6_before); else if (line_nr == 2) d = ratio * (rc6p_after - rc6p_before); else if (line_nr == 3) d = ratio * (rc6pp_after - rc6pp_before); else if (line_nr >= 4 || line_nr < 0) return buffer;
Quoted 41 lines
src/cpu/intel_gpu.cpp new file +33
-0+ /* cope with rounding errors due to the measurement interval */ + if (d < 0.0) + d = 0.0; + if (d > 100.0) + d = 100.0; + + sprintf(buffer,"%5.1f%%", d); + + return buffer; +} + + +void i965_core::measurement_end(void) +{ + gettimeofday(&after, NULL); + + rc6_after = read_sysfs("/sys/class/drm/card0/power/rc6_residency_ms", NULL); + rc6p_after = read_sysfs("/sys/class/drm/card0/power/rc6p_residency_ms", NULL); + rc6pp_after = read_sysfs("/sys/class/drm/card0/power/rc6pp_residency_ms", NULL); +} + +char * i965_core::fill_pstate_line(int line_nr, char *buffer) +{ + buffer[0] = 0; + return buffer; +} + +char * i965_core::fill_pstate_name(int line_nr, char *buffer) +{ + buffer[0] = 0; + return buffer; +} +Signature · 6 lines
1.7.7.6 _______________________________________________ PowerTop mailing list PowerTop(a)lists.01.org https://lists.01.org/mailman/listinfo/powertop
Arjan van de Ven
Quoted 12 lines
src/cpu/cpu.cpp void enumerate_cpus(void) · line 328
Quoted 8 lines
src/cpu/cpu.cpp +7
-0+ file.open("/sys/class/drm/card0/power/rc6_residency_ms", ios::in); + + if (file) { + handle_i965_gpu(); + file.close(); + } +side note: just wonder how much C++ stream with its heavy buffering, etc. is slower than
stat(). I'll review and if it makes sense will probably prepare simplestat()wrapper to lib.cpp
better to use access() than stat.
both have the fun of getting a bunch of low level system headers into C++ I'm sure that'll work most of the time, but for something as non-time critical as this I wonder how badly that is inviting trouble.
Quoted 16 lines
src/cpu/intel_gpu.cpp line 83
Quoted 12 lines
src/cpu/intel_gpu.cpp new file +12
-0+ + if (line_nr == 0) + d = 100.0 - ratio * (rc6_after + rc6p_after + rc6pp_after - rc6_before - rc6p_before - rc6pp_before); + if (line_nr == 1) + d = ratio * (rc6_after - rc6_before); + if (line_nr == 2) + d = ratio * (rc6p_after - rc6p_before); + if (line_nr == 3) + d = ratio * (rc6pp_after - rc6pp_before); + if (line_nr >= 4 || line_nr < 0) + return buffer; +small side note /* someone will do it anyway :-) */:
how about
Quoted 10 lines
if (line_nr == 0) d = 100.0 - ratio * (rc6_after + rc6p_after + rc6pp_after - rc6_before - rc6p_before - rc6pp_before); else if (line_nr == 1) d = ratio * (rc6_after - rc6_before); else if (line_nr == 2) d = ratio * (rc6p_after - rc6p_before); else if (line_nr == 3) d = ratio * (rc6pp_after - rc6pp_before); else if (line_nr >= 4 || line_nr < 0) return buffer;
well last time I looked at the disassembly for such a case, this generated worse code. I'll admit that that was about 2 gcc versions ago though.
Sergey Senozhatsky
Quoted 13 lines · Arjan van de Ven
Quoted 12 lines
src/cpu/cpu.cpp void enumerate_cpus(void) · line 328
Quoted 8 lines
src/cpu/cpu.cpp +7
-0+ file.open("/sys/class/drm/card0/power/rc6_residency_ms", ios::in); + + if (file) { + handle_i965_gpu(); + file.close(); + } +side note: just wonder how much C++ stream with its heavy buffering, etc. is slower than
stat(). I'll review and if it makes sense will probably prepare simplestat()wrapper to lib.cpp
Quoted 7 lines
better to use
access()than stat.both have the fun of getting a bunch of low level system headers into C++ I'm sure that'll work most of the time, but for something as non-time critical as this I wonder how badly that is inviting trouble.
good point, that was just a small nit. I'm ok with one time fstream call.
Quoted 27 lines
Quoted 16 lines
src/cpu/intel_gpu.cpp line 83
Quoted 12 lines
src/cpu/intel_gpu.cpp new file +12
-0+ + if (line_nr == 0) + d = 100.0 - ratio * (rc6_after + rc6p_after + rc6pp_after - rc6_before - rc6p_before - rc6pp_before); + if (line_nr == 1) + d = ratio * (rc6_after - rc6_before); + if (line_nr == 2) + d = ratio * (rc6p_after - rc6p_before); + if (line_nr == 3) + d = ratio * (rc6pp_after - rc6pp_before); + if (line_nr >= 4 || line_nr < 0) + return buffer; +small side note /* someone will do it anyway :-) */:
how aboutQuoted 10 lines
if (line_nr == 0) d = 100.0 - ratio * (rc6_after + rc6p_after + rc6pp_after - rc6_before - rc6p_before - rc6pp_before); else if (line_nr == 1) d = ratio * (rc6_after - rc6_before); else if (line_nr == 2) d = ratio * (rc6p_after - rc6p_before); else if (line_nr == 3) d = ratio * (rc6pp_after - rc6pp_before); else if (line_nr >= 4 || line_nr < 0) return buffer;
Quoted 3 lines
well last time I looked at the disassembly for such a case, this generated worse code. I'll admit that that was about 2 gcc versions ago though.
it's hard to tell nowadays, when GCC is free (and able) to generate even several functions (not in this case, of course) each of those will be specified for known parameter value: e.g. foo.part.1(), foo.part.2(), foo.part.3() for foo(1), foo(2) and foo(3) calls.
on my host (tested on dummy foo() routine) it's something like this (just for note)
gcc version 4.7.1 20120721
-O2
if ()
if ()
if ()
Dump of assembler code for function foo:
0x00000000004006b0 <+0>: cmp $0x1,%edi 0x00000000004006b3 <+3>: je 0x4006d8 <foo+40> 0x00000000004006b5 <+5>: cmp $0x2,%edi 0x00000000004006b8 <+8>: je 0x4006d8 <foo+40> 0x00000000004006ba <+10>: cmp $0x3,%edi 0x00000000004006bd <+13>: jne 0x4006f0 <foo+64> 0x00000000004006bf <+15>: mov %esi,%eax 0x00000000004006c1 <+17>: mov $0x4007bf,%edi 0x00000000004006c6 <+22>: imul %esi,%eax 0x00000000004006c9 <+25>: imul %esi,%eax 0x00000000004006cc <+28>: mov %eax,%esi 0x00000000004006ce <+30>: xor %eax,%eax 0x00000000004006d0 <+32>: jmpq 0x4004a0 <printf(a)plt> 0x00000000004006d5 <+37>: nopl (%rax) 0x00000000004006d8 <+40>: mov %esi,%eax 0x00000000004006da <+42>: imul %esi,%eax 0x00000000004006dd <+45>: mov %eax,%esi 0x00000000004006df <+47>: mov $0x4007bf,%edi 0x00000000004006e4 <+52>: xor %eax,%eax 0x00000000004006e6 <+54>: jmpq 0x4004a0 <printf(a)plt> 0x00000000004006eb <+59>: nopl 0x0(%rax,%rax,1) 0x00000000004006f0 <+64>: jbe 0x400706 <foo+86> 0x00000000004006f2 <+66>: lea (%rsi,%rsi,1),%eax 0x00000000004006f5 <+69>: mov $0x4007bf,%edi 0x00000000004006fa <+74>: imul %esi,%eax 0x00000000004006fd <+77>: mov %eax,%esi 0x00000000004006ff <+79>: xor %eax,%eax 0x0000000000400701 <+81>: jmpq 0x4004a0 <printf(a)plt> 0x0000000000400706 <+86>: xor %eax,%eax 0x0000000000400708 <+88>: jmp 0x4006dd <foo+45>
if ()
else if ()
else if ()
[..]
Dump of assembler code for function foo:
0x00000000004006b0 <+0>: xor %eax,%eax 0x00000000004006b2 <+2>: test %edi,%edi 0x00000000004006b4 <+4>: je 0x4006cd <foo+29> 0x00000000004006b6 <+6>: cmp $0x1,%edi 0x00000000004006b9 <+9>: je 0x4006e0 <foo+48> 0x00000000004006bb <+11>: cmp $0x2,%edi 0x00000000004006be <+14>: je 0x4006e0 <foo+48> 0x00000000004006c0 <+16>: cmp $0x3,%edi 0x00000000004006c3 <+19>: je 0x4006f8 <foo+72> 0x00000000004006c5 <+21>: jbe 0x4006cd <foo+29> 0x00000000004006c7 <+23>: lea (%rsi,%rsi,1),%eax 0x00000000004006ca <+26>: imul %esi,%eax 0x00000000004006cd <+29>: mov %eax,%esi 0x00000000004006cf <+31>: mov $0x4007bf,%edi 0x00000000004006d4 <+36>: xor %eax,%eax 0x00000000004006d6 <+38>: jmpq 0x4004a0 <printf(a)plt> 0x00000000004006db <+43>: nopl 0x0(%rax,%rax,1) 0x00000000004006e0 <+48>: mov %esi,%eax 0x00000000004006e2 <+50>: mov $0x4007bf,%edi 0x00000000004006e7 <+55>: imul %esi,%eax 0x00000000004006ea <+58>: mov %eax,%esi 0x00000000004006ec <+60>: xor %eax,%eax 0x00000000004006ee <+62>: jmpq 0x4004a0 <printf(a)plt> 0x00000000004006f3 <+67>: nopl 0x0(%rax,%rax,1) 0x00000000004006f8 <+72>: mov %esi,%eax 0x00000000004006fa <+74>: imul %esi,%eax 0x00000000004006fd <+77>: imul %esi,%eax 0x0000000000400700 <+80>: jmp 0x4006cd <foo+29>
both are not perfect, comparing to -Os case
if ()
if ()
if ()
Dump of assembler code for function foo:
0x0000000000400691 <+0>: cmp $0x1,%edi 0x0000000000400694 <+3>: jne 0x40069d <foo+12> 0x0000000000400696 <+5>: mov %esi,%eax 0x0000000000400698 <+7>: imul %esi,%eax 0x000000000040069b <+10>: jmp 0x4006b0 <foo+31> 0x000000000040069d <+12>: cmp $0x2,%edi 0x00000000004006a0 <+15>: je 0x400696 <foo+5> 0x00000000004006a2 <+17>: cmp $0x3,%edi 0x00000000004006a5 <+20>: jne 0x4006ae <foo+29> 0x00000000004006a7 <+22>: mov %esi,%eax 0x00000000004006a9 <+24>: imul %esi,%eax 0x00000000004006ac <+27>: jmp 0x4006b8 <foo+39> 0x00000000004006ae <+29>: xor %eax,%eax 0x00000000004006b0 <+31>: cmp $0x3,%edi 0x00000000004006b3 <+34>: jbe 0x4006bb <foo+42> 0x00000000004006b5 <+36>: lea (%rsi,%rsi,1),%eax 0x00000000004006b8 <+39>: imul %esi,%eax 0x00000000004006bb <+42>: mov %eax,%esi 0x00000000004006bd <+44>: mov $0x40077f,%edi 0x00000000004006c2 <+49>: xor %eax,%eax 0x00000000004006c4 <+51>: jmpq 0x4004a0 <printf(a)plt>
if ()
else if ()
else if ()
Dump of assembler code for function foo:
0x0000000000400691 <+0>: xor %eax,%eax 0x0000000000400693 <+2>: test %edi,%edi 0x0000000000400695 <+4>: je 0x4006b9 <foo+40> 0x0000000000400697 <+6>: cmp $0x1,%edi 0x000000000040069a <+9>: jne 0x4006a0 <foo+15> 0x000000000040069c <+11>: mov %esi,%eax 0x000000000040069e <+13>: jmp 0x4006b6 <foo+37> 0x00000000004006a0 <+15>: cmp $0x2,%edi 0x00000000004006a3 <+18>: je 0x40069c <foo+11> 0x00000000004006a5 <+20>: cmp $0x3,%edi 0x00000000004006a8 <+23>: jne 0x4006b1 <foo+32> 0x00000000004006aa <+25>: mov %esi,%eax 0x00000000004006ac <+27>: imul %esi,%eax 0x00000000004006af <+30>: jmp 0x4006b6 <foo+37> 0x00000000004006b1 <+32>: jbe 0x4006b9 <foo+40> 0x00000000004006b3 <+34>: lea (%rsi,%rsi,1),%eax 0x00000000004006b6 <+37>: imul %esi,%eax 0x00000000004006b9 <+40>: mov %eax,%esi 0x00000000004006bb <+42>: mov $0x40077f,%edi 0x00000000004006c0 <+47>: xor %eax,%eax 0x00000000004006c2 <+49>: jmpq 0x4004a0 <printf(a)plt>
-ss