Verified Commit 187d3e4a authored by Jan Ziak's avatar Jan Ziak

Use 99th percentile as the maximum frequency when adjusting icon color

When adjusting the icon color according to frequency, compute the 99th
percentile of all measured frequencies and use it as the "true" maximum
frequency. This is required because in a small percentage of cases the
current CPU frequency reported by Linux can be unrealistic, for example
4.59 GHz on a CPU that has an actual maximum frequency of about 4.4 GHz.
parent 9b47a93e
Pipeline #7163 passed with stages
in 1 minute and 4 seconds
......@@ -23,8 +23,9 @@
#include <config.h>
#endif
#include <stdlib.h>
#include <dirent.h>
#include <math.h>
#include <stdlib.h>
#include <libxfce4ui/libxfce4ui.h>
......@@ -111,10 +112,28 @@ cpufreq_update_cpus (gpointer data)
}
for (i = 0; i < cpuFreq->cpus->len; i++)
{
CpuInfo *cpu = g_ptr_array_index (cpuFreq->cpus, i);
guint cur_freq = cpu->cur_freq;
gint bin;
cpu->max_freq_measured = MAX (cpu->max_freq_measured, cur_freq);
bin = round ((cur_freq - FREQ_HIST_MIN) * ((gdouble) FREQ_HIST_BINS / (FREQ_HIST_MAX - FREQ_HIST_MIN)));
if (G_UNLIKELY (bin < 0))
bin = 0;
if (G_UNLIKELY (bin >= FREQ_HIST_BINS))
bin = FREQ_HIST_BINS - 1;
if (G_UNLIKELY (cpuFreq->freq_hist[bin] == G_MAXUINT16))
{
CpuInfo *cpu = g_ptr_array_index (cpuFreq->cpus, i);
cpu->max_freq_measured = MAX (cpu->max_freq_measured, cpu->cur_freq);
// Divide all bin counts by 2
gsize j;
for (j = 0; j < G_N_ELEMENTS (cpuFreq->freq_hist); j++)
cpuFreq->freq_hist[j] /= 2;
}
cpuFreq->freq_hist[bin]++;
}
return cpufreq_update_plugin (FALSE);
}
......@@ -464,31 +464,69 @@ cpufreq_current_cpu (void)
static void
cpufreq_update_pixmap (CpuInfo *cpu)
{
gdouble range, value;
gint index;
const gdouble min_range = 100*1000; /* frequency in kHz */
gdouble freq_98, normalized_freq, range;
gint i, index, total_count;
GdkPixbuf *pixmap;
if (G_UNLIKELY (!cpuFreq->icon || !cpuFreq->base_icon))
return;
if (G_UNLIKELY (cpu->cur_freq < cpu->min_freq))
return;
/* Note:
* max_freq_nominal can have values that are well outside
* max_freq_nominal can have values that are outside
* of the actual maximum frequency of the CPU.
* For example, Linux kernel 5.10.17 reports 5554687 kHz
* for some CPU cores on Ryzen 3700X, but the actual top
* frequency of this CPU is about 4.4 GHz.
* Therefore, rely on max_freq_measured only.
* Therefore, the following algorithm relies on measured
* frequencies only.
*/
range = cpu->max_freq_measured - (gdouble) cpu->min_freq;
if (G_UNLIKELY (range <= 0))
return;
value = (cpu->cur_freq - cpu->min_freq) / range;
index = round (value * (G_N_ELEMENTS (cpuFreq->icon_pixmaps) - 1));
if (G_UNLIKELY (index < 0 || index >= (gint) G_N_ELEMENTS (cpuFreq->icon_pixmaps)))
return;
/* Compute the 99th percentile of all measured frequencies
* and use it as the "true" maximum frequency. This is required
* because in a small percentage of cases the current CPU
* frequency reported by Linux can be unrealistic, for example
* 4.59 GHz on a CPU that has an actual maximum frequency
* of about 4.4 GHz.
*/
total_count = 0;
for (i = 0; i < (gint) G_N_ELEMENTS (cpuFreq->freq_hist); i++)
total_count += cpuFreq->freq_hist[i];
if (total_count * 0.01 < 1)
{
/* Not enough data to reliably compute the percentile,
* resort to a value that isn't based on statistics */
freq_98 = MAX (cpu->max_freq_nominal, cpu->max_freq_measured);
}
else
{
gint percentile_2 = total_count * 0.01;
for (i = G_N_ELEMENTS (cpuFreq->freq_hist) - 1; i >= 0; i--)
{
guint16 count = cpuFreq->freq_hist[i];
if (count < percentile_2)
percentile_2 -= count;
else
{
freq_98 = FREQ_HIST_MIN + i * ((gdouble) (FREQ_HIST_MAX - FREQ_HIST_MIN) / FREQ_HIST_BINS);
break;
}
}
if (G_UNLIKELY (i == -1))
freq_98 = cpu->max_freq_measured;
}
range = freq_98 - cpu->min_freq;
if (cpu->cur_freq > cpu->min_freq && range >= min_range)
normalized_freq = (cpu->cur_freq - cpu->min_freq) / range;
else
normalized_freq = 0;
index = round (normalized_freq * (G_N_ELEMENTS (cpuFreq->icon_pixmaps) - 1));
if (G_UNLIKELY (index < 0))
index = 0;
if (index >= (gint) G_N_ELEMENTS (cpuFreq->icon_pixmaps))
/* This codepath is expected to be reached in 2% of cases */
index = G_N_ELEMENTS (cpuFreq->icon_pixmaps) - 1;
pixmap = cpuFreq->icon_pixmaps[index];
if (!pixmap)
......
......@@ -32,6 +32,10 @@
#define CPU_MAX (-3)
#define CPU_DEFAULT CPU_MAX
#define FREQ_HIST_BINS 128 /* number of bins */
#define FREQ_HIST_MAX (8*1000*1000) /* frequency in kHz */
#define FREQ_HIST_MIN 0 /* frequency in kHz */
typedef enum
{
UNIT_AUTO,
......@@ -108,8 +112,15 @@ typedef struct
gboolean layout_changed;
GdkPixbuf *base_icon;
GdkPixbuf *icon_pixmaps[32]; /* table with frequency color coded pixbufs */
GdkPixbuf *current_icon_pixmap;
GdkPixbuf *icon_pixmaps[32]; /* table with frequency color coded pixbufs */
/* Histogram of measured frequencies:
* min: FREQ_HIST_MIN
* max: FREQ_HIST_MAX
* range: max - min
* resolution: range / FREQ_HIST_BINS = 62.5 MHz */
guint16 freq_hist[FREQ_HIST_BINS];
CpuFreqPluginOptions *options;
gint timeoutHandle;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment