Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1viypR-002xBy-2A for pgsql-hackers@arkaria.postgresql.org; Thu, 22 Jan 2026 17:50:46 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1viypQ-00E4Ye-2O for pgsql-hackers@arkaria.postgresql.org; Thu, 22 Jan 2026 17:50:45 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1viypQ-00E4YW-0p for pgsql-hackers@lists.postgresql.org; Thu, 22 Jan 2026 17:50:44 +0000 Received: from mail-oi1-x229.google.com ([2607:f8b0:4864:20::229]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1viypN-001oA1-1K for pgsql-hackers@postgresql.org; Thu, 22 Jan 2026 17:50:43 +0000 Received: by mail-oi1-x229.google.com with SMTP id 5614622812f47-459fa8b6044so757103b6e.2 for ; Thu, 22 Jan 2026 09:50:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769104241; x=1769709041; darn=postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=aH6ZFsF5409lr/AL/gmM9iYGhKHjXuhsyrw18Rh8VCA=; b=D+/dox425MjRoWFTkcnD9kGnYVqfbYhQ2C4eAfMOpdZoe+tPjXs7ETVyIOQJuyjpuF kepyBSXlDC9E4dMRVsHf6ckI6Sk7eOVPE/+vH3rAysGkEgKHERfCEypcv7EsskNgqsSR /tB/b/g1/1Xz+bIy+06dE6DPPs3WCTSDmLbnBWU+JrSXHtovO6pjoYPdZZSZIorcq0iG yghm7/EO9VxGXT0WloTxmQ8jaZTqOb23iG6uTVN2KeYDGWfsvFlsrL75smar66hDgwaf WWHRJ42gR/kokiZenJzhEoGtfvMOrQ6RtzRKQEpRiflRIj4AbM7QDX6P7VsGJhQYlqKX /2Vg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769104241; x=1769709041; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=aH6ZFsF5409lr/AL/gmM9iYGhKHjXuhsyrw18Rh8VCA=; b=X4baLxetxPQknDFClF5tN+oMf5nZWbgnZibI3RBFT5hLKW/YGs/0wuPvl98Ay/Ptu+ HmkkUTbfE+tvMw2IYStwXvGNCxBJbR6HVF7Z9rvIUKuEnraZiMRRv0MbBp3E8hnG3IO7 WECKv/BfODeQvpJLgU5H3YZ3OtbVuq80fwyGIBGHT0xvlBP2alkafy9/w5B8JHx9NqNA aNfORav2uHqzzx1vQyH9oQ14boKwrpaD09jAIsUuIF0ZMTnC5VfhoEYAIktyPCHbxDur eFJYNTYzJ90XHyeLF8FRHCj4Yu3uuKn8eCm3DJAhzTEAvmEL1G342Eu589xNVcx0vMT3 itIA== X-Forwarded-Encrypted: i=1; AJvYcCX9oNP8xjdIqV3gTI1w4LN0MgnfL0uvW+5IMY7wresdfJp5gegomsRFDpNdFGEZLaVuIgp4Wp5M4lUD8s/i@postgresql.org X-Gm-Message-State: AOJu0YyduhTT8edF3YEmH0LZqdfuGVPSHIH/Cuk57afDYpQCwOHv07+7 hbYmF14/e8Luvlf45Yjl6beHViL0ojPfrY1+58JSkndte17sbg1C9QnO X-Gm-Gg: AZuq6aJComT3sFsK50H5sKF1D6aGFEETVAT/AHsaA4fDwT5A/GoOBS1nj6nWvYuPJd8 6Lf4YjI3EVYwgi+ZeyM1wZVERqaP9+XnN3hb8i3yuBx32+iE3bALdxejdTquZdN1YWrnptuVqH/ 1xjLvSRMGO8hTugzIzpDYvoib2qP3QNmhNMIz5vfu3RkiqTOLdCiZu0tMMxrr6KnySoMr30cUx3 6n1KrE9F4wqJ5/h0Wy3f1rYtssTzQISYJoHWzn7OsPCjQPTxUtb7S6r1f6EhJu20pU4Pm9J4DOn uyk+aqchpP372xlEX9nYE5+gP1CS9WpEieOhNqq60dzubooJIBjWFJJQ+0wc6T5PdvhMCkILMgL 739rjJsWnhw9Gpr/nwzRA1aU6A4VTnLvFK3uFT0JUkbSoKtbYS1Pz0onBoC5wZ2dzakeQ7ftjLp RIZpheD4p5NNTiUv8bAb63XMVPoNCQiu3BCocdFke+13oJYOqSzHB4uMwrio6Kofggzdx2J1f5I 65A X-Received: by 2002:a05:6808:ec4:b0:45c:9486:5d70 with SMTP id 5614622812f47-45eb1db8ceemr170253b6e.63.1769104241204; Thu, 22 Jan 2026 09:50:41 -0800 (PST) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 5614622812f47-45c9dff95a2sm10522097b6e.11.2026.01.22.09.50.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 22 Jan 2026 09:50:40 -0800 (PST) Date: Thu, 22 Jan 2026 11:50:38 -0600 From: Nathan Bossart To: John Naylor Cc: Heikki Linnakangas , pgsql-hackers@postgresql.org Subject: Re: refactor architecture-specific popcount code Message-ID: References: <652cab58-8dfb-4514-a6b0-218a3edb0699@iki.fi> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="1Cwfhs/B30EIRjWN" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --1Cwfhs/B30EIRjWN Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Thu, Jan 22, 2026 at 04:50:26PM +0700, John Naylor wrote: > 1) Nowadays, the only global call sites of the word-sized functions > are select_best_grantor() and in bitmapsets. The latter calls the > word-sized functions in a loop (could be just one word). It may be > more efficient to calculate the size in bytes and call pg_popcount(). Yeah, these seem like obvious places to use pg_popcount(). Note that bms_member_index() does a final popcount on a masked version of the last word. We could swap that with pg_popcount(), too, but it might be slower than just calling the word-sized function. However, it could be hard to tell the difference, as we'd be trading a function or function pointer call with an inlined loop over pg_number_of_ones. And even if it is slower, I'm not sure it matters all that much in the grand scheme of things. In any case, 0001 gets the easy ones out of the way. > Then we could get rid of all the pointer indirection for the > word-sized functions. Do you mean that we'd just keep the portable ones around? I see some code in pgvector that might be negatively impacted by that, but if I understand correctly it would require an unusual setup. > 2) The x86 byte buffer variants expend a lot of effort to detect > whether the buffer is aligned on both 64- and 32-bit platforms, with > an optimized path for each. At least 64-bit doesn't care about > alignment, and 32-bit doesn't warrant anything fancier than pure C. > Simultaneously, the aarch64 equivalent doesn't seem to take care about > alignment. (I think Nathan mentioned he didn't see a difference during > testing, but I wonder how universal that is). 0002 makes these changes for pg_popcount_sse42() and pg_popcount_masked_sse42(). It does seem strange to prefer a loop over pg_number_of_ones instead of using POPCNTQ when unaligned, but perhaps it's worth testing. I do recall the alignment stuff in the AVX-512 code showing benefits in tests because it avoids double-load overhead, so I think we should keep that for now. > 3) There is repeated code for the <8 bytes case, and the tail of the > "optimized" functions. I'm also not sure why the small case is inlined > everywhere. This is intended to help avoid function call and SIMD instruction overhead when it doesn't make sense to take it. I recall this showing a rather big difference in benchmarks when we were working on the AVX-512 versions. Regarding the duplicated code, sure, we could add some static inline functions or something. I think the only reason I haven't done so is because it's ~2 lines of code. -- nathan --1Cwfhs/B30EIRjWN Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v3-0001-Make-use-of-pg_popcount-in-more-places.patch From f0cfa4c535b6658bc6e5f63c068e606f28ef9acd Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Thu, 22 Jan 2026 11:16:09 -0600 Subject: [PATCH v3 1/2] Make use of pg_popcount() in more places. --- src/backend/nodes/bitmapset.c | 27 ++++----------------------- src/include/lib/radixtree.h | 4 ++-- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c index a4765876c31..23c91fdb6c9 100644 --- a/src/backend/nodes/bitmapset.c +++ b/src/backend/nodes/bitmapset.c @@ -553,14 +553,8 @@ bms_member_index(Bitmapset *a, int x) bitnum = BITNUM(x); /* count bits in preceding words */ - for (int i = 0; i < wordnum; i++) - { - bitmapword w = a->words[i]; - - /* No need to count the bits in a zero word */ - if (w != 0) - result += bmw_popcount(w); - } + result += pg_popcount((const char *) a->words, + wordnum * sizeof(bitmapword)); /* * Now add bits of the last word, but only those before the item. We can @@ -749,26 +743,13 @@ bms_get_singleton_member(const Bitmapset *a, int *member) int bms_num_members(const Bitmapset *a) { - int result = 0; - int nwords; - int wordnum; - Assert(bms_is_valid_set(a)); if (a == NULL) return 0; - nwords = a->nwords; - wordnum = 0; - do - { - bitmapword w = a->words[wordnum]; - - /* No need to count the bits in a zero word */ - if (w != 0) - result += bmw_popcount(w); - } while (++wordnum < nwords); - return result; + return pg_popcount((const char *) a->words, + a->nwords * sizeof(bitmapword)); } /* diff --git a/src/include/lib/radixtree.h b/src/include/lib/radixtree.h index b223ce10a2d..1425654a67c 100644 --- a/src/include/lib/radixtree.h +++ b/src/include/lib/radixtree.h @@ -2725,8 +2725,8 @@ RT_VERIFY_NODE(RT_NODE * node) /* RT_DUMP_NODE(node); */ - for (int i = 0; i < RT_BM_IDX(RT_NODE_MAX_SLOTS); i++) - cnt += bmw_popcount(n256->isset[i]); + cnt += pg_popcount((const char *) n256->isset, + RT_NODE_MAX_SLOTS / BITS_PER_BYTE); /* * Check if the number of used chunk matches, accounting for -- 2.50.1 (Apple Git-155) --1Cwfhs/B30EIRjWN Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v3-0002-Remove-unnecessary-32-bit-optimizations-and-align.patch From aa70ca356ce21cb1a3861a9f1c42ca9485b2dbfd Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Thu, 22 Jan 2026 11:33:56 -0600 Subject: [PATCH v3 2/2] Remove unnecessary 32-bit optimizations and alignment checks. --- src/port/pg_popcount_x86.c | 63 ++++++++------------------------------ 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/port/pg_popcount_x86.c b/src/port/pg_popcount_x86.c index 245f0167d00..0e98f532552 100644 --- a/src/port/pg_popcount_x86.c +++ b/src/port/pg_popcount_x86.c @@ -382,33 +382,16 @@ pg_popcount_sse42(const char *buf, int bytes) uint64 popcnt = 0; #if SIZEOF_VOID_P >= 8 - /* Process in 64-bit chunks if the buffer is aligned. */ - if (buf == (const char *) TYPEALIGN(8, buf)) - { - const uint64 *words = (const uint64 *) buf; - - while (bytes >= 8) - { - popcnt += pg_popcount64_sse42(*words++); - bytes -= 8; - } + /* Process in 64-bit chunks. */ + const uint64 *words = (const uint64 *) buf; - buf = (const char *) words; - } -#else - /* Process in 32-bit chunks if the buffer is aligned. */ - if (buf == (const char *) TYPEALIGN(4, buf)) + while (bytes >= 8) { - const uint32 *words = (const uint32 *) buf; - - while (bytes >= 4) - { - popcnt += pg_popcount32_sse42(*words++); - bytes -= 4; - } - - buf = (const char *) words; + popcnt += pg_popcount64_sse42(*words++); + bytes -= 8; } + + buf = (const char *) words; #endif /* Process any remaining bytes */ @@ -428,37 +411,17 @@ pg_popcount_masked_sse42(const char *buf, int bytes, bits8 mask) uint64 popcnt = 0; #if SIZEOF_VOID_P >= 8 - /* Process in 64-bit chunks if the buffer is aligned */ + /* Process in 64-bit chunks. */ uint64 maskv = ~UINT64CONST(0) / 0xFF * mask; + const uint64 *words = (const uint64 *) buf; - if (buf == (const char *) TYPEALIGN(8, buf)) + while (bytes >= 8) { - const uint64 *words = (const uint64 *) buf; - - while (bytes >= 8) - { - popcnt += pg_popcount64_sse42(*words++ & maskv); - bytes -= 8; - } - - buf = (const char *) words; + popcnt += pg_popcount64_sse42(*words++ & maskv); + bytes -= 8; } -#else - /* Process in 32-bit chunks if the buffer is aligned. */ - uint32 maskv = ~((uint32) 0) / 0xFF * mask; - if (buf == (const char *) TYPEALIGN(4, buf)) - { - const uint32 *words = (const uint32 *) buf; - - while (bytes >= 4) - { - popcnt += pg_popcount32_sse42(*words++ & maskv); - bytes -= 4; - } - - buf = (const char *) words; - } + buf = (const char *) words; #endif /* Process any remaining bytes */ -- 2.50.1 (Apple Git-155) --1Cwfhs/B30EIRjWN--