CPUMeter: Improve spacing between sub-meter algorithm
Fixes #1654. The formula is derived by myself.
Interestingly, I asked both ChatGPT and Google Gemini (AI) on how to make a formula based on a table of given values, they either gave me a wrong result or simply fail to make one. (I don't trust these AIs in general, by the way.) (https://chatgpt.com/share/67e6fb4a-da80-8004-a993-11cc19666fad) (https://g.co/gemini/share/356fa79d2f5a)
Please explain those magic numbers …
It's a bit difficult to explain in code. I wonder if you or other people have ideas to make the code more readable (maybe wrap it into a function or something - I don't know how to name such function, though).
Anyway. Here're my explanations:
- The goal is to make the sub-meters of each CPU core/thread aligned despite the number of sub-columns of a CPU meter (group).
- Currently, there can only be 2, 4 or 8 sub-columns for a meter.
- The idea is to adjust the distribution of "spaces" between sub-meters based on the remainder of (
wmodncol). Since the width of each sub-meter is floor(w/ncol), the total number of spaces to distribute would be equal to (wmodncol). - When the remainder is a multiple of 2, there should not be a space inserted in the (
ncol/ 2) position or otherwise this meter (group) would appear misaligned with other meter groups with 2 sub-columns. - Apply the similar rule: When the remainder is a multiple of N, there should not be a space inserted in the (
ncol* M / N) position (where M is any integer in the range [1, N-1]) or otherwise this meter (group) would appear misaligned with other meter groups with N sub-columns. - The problem may be more complicated if N can be any positive integer (it would require integer factorisation and a loop), so I narrow the problem scope to cases where N is a power of two, like 2, 4, 8, 16 and 32.
Pseudocode:(EDIT: this pseudocode is inaccurate in describing the actual algorithm; I will correct it later.)
spacing_function(ncol, i, w) {
// 'i' is column index of the sub-meter.
// assert(i >=0 && i < ncol);
w %= ncol;
res = 0;
foreach (N in {2, 4, 8, 16, 32}) {
if ((w + 1) % N == 0)
res += (int)(i * N / ncol);
}
return res;
}
It technically requires a loop. So I use bit manipulation tricks to simplify this, utilising N is a power of two within the narrowed problem scope, and came up with the magic numbers for multiplication and mask values.
((w % ncol) * 32 / ncol) // Maximum number of columns is 32 for this formula.
(v * 0x00210842U) & 0x02082082U) // Break the remainder of (w % ncol) into 5 separate bits, and reverse the bit order.
... * (unsigned int)(i / nrows) // Make up to 5 copies of this column index value, but if ((w + 1) % N != 0) set that copy to zero.
(v + 0x02108420U) & 0x7DE71840U) // Compute the addends that's equivalent to the (int)(i * N / ncol) in the pseudocode, for each copy.
(... * 0x00210842ULL) >> 27 // Sum up all 5 numbers for the final result.
Before
w=56 | [99.5%][99.5%][99.5%][99.5%][99.5%][99.5%][99.5%][99.5%]
w=57 | [99.5%] [99.5%][99.5%][99.5%][99.5%][99.5%][99.5%][99.5%]
w=58 | [99.5%] [99.5%] [99.5%][99.5%][99.5%][99.5%][99.5%][99.5%]
w=59 | [99.5%] [99.5%] [99.5%] [99.5%][99.5%][99.5%][99.5%][99.5%]
w=60 | [99.5%] [99.5%] [99.5%] [99.5%] [99.5%][99.5%][99.5%][99.5%]
w=61 | [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%][99.5%][99.5%]
w=62 | [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%][99.5%]
w=63 | [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%]
w=64 | [|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%]
w=65 | [|99.5%] [|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%]
w=66 | [|99.5%] [|99.5%] [|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%]
w=67 | [|99.5%] [|99.5%] [|99.5%] [|99.5%][|99.5%][|99.5%][|99.5%][|99.5%]
w=68 | [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%][|99.5%][|99.5%][|99.5%]
w=69 | [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%][|99.5%][|99.5%]
w=70 | [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%][|99.5%]
w=71 | [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%]
w=72 | [||99.5%][||99.5%][||99.5%][||99.5%][||99.5%][||99.5%][||99.5%][||99.5%]
After
w=56 | [99.5%][99.5%][99.5%][99.5%][99.5%][99.5%][99.5%][99.5%]
w=57 | [99.5%][99.5%][99.5%][99.5%] [99.5%][99.5%][99.5%][99.5%]
w=58 | [99.5%][99.5%] [99.5%][99.5%][99.5%][99.5%] [99.5%][99.5%]
w=59 | [99.5%][99.5%] [99.5%][99.5%] [99.5%][99.5%] [99.5%][99.5%]
w=60 | [99.5%] [99.5%][99.5%] [99.5%][99.5%] [99.5%][99.5%] [99.5%]
w=61 | [99.5%] [99.5%][99.5%] [99.5%] [99.5%] [99.5%][99.5%] [99.5%]
w=62 | [99.5%] [99.5%] [99.5%] [99.5%][99.5%] [99.5%] [99.5%] [99.5%]
w=63 | [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%] [99.5%]
w=64 | [|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%][|99.5%]
w=65 | [|99.5%][|99.5%][|99.5%][|99.5%] [|99.5%][|99.5%][|99.5%][|99.5%]
w=66 | [|99.5%][|99.5%] [|99.5%][|99.5%][|99.5%][|99.5%] [|99.5%][|99.5%]
w=67 | [|99.5%][|99.5%] [|99.5%][|99.5%] [|99.5%][|99.5%] [|99.5%][|99.5%]
w=68 | [|99.5%] [|99.5%][|99.5%] [|99.5%][|99.5%] [|99.5%][|99.5%] [|99.5%]
w=69 | [|99.5%] [|99.5%][|99.5%] [|99.5%] [|99.5%] [|99.5%][|99.5%] [|99.5%]
w=70 | [|99.5%] [|99.5%] [|99.5%] [|99.5%][|99.5%] [|99.5%] [|99.5%] [|99.5%]
w=71 | [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%] [|99.5%]
w=72 | [||99.5%][||99.5%][||99.5%][||99.5%][||99.5%][||99.5%][||99.5%][||99.5%]