manage: propertynotify: Reduce cost of unused size hints 8806b6e2
This patch defers all size hint calculations until they are actually
needed, drastically reducing the number of calls to updatesizehints(),
which can be expensive when called repeatedly (as it currently is during
resizes).

In my unscientific testing this reduces calls to updatesizehints() by
over 90% during a typical work session. There are no functional changes
for users other than an increase in responsiveness after resizes and
a reduction in CPU time.

In slower environments or X servers, this patch also offers an
improvement in responsiveness that is often tangible after resizing a
client that changes hints during resizes.

There are two main motivations to defer this work to the time of hint
application:

1. Some clients, especially terminals using incremental size hints,
   resend XA_WM_NORMAL_HINTS events on resize to avoid fighting with the
   WM or mouse resizing. For example, some terminals like urxvt clear
   PBaseSize and PResizeInc during XResizeWindow and restore them
   afterwards.

   For this reason, after the resize is concluded, we typically receive
   a backlogged XA_WM_NORMAL_HINTS message for each update period with
   movement, which is useless. In some cases one may get hundreds or
   thousands of XA_WM_NORMAL_HINTS messages on large resizes, and
   currently all of these result in a separate updatesizehints() call,
   of which all but the final one are immediately outdated.

   (We can't just blindly discard these messages during resizes like we
   do for EnterNotify, because some of them might actually be for other
   windows, and may not be XA_WM_NORMAL_HINTS events.)

2. For users which use resizehints=0 most of these updates are unused
   anyway -- in the normal case where the client is not floating these
   values won't be used, so there's no need to calculate them up front.

A synthetic test using the mouse to resize a floating terminal window
from roughly 256x256 to 1024x1024 and back again shows that the number
of calls to updatesizehints() goes from over 500 before this patch (one
for each update interval with movement) to 2 after this patch (one for
each hint application), with no change in user visible behaviour.

This also reduces the delay before dwm is ready to process new events
again after a large resize on such a client, as it avoids the thundering
herd of updatesizehints() calls when hundreds of backlogged
XA_WM_NORMAL_HINTS messages appear at once after a resize is finished.
Chris Down · 2022-03-17 15:56 1 file(s) · +5 −3
dwm.c +5 −3
89 89
	float mina, maxa;
90 90
	int x, y, w, h;
91 91
	int oldx, oldy, oldw, oldh;
92 -
	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
92 +
	int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
93 93
	int bw, oldbw;
94 94
	unsigned int tags;
95 95
	int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
345 345
	if (*w < bh)
346 346
		*w = bh;
347 347
	if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
348 +
		if (!c->hintsvalid)
349 +
			updatesizehints(c);
348 350
		/* see last two sentences in ICCCM 4.1.2.3 */
349 351
		baseismin = c->basew == c->minw && c->baseh == c->minh;
350 352
		if (!baseismin) { /* temporarily remove base dimensions */
1059 1061
	XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
1060 1062
	configure(c); /* propagates border_width, if size doesn't change */
1061 1063
	updatewindowtype(c);
1062 -
	updatesizehints(c);
1063 1064
	updatewmhints(c);
1064 1065
	XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
1065 1066
	grabbuttons(c, 0);
1233 1234
				arrange(c->mon);
1234 1235
			break;
1235 1236
		case XA_WM_NORMAL_HINTS:
1236 -
			updatesizehints(c);
1237 +
			c->hintsvalid = 0;
1237 1238
			break;
1238 1239
		case XA_WM_HINTS:
1239 1240
			updatewmhints(c);
1989 1990
	} else
1990 1991
		c->maxa = c->mina = 0.0;
1991 1992
	c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
1993 +
	c->hintsvalid = 1;
1992 1994
}
1993 1995
1994 1996
void