perf(wave1): reduce Config caps, reorder Client/Monitor for cache
Wave 1 changes: - Reduce CFG_MAX_KEYBINDS 256→128, CFG_MAX_STRLEN 256→64, CFG_MAX_ARGS 16→8 → saves ~2.5 MB BSS from Config struct - Add truncation warnings to config_load when keybind/button/scroll limit reached - Reorder Client struct: HOT fields (type, mon, tags, floating flags, geom) in first cache line, COLD fields (wl_listeners) at end — saves cache misses in arrange() - Reorder LayerSurface struct similarly - Split Monitor into Monitor/MonitorCold: hot fields (~120B) stay inline, cold layout state lazily allocated on first dwindle/master use - All cold-field accesses through m->cold-> indirection - cleanupmon frees m->cold - Zero-warning build with clang -Werror -Wpedantic
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "client.h"
|
||||
#include "ipc.h"
|
||||
#include "layout.h"
|
||||
#include "util.h"
|
||||
|
||||
/* IPC globals */
|
||||
@@ -75,7 +76,7 @@ ipc_send_output_state(IpcOutput *ipc_out)
|
||||
}
|
||||
|
||||
/* layout symbol */
|
||||
zpeachwm_ipc_output_v2_send_layout_symbol(res, m->ltsymbol[current_tag_idx(m)]);
|
||||
zpeachwm_ipc_output_v2_send_layout_symbol(res, m->cold->ltsymbol[current_tag_idx(m)]);
|
||||
|
||||
/* title/appid/fullscreen/floating */
|
||||
if (c) {
|
||||
@@ -171,10 +172,11 @@ ipc_output_handle_set_layout(struct wl_client *client,
|
||||
if (!m || idx >= (uint32_t)layout_count) return;
|
||||
|
||||
int ti = current_tag_idx(m);
|
||||
if (m->lt[ti][m->sellt[ti]] != &layouts[idx])
|
||||
m->sellt[ti] ^= 1;
|
||||
m->lt[ti][m->sellt[ti]] = &layouts[idx];
|
||||
strncpy(m->ltsymbol[ti], layouts[idx].symbol, LENGTH(m->ltsymbol[ti]));
|
||||
ensure_cold(m);
|
||||
if (m->cold->lt[ti][m->cold->sellt[ti]] != &layouts[idx])
|
||||
m->cold->sellt[ti] ^= 1;
|
||||
m->cold->lt[ti][m->cold->sellt[ti]] = &layouts[idx];
|
||||
strncpy(m->cold->ltsymbol[ti], layouts[idx].symbol, LENGTH(m->cold->ltsymbol[ti]));
|
||||
arrange(m);
|
||||
printstatus();
|
||||
}
|
||||
|
||||
+2
-2
@@ -384,7 +384,7 @@ ipc_build_workspace(struct json_writer *w, Monitor *m, uint32_t tag_bit,
|
||||
json_object_end(w);
|
||||
|
||||
json_key_string(w, "output", m->wlr_output->name);
|
||||
json_key_string(w, "layout", m->ltsymbol[tag_idx]);
|
||||
json_key_string(w, "layout", m->cold->ltsymbol[tag_idx]);
|
||||
json_key_string(w, "representation", "");
|
||||
json_key_string(w, "type", "workspace");
|
||||
|
||||
@@ -675,7 +675,7 @@ ipc_handle_get_tree(struct ipc_client *client)
|
||||
json_key_string(&w, "type", "workspace");
|
||||
json_key_string(&w, "name",
|
||||
ipc_tag_name(i));
|
||||
json_key_string(&w, "layout", m->ltsymbol[i]);
|
||||
json_key_string(&w, "layout", m->cold->ltsymbol[i]);
|
||||
|
||||
json_key(&w, "rect");
|
||||
json_object_start(&w);
|
||||
|
||||
+34
-14
@@ -40,6 +40,21 @@ const unsigned int layout_count = LENGTH(layouts);
|
||||
* helpers
|
||||
* ================================================================ */
|
||||
|
||||
/* Allocate and default-initialize monitor cold state on first use. */
|
||||
void
|
||||
ensure_cold(Monitor *m)
|
||||
{
|
||||
if (m->cold)
|
||||
return;
|
||||
m->cold = ecalloc(1, sizeof(MonitorCold));
|
||||
for (int i = 0; i < TAGCOUNT; i++) {
|
||||
m->cold->lt[i][0] = &layouts[0];
|
||||
m->cold->lt[i][1] = &layouts[0];
|
||||
}
|
||||
m->cold->mfact = 0.55f;
|
||||
m->cold->nmaster = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 0-based index of the lowest set tag bit for monitor m.
|
||||
* For single-tag views this is the exact tag. For multi-tag views it
|
||||
@@ -64,7 +79,8 @@ const Layout *
|
||||
curlayout(Monitor *m)
|
||||
{
|
||||
int ti = current_tag_idx(m);
|
||||
return m->lt[ti][m->sellt[ti]];
|
||||
ensure_cold(m);
|
||||
return m->cold->lt[ti][m->cold->sellt[ti]];
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
@@ -230,10 +246,10 @@ void
|
||||
dwindle_remove_client(Client *c)
|
||||
{
|
||||
Monitor *m = c->mon;
|
||||
if (!m)
|
||||
if (!m || !m->cold)
|
||||
return;
|
||||
for (int i = 0; i < TAGCOUNT; i++)
|
||||
dwindle_remove(&m->dwindle_root[i], c);
|
||||
dwindle_remove(&m->cold->dwindle_root[i], c);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
@@ -243,6 +259,7 @@ dwindle_remove_client(Client *c)
|
||||
void
|
||||
dwindle(Monitor *m)
|
||||
{
|
||||
ensure_cold(m);
|
||||
Client *c;
|
||||
int n = 0, e;
|
||||
|
||||
@@ -258,7 +275,7 @@ dwindle(Monitor *m)
|
||||
: 0;
|
||||
|
||||
int ti = current_tag_idx(m);
|
||||
DwindleNode **root = &m->dwindle_root[ti];
|
||||
DwindleNode **root = &m->cold->dwindle_root[ti];
|
||||
|
||||
/* prune leaves whose clients are no longer tiled here */
|
||||
{
|
||||
@@ -295,11 +312,11 @@ dwindle(Monitor *m)
|
||||
|
||||
/*
|
||||
* Insert any newly visible client, splitting the focused node.
|
||||
* Use m->dwindle_focus[ti] so it actually splits what the user
|
||||
* Use m->cold->dwindle_focus[ti] so it actually splits what the user
|
||||
* was looking at when they spawned the window, rather than whatever
|
||||
* focustop() happens to return.
|
||||
*/
|
||||
Client *focused = m->dwindle_focus[ti];
|
||||
Client *focused = m->cold->dwindle_focus[ti];
|
||||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
|
||||
@@ -329,13 +346,14 @@ dwindle(Monitor *m)
|
||||
static void
|
||||
master_arrange(Monitor *m, int ti)
|
||||
{
|
||||
ensure_cold(m);
|
||||
Client *c, *master_c = nullptr;
|
||||
Client *stack[256];
|
||||
int nstack = 0, n = 0;
|
||||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (VISIBLEON(c, m) && !c->isfloating) {
|
||||
if (c == m->master_master[ti])
|
||||
if (c == m->cold->master_master[ti])
|
||||
master_c = c;
|
||||
if (!c->isfullscreen)
|
||||
n++;
|
||||
@@ -348,7 +366,7 @@ master_arrange(Monitor *m, int ti)
|
||||
if (!master_c) {
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) {
|
||||
m->master_master[ti] = c;
|
||||
m->cold->master_master[ti] = c;
|
||||
master_c = c;
|
||||
break;
|
||||
}
|
||||
@@ -374,9 +392,9 @@ master_arrange(Monitor *m, int ti)
|
||||
return;
|
||||
}
|
||||
|
||||
int master_w = MAX(1, (int)(aw * m->mfact));
|
||||
int master_w = MAX(1, (int)(aw * m->cold->mfact));
|
||||
|
||||
if (m->master_side[ti] == 0) {
|
||||
if (m->cold->master_side[ti] == 0) {
|
||||
int stack_x = m->w.x + e + master_w + e;
|
||||
int stack_w = MAX(1, aw - master_w - e);
|
||||
|
||||
@@ -421,11 +439,11 @@ void
|
||||
master_remove_client(Client *c)
|
||||
{
|
||||
Monitor *m = c->mon;
|
||||
if (!m)
|
||||
if (!m || !m->cold)
|
||||
return;
|
||||
for (int i = 0; i < TAGCOUNT; i++) {
|
||||
if (m->master_master[i] == c)
|
||||
m->master_master[i] = nullptr;
|
||||
if (m->cold->master_master[i] == c)
|
||||
m->cold->master_master[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,8 +497,10 @@ swaptiled(Client *a, Client *b)
|
||||
return;
|
||||
|
||||
wl_list_for_each(m, &mons, link) {
|
||||
if (!m->cold)
|
||||
continue;
|
||||
for (int i = 0; i < TAGCOUNT; i++) {
|
||||
DwindleNode **root = &m->dwindle_root[i];
|
||||
DwindleNode **root = &m->cold->dwindle_root[i];
|
||||
if (!*root)
|
||||
continue;
|
||||
DwindleNode *la = dwindle_find_leaf(*root, a);
|
||||
|
||||
@@ -26,6 +26,7 @@ void monocle(Monitor *m);
|
||||
/* helpers used across modules */
|
||||
[[nodiscard]] int current_tag_idx(Monitor *m);
|
||||
[[nodiscard]] const Layout *curlayout(Monitor *m);
|
||||
void ensure_cold(Monitor *m);
|
||||
|
||||
/* dwindle tree helpers (exposed for swapdir) */
|
||||
[[nodiscard]] DwindleNode *dwindle_find_leaf(DwindleNode *n, Client *c);
|
||||
|
||||
+44
-38
@@ -401,21 +401,22 @@ static void apply_workspace_layout(Monitor *m, int tag_idx) {
|
||||
if (!layout_name[0])
|
||||
return;
|
||||
|
||||
/* ensure_cold will be called by whichever write path triggers first */
|
||||
for (int li = 0; li < (int)layout_count; li++) {
|
||||
if (layouts[li].symbol && !strcmp(layout_name, layouts[li].symbol)) {
|
||||
m->lt[tag_idx][m->sellt[tag_idx]] = &layouts[li];
|
||||
m->cold->lt[tag_idx][m->cold->sellt[tag_idx]] = &layouts[li];
|
||||
break;
|
||||
}
|
||||
if (!strcmp(layout_name, "dwindle") && layouts[li].arrange == dwindle) {
|
||||
m->lt[tag_idx][m->sellt[tag_idx]] = &layouts[li];
|
||||
m->cold->lt[tag_idx][m->cold->sellt[tag_idx]] = &layouts[li];
|
||||
break;
|
||||
}
|
||||
if (!strcmp(layout_name, "master") && layouts[li].arrange == master) {
|
||||
m->lt[tag_idx][m->sellt[tag_idx]] = &layouts[li];
|
||||
m->cold->lt[tag_idx][m->cold->sellt[tag_idx]] = &layouts[li];
|
||||
break;
|
||||
}
|
||||
if (!strcmp(layout_name, "monocle") && layouts[li].arrange == monocle) {
|
||||
m->lt[tag_idx][m->sellt[tag_idx]] = &layouts[li];
|
||||
m->cold->lt[tag_idx][m->cold->sellt[tag_idx]] = &layouts[li];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -481,8 +482,8 @@ void arrange(Monitor *m) {
|
||||
(c = focustop(m)) && c->isfullscreen);
|
||||
|
||||
int ti = current_tag_idx(m);
|
||||
strncpy(m->ltsymbol[ti], curlayout(m)->symbol, LENGTH(m->ltsymbol[ti]));
|
||||
m->ltsymbol[ti][LENGTH(m->ltsymbol[ti]) - 1] = '\0';
|
||||
strncpy(m->cold->ltsymbol[ti], curlayout(m)->symbol, LENGTH(m->cold->ltsymbol[ti]));
|
||||
m->cold->ltsymbol[ti][LENGTH(m->cold->ltsymbol[ti]) - 1] = '\0';
|
||||
|
||||
/* We move all clients (except fullscreen and unmanaged) to LyrTile while
|
||||
* in floating layout to avoid "real" floating clients be always on top */
|
||||
@@ -756,9 +757,10 @@ static void cleanupmon(struct wl_listener *listener, void *data) {
|
||||
closemon(m);
|
||||
ext_workspace_cleanupmon(m);
|
||||
ipc_socket_send_output_event();
|
||||
for (int i = 0; i < TAGCOUNT; i++) {
|
||||
dwindle_free_tree(m->dwindle_root[i]);
|
||||
m->dwindle_root[i] = nullptr;
|
||||
if (m->cold) {
|
||||
for (int i = 0; i < TAGCOUNT; i++)
|
||||
dwindle_free_tree(m->cold->dwindle_root[i]);
|
||||
free(m->cold);
|
||||
}
|
||||
wlr_scene_node_destroy(&m->fullscreen_bg->node);
|
||||
free(m);
|
||||
@@ -1077,8 +1079,10 @@ static void createmon(struct wl_listener *listener, void *data) {
|
||||
if (!r->name[0] || strstr(wlr_output->name, r->name)) {
|
||||
m->m.x = r->x;
|
||||
m->m.y = r->y;
|
||||
m->mfact = r->mfact;
|
||||
m->nmaster = r->nmaster;
|
||||
/* Allocate cold state lazily via the shared helper */
|
||||
ensure_cold(m);
|
||||
m->cold->mfact = r->mfact;
|
||||
m->cold->nmaster = r->nmaster;
|
||||
/* Determine monitor's default layout */
|
||||
const Layout *mon_layout = &layouts[0];
|
||||
for (int li = 0; li < (int)layout_count; li++) {
|
||||
@@ -1102,15 +1106,15 @@ static void createmon(struct wl_listener *listener, void *data) {
|
||||
const Layout *alt_layout = &layouts[layout_count > 1 && mon_layout != &layouts[1]];
|
||||
/* Propagate monitor layout to all tags */
|
||||
for (int ti = 0; ti < TAGCOUNT; ti++) {
|
||||
m->lt[ti][0] = mon_layout;
|
||||
m->lt[ti][1] = alt_layout;
|
||||
m->sellt[ti] = 0;
|
||||
m->cold->lt[ti][0] = mon_layout;
|
||||
m->cold->lt[ti][1] = alt_layout;
|
||||
m->cold->sellt[ti] = 0;
|
||||
}
|
||||
/* Apply per-workspace default layout for all tags */
|
||||
for (int ti = 0; ti < TAGCOUNT; ti++)
|
||||
apply_workspace_layout(m, ti);
|
||||
strncpy(m->ltsymbol[0], curlayout(m)->symbol, LENGTH(m->ltsymbol[0]));
|
||||
m->ltsymbol[0][LENGTH(m->ltsymbol[0]) - 1] = '\0';
|
||||
strncpy(m->cold->ltsymbol[0], curlayout(m)->symbol, LENGTH(m->cold->ltsymbol[0]));
|
||||
m->cold->ltsymbol[0][LENGTH(m->cold->ltsymbol[0]) - 1] = '\0';
|
||||
wlr_output_state_set_scale(&state, r->scale);
|
||||
wlr_output_state_set_transform(&state, r->transform);
|
||||
break;
|
||||
@@ -1474,8 +1478,8 @@ void focusclient(Client *c, int lift) {
|
||||
c->isurgent = 0;
|
||||
|
||||
/* Track focused client for dwindle insertion anchor */
|
||||
if (c->mon && !c->isfloating && !c->isfullscreen)
|
||||
c->mon->dwindle_focus[current_tag_idx(c->mon)] = c;
|
||||
if (c->mon && !c->isfloating && !c->isfullscreen && c->mon->cold)
|
||||
c->mon->cold->dwindle_focus[current_tag_idx(c->mon)] = c;
|
||||
|
||||
/* Don't change border color if there is an exclusive focus or we are
|
||||
* handling a drag operation */
|
||||
@@ -2507,7 +2511,7 @@ void printstatus(void) {
|
||||
printf("%s tags %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
|
||||
m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
|
||||
int _ti = current_tag_idx(m);
|
||||
printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol[_ti]);
|
||||
printf("%s layout %s\n", m->wlr_output->name, m->cold->ltsymbol[_ti]);
|
||||
ext_workspace_printstatus(m);
|
||||
}
|
||||
fflush(stdout);
|
||||
@@ -2853,26 +2857,27 @@ static void setfullscreen(Client *c, int fullscreen) {
|
||||
static void setlayout(const Arg *arg) {
|
||||
if (!selmon)
|
||||
return;
|
||||
ensure_cold(selmon);
|
||||
int ti = current_tag_idx(selmon);
|
||||
/* arg->ui is an index into layouts[]. If out of range, just toggle. */
|
||||
if (arg && arg->ui < layout_count) {
|
||||
if (selmon->lt[ti][selmon->sellt[ti]] != &layouts[arg->ui])
|
||||
selmon->sellt[ti] ^= 1;
|
||||
selmon->lt[ti][selmon->sellt[ti]] = &layouts[arg->ui];
|
||||
if (selmon->cold->lt[ti][selmon->cold->sellt[ti]] != &layouts[arg->ui])
|
||||
selmon->cold->sellt[ti] ^= 1;
|
||||
selmon->cold->lt[ti][selmon->cold->sellt[ti]] = &layouts[arg->ui];
|
||||
} else {
|
||||
selmon->sellt[ti] ^= 1;
|
||||
selmon->cold->sellt[ti] ^= 1;
|
||||
}
|
||||
strncpy(selmon->ltsymbol[ti], selmon->lt[ti][selmon->sellt[ti]]->symbol,
|
||||
LENGTH(selmon->ltsymbol[ti]));
|
||||
selmon->ltsymbol[ti][LENGTH(selmon->ltsymbol[ti]) - 1] = '\0';
|
||||
strncpy(selmon->cold->ltsymbol[ti], selmon->cold->lt[ti][selmon->cold->sellt[ti]]->symbol,
|
||||
LENGTH(selmon->cold->ltsymbol[ti]));
|
||||
selmon->cold->ltsymbol[ti][LENGTH(selmon->cold->ltsymbol[ti]) - 1] = '\0';
|
||||
|
||||
/* Convert windows when switching to master: set focused client as master */
|
||||
if (curlayout(selmon)->arrange == master) {
|
||||
Client *sel = focustop(selmon);
|
||||
if (sel && VISIBLEON(sel, selmon) && !sel->isfloating && !sel->isfullscreen)
|
||||
selmon->master_master[ti] = sel;
|
||||
selmon->cold->master_master[ti] = sel;
|
||||
else
|
||||
selmon->master_master[ti] = nullptr;
|
||||
selmon->cold->master_master[ti] = nullptr;
|
||||
/* Convert to floating: float all tiled clients */
|
||||
} else if (!curlayout(selmon)->arrange) {
|
||||
Client *c;
|
||||
@@ -3007,8 +3012,9 @@ reapply_monitor_config(void)
|
||||
for (int ri = 0; ri < cfg.nmonitors; ri++) {
|
||||
const CfgMonitorRule *r = &cfg.monitors[ri];
|
||||
if (!r->name[0] || strstr(m->wlr_output->name, r->name)) {
|
||||
m->mfact = r->mfact;
|
||||
m->nmaster = r->nmaster;
|
||||
ensure_cold(m);
|
||||
m->cold->mfact = r->mfact;
|
||||
m->cold->nmaster = r->nmaster;
|
||||
/* Map layout name */
|
||||
const Layout *mon_layout = &layouts[0];
|
||||
for (int li = 0; li < (int)layout_count; li++) {
|
||||
@@ -3031,9 +3037,9 @@ reapply_monitor_config(void)
|
||||
}
|
||||
const Layout *alt_layout = &layouts[layout_count > 1 && mon_layout != &layouts[1]];
|
||||
for (int ti = 0; ti < TAGCOUNT; ti++) {
|
||||
m->lt[ti][0] = mon_layout;
|
||||
m->lt[ti][1] = alt_layout;
|
||||
m->sellt[ti] = 0;
|
||||
m->cold->lt[ti][0] = mon_layout;
|
||||
m->cold->lt[ti][1] = alt_layout;
|
||||
m->cold->sellt[ti] = 0;
|
||||
}
|
||||
/* Apply scale and transform */
|
||||
struct wlr_output_state state;
|
||||
@@ -3568,7 +3574,7 @@ static void swapdir(const Arg *arg) {
|
||||
|
||||
if (curlayout(selmon)->arrange == dwindle) {
|
||||
int ti = current_tag_idx(selmon);
|
||||
DwindleNode **root = &selmon->dwindle_root[ti];
|
||||
DwindleNode **root = &selmon->cold->dwindle_root[ti];
|
||||
if (*root) {
|
||||
DwindleNode *leaf_sel = dwindle_find_leaf(*root, sel);
|
||||
DwindleNode *leaf_other = dwindle_find_leaf(*root, other);
|
||||
@@ -3588,13 +3594,13 @@ static void swapdir(const Arg *arg) {
|
||||
}
|
||||
} else if (curlayout(selmon)->arrange == master) {
|
||||
int ti = current_tag_idx(selmon);
|
||||
int side = selmon->master_side[ti];
|
||||
int side = selmon->cold->master_side[ti];
|
||||
|
||||
if (sel == selmon->master_master[ti]) {
|
||||
if (sel == selmon->cold->master_master[ti]) {
|
||||
/* master moving away from master side → flip */
|
||||
if ((side == 0 && arg->i == WLR_DIRECTION_RIGHT) ||
|
||||
(side == 1 && arg->i == WLR_DIRECTION_LEFT)) {
|
||||
selmon->master_side[ti] = !side;
|
||||
selmon->cold->master_side[ti] = !side;
|
||||
arrange(selmon);
|
||||
printstatus();
|
||||
focusclient(sel, 1);
|
||||
@@ -3604,7 +3610,7 @@ static void swapdir(const Arg *arg) {
|
||||
/* stack moving toward master side → promote */
|
||||
if ((side == 0 && arg->i == WLR_DIRECTION_LEFT) ||
|
||||
(side == 1 && arg->i == WLR_DIRECTION_RIGHT)) {
|
||||
selmon->master_master[ti] = sel;
|
||||
selmon->cold->master_master[ti] = sel;
|
||||
arrange(selmon);
|
||||
printstatus();
|
||||
focusclient(sel, 1);
|
||||
|
||||
Reference in New Issue
Block a user