From d0d60a007db9623e248f006e7ca8371dab657c9b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 15 Jul 2025 11:47:54 +0200 Subject: [PATCH] MEDIUM: proxy: switch conf.name to cebis_tree This is used to index the proxy's name and it contains a copy of the pointer to the proxy's name in . Changing that for a ceb_node placed just before saves 32 bytes to the struct proxy, which is now 3112 bytes large. Here we need to continue to support duplicates since they're still allowed between type-incompatible proxies. Interestingly, the use of cebis_next_dup() instead of cebis_next() in proxy_find_by_name() allows us to get rid of an strcmp() that was performed for each use_backend rule. A test with a large config (100k backends) shows that we can get 3% extra performance on a config involving a static use_backend rule (3.09M to 3.18M rps), and even 4.5% on a dynamic rule selecting a random backend (2.47M to 2.59M). --- include/haproxy/proxy-t.h | 5 +++-- include/haproxy/proxy.h | 2 +- src/proxy.c | 36 ++++++++++++++---------------------- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index 1bf0008cd..6d93eeb54 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -396,7 +396,8 @@ struct proxy { } timeout; __decl_thread(HA_RWLOCK_T lock); /* may be taken under the server's lock */ - char *id, *desc; /* proxy id (name) and description */ + char *id; /* proxy id (name), indexed by below */ + char *desc; /* proxy description */ struct proxy_per_tgroup *per_tgrp; /* array of per-tgroup stuff such as queues */ unsigned int queueslength; /* Sum of the length of each queue */ int totpend; /* total number of pending connections on this instance (for stats) */ @@ -468,7 +469,7 @@ struct proxy { struct list listeners; /* list of listeners belonging to this frontend */ struct list errors; /* list of all custom error files */ struct arg_list args; /* sample arg list that need to be resolved */ - struct ebpt_node by_name; /* proxies are stored sorted by name here */ + struct ceb_node name_node; /* proxies are stored sorted by name here; indexes below */ struct list lf_checks; /* list of logformats found in the proxy section that needs to be checked during postparse */ struct log_steps log_steps; /* bitfield of log origins where log should be generated during request handling */ const char *file_prev; /* file of the previous instance found with the same name, or NULL */ diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h index 502d7ba6a..2329db8b7 100644 --- a/include/haproxy/proxy.h +++ b/include/haproxy/proxy.h @@ -36,7 +36,7 @@ extern struct proxy *proxies_list; extern struct list proxies; extern struct eb_root used_proxy_id; /* list of proxy IDs in use */ extern unsigned int error_snapshot_id; /* global ID assigned to each error then incremented */ -extern struct eb_root proxy_by_name; /* tree of proxies sorted by name */ +extern struct ceb_root *proxy_by_name; /* tree of proxies sorted by name */ extern const struct cfg_opt cfg_opts[]; extern const struct cfg_opt cfg_opts2[]; diff --git a/src/proxy.c b/src/proxy.c index 038870e0e..c886e7473 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -16,8 +16,8 @@ #include #include +#include #include -#include #include #include @@ -62,8 +62,8 @@ int listeners; /* # of proxy listeners, set by cfgparse */ struct proxy *proxies_list = NULL; /* list of main proxies */ struct list proxies = LIST_HEAD_INIT(proxies); /* list of all proxies */ struct eb_root used_proxy_id = EB_ROOT; /* list of proxy IDs in use */ -struct eb_root proxy_by_name = EB_ROOT; /* tree of proxies sorted by name */ -struct eb_root defproxy_by_name = EB_ROOT; /* tree of default proxies sorted by name (dups possible) */ +struct ceb_root *proxy_by_name = NULL; /* tree of proxies sorted by name */ +struct ceb_root *defproxy_by_name = NULL; /* tree of default proxies sorted by name (dups possible) */ struct proxy *orphaned_default_proxies = NULL; /* deleted ones with refcount != 0 */ unsigned int error_snapshot_id = 0; /* global ID assigned to each error then incremented */ @@ -217,7 +217,7 @@ static inline void proxy_free_common(struct proxy *px) struct lf_expr *lf, *lfb; /* note that the node's key points to p->id */ - ebpt_delete(&px->conf.by_name); + cebis_item_delete((px->cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name, conf.name_node, id, px); ha_free(&px->id); LIST_DEL_INIT(&px->global_list); drop_file_name(&px->conf.file); @@ -1172,10 +1172,9 @@ static int proxy_parse_guid(char **args, int section_type, struct proxy *curpx, */ void proxy_store_name(struct proxy *px) { - struct eb_root *root = (px->cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name; + struct ceb_root **root = (px->cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name; - px->conf.by_name.key = px->id; - ebis_insert(root, &px->conf.by_name); + cebis_item_insert(root, conf.name_node, id, px); } /* Returns a pointer to the first proxy matching capabilities and id @@ -1219,16 +1218,11 @@ struct proxy *proxy_find_by_name(const char *name, int cap, int table) return curproxy; } else { - struct eb_root *root; - struct ebpt_node *node; + struct ceb_root **root; root = (cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name; - for (node = ebis_lookup(root, name); node; node = ebpt_next(node)) { - curproxy = container_of(node, struct proxy, conf.by_name); - - if (strcmp(curproxy->id, name) != 0) - break; - + for (curproxy = cebis_item_lookup(root, conf.name_node, id, name, struct proxy); + curproxy; curproxy = cebis_item_next_dup(root, conf.name_node, id, curproxy)) { if ((curproxy->cap & cap) != cap) continue; @@ -1583,7 +1577,8 @@ void proxy_destroy_defaults(struct proxy *px) if (!(px->cap & PR_CAP_DEF)) return; BUG_ON(px->conf.refcount != 0); - ebpt_delete(&px->conf.by_name); + cebis_item_delete((px->cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name, + conf.name_node, id, px); proxy_free_defaults(px); free(px); } @@ -1593,14 +1588,11 @@ void proxy_destroy_defaults(struct proxy *px) */ void proxy_destroy_all_unref_defaults() { - struct ebpt_node *n; struct proxy *px, *nx; - n = ebpt_first(&defproxy_by_name); - while (n) { - px = container_of(n, struct proxy, conf.by_name); + for (px = cebis_item_first(&defproxy_by_name, conf.name_node, id, struct proxy); px; px = nx) { BUG_ON(!(px->cap & PR_CAP_DEF)); - n = ebpt_next(n); + nx = cebis_item_next(&defproxy_by_name, conf.name_node, id, px); if (!px->conf.refcount) proxy_destroy_defaults(px); } @@ -1624,7 +1616,7 @@ void proxy_unref_or_destroy_defaults(struct proxy *px) if (!px || !(px->cap & PR_CAP_DEF)) return; - ebpt_delete(&px->conf.by_name); + cebis_item_delete((px->cap & PR_CAP_DEF) ? &defproxy_by_name : &proxy_by_name, conf.name_node, id, px); if (px->conf.refcount) { /* still referenced just append it to the orphaned list */ px->next = orphaned_default_proxies;