From 4017a40b1de475ca52fe0f5488d0045367cf31a5 Mon Sep 17 00:00:00 2001 From: alessio Date: Thu, 30 Jan 2025 12:33:48 +0100 Subject: [PATCH] Remove zero initialization of large buffers Profiles show that an high amount of CPU time spent in memset. By removing zero initalization of certain large buffers we improve performance in certain authoritative workloads. --- lib/dns/qp.c | 29 +++++++++++++++++++++-------- lib/dns/qpcache.c | 31 ++++++++++++++++++++++++++----- lib/dns/qpzone.c | 39 +++++++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/lib/dns/qp.c b/lib/dns/qp.c index 3805845b91..79a0b8e6ee 100644 --- a/lib/dns/qp.c +++ b/lib/dns/qp.c @@ -1785,10 +1785,14 @@ dns_qpchain_init(dns_qpreadable_t qpr, dns_qpchain_t *chain) { REQUIRE(QP_VALID(qp)); REQUIRE(chain != NULL); - *chain = (dns_qpchain_t){ - .magic = QPCHAIN_MAGIC, - .qp = qp, - }; + /* + * dns_qpchain_t contains a 2kb buffer, which is slow to + * zero-initialize. Therefore we avoid designated initializers, and + * initialize each field manually. + */ + chain->magic = QPCHAIN_MAGIC; + chain->qp = qp; + chain->len = 0; } unsigned int @@ -1821,10 +1825,19 @@ dns_qpiter_init(dns_qpreadable_t qpr, dns_qpiter_t *qpi) { dns_qpreader_t *qp = dns_qpreader(qpr); REQUIRE(QP_VALID(qp)); REQUIRE(qpi != NULL); - *qpi = (dns_qpiter_t){ - .qp = qp, - .magic = QPITER_MAGIC, - }; + + /* + * dns_qpiter_t contains a 4kb buffer, which is slow to zero-initialize. + * Therefore we avoid designated initializers, and initialize each + * field manually. + */ + qpi->qp = qp; + qpi->sp = 0; + qpi->magic = QPITER_MAGIC; + /* + * The top of the stack must be initialized. + */ + qpi->stack[qpi->sp] = NULL; } /* diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index 1b50608203..3b7f5680b1 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -1458,6 +1458,29 @@ find_coveringnsec(qpc_search_t *search, const dns_name_t *name, (DNS_TRUST_PENDING((found)->trust) && \ (((options) & DNS_DBFIND_PENDINGOK) == 0))) +static void +qpc_search_init(qpc_search_t *search, qpcache_t *db, unsigned int options, + isc_stdtime_t now) { + /* + * qpc_search_t contains two structures with large buffers (dns_qpiter_t + * and dns_qpchain_t). Those two structures will be initialized later by + * dns_qp_lookup anyway. + * To avoid the overhead of zero initialization, we avoid designated + * initializers and initialize all "small" fields manually. + */ + search->qpdb = (qpcache_t *)db; + search->options = options; + /* + * qpch->in - Init by dns_qp_lookup + * qpiter - Init by dns_qp_lookup + */ + search->need_cleanup = false; + search->now = now ? now : isc_stdtime_now(); + search->zonecut = NULL; + search->zonecut_header = NULL; + search->zonecut_sigheader = NULL; +} + static isc_result_t qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, dns_rdatatype_t type, unsigned int options, isc_stdtime_t __now, @@ -1479,11 +1502,9 @@ qpcache_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, dns_slabheader_t *foundsig = NULL, *nssig = NULL, *cnamesig = NULL; dns_slabheader_t *nsecheader = NULL, *nsecsig = NULL; dns_typepair_t sigtype, negtype; - qpc_search_t search = (qpc_search_t){ - .qpdb = (qpcache_t *)db, - .options = options, - .now = __now ? __now : isc_stdtime_now(), - }; + + qpc_search_t search; + qpc_search_init(&search, (qpcache_t *)db, options, __now); REQUIRE(VALID_QPDB((qpcache_t *)db)); REQUIRE(version == NULL); diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c index 9ede2dfa4c..4c60d4c17d 100644 --- a/lib/dns/qpzone.c +++ b/lib/dns/qpzone.c @@ -3326,6 +3326,34 @@ qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) { return result; } +static void +qpz_search_init(qpz_search_t *search, qpzonedb_t *db, qpz_version_t *version, + unsigned int options) { + /* + * qpz_search_t contains two structures with large buffers (dns_qpiter_t + * and dns_qpchain_t). Those two structures will be initialized later by + * dns_qp_lookup anyway. + * To avoid the overhead of zero initialization, we avoid designated + * initializers and initialize all "small" fields manually. + */ + search->qpdb = db; + search->version = version; + search->qpr = (dns_qpread_t){}; + search->serial = version->serial; + search->options = options; + /* + * qpch->in -- init in dns_qp_lookup + * qpiter -- init in dns_qp_lookup + */ + search->copy_name = false; + search->need_cleanup = false; + search->wild = false; + search->zonecut = NULL; + search->zonecut_header = NULL; + search->zonecut_sigheader = NULL; + dns_fixedname_init(&search->zonecut_name); +} + static isc_result_t qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, dns_rdatatype_t type, unsigned int options, @@ -3335,7 +3363,6 @@ qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, isc_result_t result; qpzonedb_t *qpdb = (qpzonedb_t *)db; qpznode_t *node = NULL; - qpz_search_t search; bool cname_ok = true, close_version = false; bool maybe_zonecut = false, at_zonecut = false; bool wild = false, empty_node = false; @@ -3361,13 +3388,9 @@ qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, close_version = true; } - search = (qpz_search_t){ - .qpdb = (qpzonedb_t *)db, - .version = (qpz_version_t *)version, - .serial = ((qpz_version_t *)version)->serial, - .options = options, - }; - dns_fixedname_init(&search.zonecut_name); + qpz_search_t search; + qpz_search_init(&search, (qpzonedb_t *)db, (qpz_version_t *)version, + options); if ((options & DNS_DBFIND_FORCENSEC3) != 0) { dns_qpmulti_query(qpdb->nsec3, &search.qpr);