Floor timestamp to the second when displaying age#1140
Conversation
|
Thanks for taking a look at the issue! It seems like this is more a workaround for the bug. Dunst should not redraw the notification when the age is a millisecond different. Is it possible to fix it at the point where age value is checked?
Where are some other areas for improvement? |
Codecov Report
📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more @@ Coverage Diff @@
## master #1140 +/- ##
==========================================
+ Coverage 65.96% 66.09% +0.12%
==========================================
Files 46 46
Lines 7548 7588 +40
==========================================
+ Hits 4979 5015 +36
- Misses 2569 2573 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
I have not gone into the details of what happens during a refresh, but the main problem here seems that if there are eg. 200 notifications, Dunst will be awoken in average once every 5ms. Whether the refresh stack is fast or not, 5ms is a very short timespan; if it takes eg. 2ms of boilerplate and pretty much nothing of actual refresh, this still makes for 40% CPU usage (2ms work every 5ms). Thus, it seems to me that it is crucial not to wake Dunst this often.
Agreed. I don't see easily how that would be a unit test, though.
When profiling (with this patch applied) using $ perf record -g ./dunstand stress-testing with while I have not looked into the details, but from a quick glance, it seems that a lot of time is spent figuring the text size --- here, there are merely 200 notifications. For 200 notifications, it still takes about 150ms to redraw them each second. I'm not sure where the room for improvement mostly is, but I expect that all of this is redrawn only when the text changes, that is, every second. Either part of these computations can be cached from one redraw to the next (but the age part makes it hard, I guess), or maybe it would be a good idea to simply stop drawing when the size of the notification stack exceeds the size of the screen? (I expect this is what the |
|
What you're saying makes sense. Dunst should only wake up once a second to update the timestamps. I would suggest every time the real time gets to the next second. This can be fixed in queues_get_next_datachange in queues.c. This is also the function for which you can write a test. The draw code could be optimized, but I don't think it needs to, since we (should) only draw about once a second at most. |
|
I would also say that the notification limit should help against it. Setting it to some reasonable value by default should also help |
In practice, this is what this PR's code does: by flooring the notifications' timestamps (for display purposes), this ensures that the next wake-up for this purpose is when the real time is at an exact second. It also does not do so when there is no notification age to update. If you prefer, I could rewrite this to do explicitly what you suggest (check that there is one notification needing update, and if so, wait until the next exact second), but I find the code quite elegant this way. I could indeed write a test in any case, though.
This problem is independent, but this is not exactly true: it does trigger only once a second, but with ~200 notifications, the (single) update takes about 150ms, which is already a 15% CPU usage for something that should not, I believe, be compute-intensive. Setting a reasonable |
| gint64 age = time - n->timestamp; | ||
| /* When displaying age, floor the timestamp to the | ||
| * second. See notification.c for details.*/ | ||
| gint64 age = time - (n->timestamp - (n->timestamp % S2US(1))); |
There was a problem hiding this comment.
I'd suggest to move the flooring to inside the if statement. That's a little more explicit and makes sure you aren't too early.
gint64 next_second = time - time % S2US(1);
sleep = MIN(sleep, (S2US(1) - (next_second % S2US(1))));There was a problem hiding this comment.
Done in the current revision.
I have suggested a way that's a little more explicit and hopefully easier to read.
👍
Yes, could you also do that? |
6795da7 to
cf4494e
Compare
Done
Done It also seems to me that dunst.c:95 gint64 sleep = queues_get_next_datachange(now);is actually wrong: computing the sleep delay from gint64 now = time_monotonic_now();is problematic, because between those two lines, all the loop logic happens, some of which might be taking time. It seems reasonable to use the same value for |
|
bc257cf also ensures that |
I see I've changed that in #1073, more specifically in 1fc9cb6. I'll have to think a bit more about why I did that. It comes done to the question if queues_update and queues_get_next_datachange need the same time.
Perhaps this is related: #1078 |
|
I think it might be more sane to calculate the time at which the sleep should end, instead of the duration of the sleep. This way slow code doesn't impact the sleep duration (keep in mind the timer is still not guaranteed to be accurate). |
Actually,
This would indeed avoid the issue above and looks cleaner. I don't believe timer accuracy is needed anyway? However, as I found that with lots of notifications,
It might be, but it doesn't look like the same issue. What I noticed was that dunst woke up about once a second (to update notifications' age), then 1-4ms later, possibly multiple times: eg. sleep duration sequence could be 1s, 2ms, 1ms, 1s, 2ms, 1ms, etc. |
Before this commit, this function returned a time (in microseconds) to be slept until the next update; this is harder to manage and slightly less robust than returning a timestamp.
This is implemented, with tests updated, in the latest revision. |
|
Thanks for taking the time to debug this and find a good solution. I'll merge this now |
|
This PR broke disabling idle_threshold when setting = -1 as spesified in dunstrc |
|
I'll have a look into this as soon as I get time. |
This fixes the issue discussed in #1102; however, profiling shows that this is not the only performance problem when many notifications are active at once — which happens if eg. the computer is left unattended for a long time with
idle_threshold > 0.