Add an option for using a different governor for integrated GPUs

This commit adds two new configuration options: igpu_desiredgov and
igpu_power_threshold which allow for a different CPU governor when the
Intel integrated GPU is under load.  This currently only applies to
Intel integrated GPUs and not AMD APUs because it uses the Intel RAPL
infrastructure for getting power information.  If on a platform that
without an Intel integrated GPU or where the kernel does not support
RAPL, the new options will be ignored and it will fall back to the old
behavior.

One of the core principals of gamemoded to date has been that, when
playing a game, we want to use the "performance" CPU governor to
increase CPU performance and prevent CPU-limiting.  However, when the
integrated GPU is under load, this can be counter-productive because the
CPU and GPU share a thermal and power budget.  By throwing the CPU
governor to "performance" game mode currently makes the CPU frequency
management far too aggressive and it burns more power than needed.  With
a discrete GPU, this is fine because the worst that happens is a bit
more fan noise.  With an integrated GPU, however, the additional power
being burned by the CPU is power not available to the GPU and this can
cause the GPU to clock down and lead to significantly worse performance.

By using the "powersave" governor instead of the "performance" governor
while the integrated GPU is under load, we can save power on the CPU
side which lets the GPU clock up higher.  On my Razer Blade Stealth 13
with an i7-1065G7, this improves the performance of "Shadow of the Tomb
Raider" by around 25-30% according to its internal benchmark mode.
This commit is contained in:
Jason Ekstrand
2019-12-16 12:32:01 -06:00
parent c1646ecdd9
commit 688373a260
4 changed files with 182 additions and 2 deletions

View File

@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "ini.h"
#include <dirent.h>
#include <math.h>
#include <pthread.h>
#include <pwd.h>
#include <sys/inotify.h>
@ -50,6 +51,8 @@ POSSIBILITY OF SUCH DAMAGE.
/* Default value for the reaper frequency */
#define DEFAULT_REAPER_FREQ 5
#define DEFAULT_IGPU_POWER_THRESHOLD 0.3f
/* Helper macro for defining the config variable getter */
#define DEFINE_CONFIG_GET(name) \
long config_get_##name(GameModeConfig *self) \
@ -82,6 +85,9 @@ struct GameModeConfig {
char defaultgov[CONFIG_VALUE_MAX];
char desiredgov[CONFIG_VALUE_MAX];
char igpu_desiredgov[CONFIG_VALUE_MAX];
float igpu_power_threshold;
char softrealtime[CONFIG_VALUE_MAX];
long renice;
@ -179,6 +185,27 @@ __attribute__((unused)) static bool get_long_value_hex(const char *value_name, c
return true;
}
/*
* Get a long value from a string
*/
static bool get_float_value(const char *value_name, const char *value, float *output)
{
char *end = NULL;
float config_value = strtof(value, &end);
if (errno == ERANGE) {
LOG_ERROR("Config: %s overflowed, given [%s]\n", value_name, value);
return false;
} else if (!(*value != '\0' && end && *end == '\0')) {
LOG_ERROR("Config: %s was invalid, given [%s]\n", value_name, value);
return false;
} else {
*output = config_value;
}
return true;
}
/**
* Simple strstr scheck
* Could be expanded for wildcard or regex
@ -230,6 +257,10 @@ static int inih_handler(void *user, const char *section, const char *name, const
valid = get_string_value(value, self->values.defaultgov);
} else if (strcmp(name, "desiredgov") == 0) {
valid = get_string_value(value, self->values.desiredgov);
} else if (strcmp(name, "igpu_desiredgov") == 0) {
valid = get_string_value(value, self->values.igpu_desiredgov);
} else if (strcmp(name, "igpu_power_threshold") == 0) {
valid = get_float_value(name, value, &self->values.igpu_power_threshold);
} else if (strcmp(name, "softrealtime") == 0) {
valid = get_string_value(value, self->values.softrealtime);
} else if (strcmp(name, "renice") == 0) {
@ -329,6 +360,7 @@ static void load_config_files(GameModeConfig *self)
memset(&self->values, 0, sizeof(self->values));
/* Set some non-zero defaults */
self->values.igpu_power_threshold = DEFAULT_IGPU_POWER_THRESHOLD;
self->values.inhibit_screensaver = 1; /* Defaults to on */
self->values.reaper_frequency = DEFAULT_REAPER_FREQ;
self->values.gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */
@ -641,6 +673,35 @@ void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALU
memcpy_locked_config(self, governor, self->values.desiredgov, sizeof(self->values.desiredgov));
}
/*
* Get the chosen iGPU desired governor
*/
void config_get_igpu_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self,
governor,
self->values.igpu_desiredgov,
sizeof(self->values.igpu_desiredgov));
}
/*
* Get the chosen iGPU power threshold
*/
float config_get_igpu_power_threshold(GameModeConfig *self)
{
float value = 0;
memcpy_locked_config(self, &value, &self->values.igpu_power_threshold, sizeof(float));
/* Validate the threshold value */
if (isnan(value) || value < 0) {
LOG_ONCE(ERROR,
"Configured iGPU power threshold value '%f' is invalid, ignoring iGPU default "
"governor.\n",
value);
value = FP_INFINITE;
}
return value;
}
/*
* Get the chosen soft realtime behavior
*/