htop-dev / htop

htop - an interactive process viewer
https://htop.dev/
GNU General Public License v2.0
6.54k stars 441 forks source link

Graph meter style with negative values #1390

Open Explorer09 opened 9 months ago

Explorer09 commented 9 months ago

This thread is for discussion of a new graph style proposal, originally presented by @BenBE in #1387.

This would allow to show a special graph that displays a second data series downwards instead of stacked on top (@eworm-de @Explorer09 : That's what I'd prefer for Disk IO and Network IO meters at least). ... Kinda inspired by graphs like this: 298582710-faae5d47-1465-45d4-a545-ddc12652b836

Foo:⠀⠀⠀⠀⠀⡀⡄⠀⠀⣠⣧
    ⣀⣀⣀⣀⣄⣷⣷⣶⣾⣿⣿
    ⠉⠉⠋⠉⠉⠋⠏⠛⠛⠟⣿
    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹

I would personally call this as "two quadrant" graph display. While I am happy to extend my code in #714 (which display colored, stacked graphs) to cover this display style, I still have doubts about the scope and constraints of it. Rather than jump in and implement it right away, I would like more discussions on this feature in order to make the vision clearer. (Hence this thread.)

If I were to implement the feature, I would make the following constraints:

  1. Scale constraint. The display scale would have to be the same for the upper half (quadrant) and lower half (quadrant) of the graph.
  2. Available scales are in powers of two if total is not fixed. E.g. 1M would mean 2^20 and not 10^6. (There would be no base-10 scales due to data structure limitations, sorry.)
  3. Max number of items. Only meters with one or two items (MeterClass.maxItems < 2) may be displayed in this mode. There can't be items stacked on either side (upper or lower) as the "stacked" color data structure in #714 is not compatible with the new style, i.e. when item stacking happens on both sides.
  4. The "time" axis, i.e. the zero value line, has to stay in the middle of the graph. It would be unable to move up or down. (It could look ugly if you use this style for e.g. temperature graphs; you know what I mean.)
Explorer09 commented 9 months ago

My questions:

  1. Should this new graph style considered a new mode (like in Bar, Text and LED meter modes) or a "sub-style" under the Graph meter mode?
  2. How do we indicate whether the item should display above the zero line or below the zero line? For one-item meters, this is trivial by using the sign bit of the values[0]. What do we do with the two-item meters such as NetworkIO?
  3. Are we going to permit a negative total for a meter object with this feature? Before this, the Meter.total has to be positive or zero (and it must not be Infinity or NaN). Should the sign bit of total indicate any special behavior, or would it work the same as if the sign bit is ignored?
BenBE commented 9 months ago

My questions:

  1. Should this new graph style considered a new mode (like in Bar, Text and LED meter modes) or a "sub-style" under the Graph meter mode?

Yes. Added to the list of supported modes if sensible based on #1387.

  1. How do we indicate whether the item should display above the zero line or below the zero line? For one-item meters, this is trivial by using the sign bit of the values[0]. What do we do with the two-item meters such as NetworkIO?

For two-item meters the first value is plottet upwards, the second downwards. This would typically be the RX (read) direction for the top and TX (write) for the bottom part of the graph.

  1. Are we going to permit a negative total for a meter object with this feature? Before this, the Meter.total has to be positive or zero (and it must not be Infinity or NaN). Should the sign bit of total indicate any special behavior, or would it work the same as if the sign bit is ignored?

The total value is just the same in the two-value case: Simply think of having one value report the RX and the second value report the TX as positive values as normal. The total would be RX+TX as usual. Stacking negative values doesn't normally make sense in the first place.

For rendering the graph think of it basically like drawing both graphs individually at half height, but mirroring the second graph vertically. If you use the one-value case to draw negative values, then it's basically a graph of that value, except that things aren't cut off at the zero line.

Regarding the four constraints:

  1. As the graph is basically one graph, just with the second data series flipped down below the zero line for rendering, I think that comes natural. I.e. scale such, that max(value1, value2) fit in the allotted display space (2 character rows -> 8 braille pixels).
  2. Scaling should in the best case allow for a somewhat "log scale". As data is likely to both include 10 KiB/s values alongside 100 MiB/s peaks we should take care, that the graph isn't just a plain flat-line with some peaks, but reflects that dynamic to some extend. One way could be some sort of bias+scale*rows calculation for the shown pixels (scale starts with 1 and increases whenever bias is larger then rows: For a maximum value shown of 100 MiB this would give log(max)=27 -> bias=6 & scale=3 (const rows=7) -> The lowest pixel above the baseline would indicate at least 64 Bytes/s, then 512 Bytes/s, 4 KiB/s, 32 KiB/s, 256 KiB/s, 2 MiB/s, 16 MiB/s and finally 128 MiB/s (which would trigger the 8th row above base).
  3. ACK. The whole purpose is mainly for graphs like quoted in the initial note. Even handling the special case of just one value isn't strictly necessary for this implementation; I'm not even sure if we currently have any meter that could show negative values in a matter where graphing them would actually make sense. The primary case is having to data series (both capped positive), to show in a graph with the Y axis of the second one flipped vertically.
  4. ACK. Follows from the previous item of this list. Lifting this constraint also would require the implementation to be much more complicated than necessary.
Explorer09 commented 9 months ago
  1. Should this new graph style considered a new mode (like in Bar, Text and LED meter modes) or a "sub-style" under the Graph meter mode?

Yes. Added to the list of supported modes if sensible based on #1387.

Okay. It makes sense to make the new graph a separate mode. The user would have the choice to display RX+TX as stacked bars or separate.

Regarding the constraints:

  1. Scaling should in the best case allow for a somewhat "log scale". As data is likely to both include 10 KiB/s values alongside 100 MiB/s peaks we should take care, that the graph isn't just a plain flat-line with some peaks, but reflects that dynamic to some extend. One way could be some sort of bias+scale*rows calculation for the shown pixels (scale starts with 1 and increases whenever bias is larger then rows: For a maximum value shown of 100 MiB this would give log(max)=27 -> bias=6 & scale=3 (const rows=7) -> The lowest pixel above the baseline would indicate at least 64 Bytes/s, then 512 Bytes/s, 4 KiB/s, 32 KiB/s, 256 KiB/s, 2 MiB/s, 16 MiB/s and finally 128 MiB/s (which would trigger the 8th row above base).

The logarithm scale would be outside the scope of this feature. While I can see the logarithm scale be useful in some use cases, the log scale can contradict some of the goals I want to achieve with the "two quadrant" graph, specifically: