Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1guM2d-0002wu-Fe for pgsql-hackers@arkaria.postgresql.org; Thu, 14 Feb 2019 18:47:23 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1guM2c-00038k-2t for pgsql-hackers@arkaria.postgresql.org; Thu, 14 Feb 2019 18:47:22 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1guM2b-00031T-H7 for pgsql-hackers@lists.postgresql.org; Thu, 14 Feb 2019 18:47:21 +0000 Received: from mail-qt1-x841.google.com ([2607:f8b0:4864:20::841]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1guM2Y-00054h-GK for pgsql-hackers@lists.postgresql.org; Thu, 14 Feb 2019 18:47:21 +0000 Received: by mail-qt1-x841.google.com with SMTP id p25so7781379qtb.3 for ; Thu, 14 Feb 2019 10:47:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :content-transfer-encoding:in-reply-to:user-agent; bh=jXnCp0PlIJAL7OuuIQeYSYTTs/qerxTsN/KcvLUBGhw=; b=k8mF9comsAWXBPjRu7rZ1HxE/SjYgzNGEXv/YQwDcIEJA9rNVDm/+TqWMzFHKtetOs ujtl1veW4zbAEYcr18PS/sQhIFT1RrRdfTp/2BnMRtoJL8F2mjyQZSTWH19m8mUDuhJN 0eE1B8AMW/Ok7NoJRJt+MZf5VQgeQQxlX9qHrTB0B9CPjLKayomNr9qlEvHPcO6ymLsE SpQbiaBYdO006/aSP3fADWa125y7cdBkFcW+Q+3AH9DnRqDDXY0daSYcVeDI1ByoqHuu jv+WVMNtYk3/FNt3kRPKS5hu0HupG8aUmsYqhSX/aNV8rrEKRD6NopjW1jdZAhjSnPXT Up1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:content-transfer-encoding:in-reply-to :user-agent; bh=jXnCp0PlIJAL7OuuIQeYSYTTs/qerxTsN/KcvLUBGhw=; b=d0Ev6IDumRxQ2t830a/vcFn8pMrdRfukoxl/M0y6784UzWubLyDId8Io5k7QXDCf4X 3SbY3ZlQ9mE9QpR07JsG6zhS9gfVhSySvXY7cfC+HW92EcpkyLNkvqnHxJVGjr9yBoJG +1aBaGk7Z1SIk1VWGaFEB9eSr7v1nxMVYUpoEclxPe+NciczdBPxFKTtPWvz5Zpih5j4 EziVHw54Bgr10dAZyl0GC0oFgYUBXGkbCK6KNjyEqolR8zIDK9ErsAGBbQEvIZxNZfvP fjeGyZPR0BkfmlwdDLs1qxk6HSWnqI4Kcy8Z+rn6/mBBp9YnuC2PPywxtu8OPsrSIqzJ LzIg== X-Gm-Message-State: AHQUAubIbGM8Yt04LpVXNQsIRjVrzpPLgWDgLfSidOsLwKokW4PJF0fI /F9mTW27X3y2BEPVJn9beSpsMw== X-Google-Smtp-Source: AHgI3Ib/OhtiquT0jUNUSE8LCV1j+NR5NJEajUTeCQtWRtyoc7b/3uArGF72fuMTxoX2V0NuIm4/fw== X-Received: by 2002:a0c:985d:: with SMTP id e29mr4224450qvd.16.1550170036648; Thu, 14 Feb 2019 10:47:16 -0800 (PST) Received: from nimloth.alvh.no-ip.org ([190.121.29.3]) by smtp.gmail.com with ESMTPSA id c9sm2303632qkj.61.2019.02.14.10.47.15 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 14 Feb 2019 10:47:15 -0800 (PST) Received: by nimloth.alvh.no-ip.org (Postfix, from userid 1000) id 022F0124627; Thu, 14 Feb 2019 15:47:13 -0300 (-03) Date: Thu, 14 Feb 2019 15:47:13 -0300 From: Alvaro Herrera To: Tom Lane Cc: Thomas Munro , Andrew Gierth , David Rowley , PostgreSQL Hackers Subject: Re: Using POPCNT and other advanced bit manipulation instructions Message-ID: <20190214184713.GA6080@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="Q68bSM7Ycu6FN28Q" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <16168.1550121890@sss.pgh.pa.us> User-Agent: Mutt/1.9.4 (2018-02-28) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2019-Feb-14, Tom Lane wrote: > Some further thoughts here ... > > Does the "lzcnt" runtime probe actually do anything useful? > On the x86_64 compilers I tried (gcc 8.2.1 and 4.4.7), __builtin_clz > and __builtin_ctz compile to sequences involving bsrq and bsfq > regardless of -mpopcnt. It's fairly hard to see how lzcnt would > buy anything over those sequences even if there were zero overhead > involved in using it. Hah, I just realized you have to add -mlzcnt in order for these builtins to use the lzcnt instructions. It goes from something like bsrq %rax, %rax xorq $63, %rax to lzcntq %rax, %rax Significant? I have this patch now, written before I realized the above; I'll augment it to cater to this (adding -mlzcnt and a new set of functions -- perhaps a new file "lzcnt.c" or maybe put the lot in pg_popcount.c and rename it?) and resubmit after an errand I have to run now. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --Q68bSM7Ycu6FN28Q Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="popcount.patch" commit 477cc89802effed5d80d4a82891c7ef3b5e61e63 Author: Alvaro Herrera AuthorDate: Thu Feb 14 15:18:03 2019 -0300 CommitDate: Thu Feb 14 15:31:32 2019 -0300 fix popcount etc diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h index 148c5550573..53787a7ef32 100644 --- a/src/include/port/pg_bitutils.h +++ b/src/include/port/pg_bitutils.h @@ -23,4 +23,8 @@ extern int (*pg_leftmost_one64) (uint64 word); extern uint64 pg_popcount(const char *buf, int bytes); +/* in pg_popcount.c */ +extern int pg_popcount32_sse42(uint32 word); +extern int pg_popcount64_sse42(uint64 word); + #endif /* PG_BITUTILS_H */ diff --git a/src/port/Makefile b/src/port/Makefile index 2da73260a13..d7290573c65 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -41,6 +41,13 @@ OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \ qsort.o qsort_arg.o quotes.o snprintf.o sprompt.o strerror.o \ tar.o thread.o +# If the compiler supports a flag for the POPCOUNT instruction, we compile +# pg_popcount.o with it. (Whether to actually use the functions therein is +# determined at runtime by testing CPUID flags.) +ifneq ($(CFLAGS_POPCNT),) +OBJS += pg_popcount.o +endif + # libpgport.a, libpgport_shlib.a, and libpgport_srv.a contain the same files # foo.o, foo_shlib.o, and foo_srv.o are all built from foo.c OBJS_SHLIB = $(OBJS:%.o=%_shlib.o) @@ -78,10 +85,10 @@ pg_crc32c_armv8.o: CFLAGS+=$(CFLAGS_ARMV8_CRC32C) pg_crc32c_armv8_shlib.o: CFLAGS+=$(CFLAGS_ARMV8_CRC32C) pg_crc32c_armv8_srv.o: CFLAGS+=$(CFLAGS_ARMV8_CRC32C) -# pg_bitutils.c needs CFLAGS_POPCNT -pg_bitutils.o: CFLAGS+=$(CFLAGS_POPCNT) -pg_bitutils_shlib.o: CFLAGS+=$(CFLAGS_POPCNT) -pg_bitutils_srv.o: CFLAGS+=$(CFLAGS_POPCNT) +# pg_popcount.c needs CFLAGS_POPCNT +pg_popcount.o: CFLAGS+=$(CFLAGS_POPCNT) +pg_popcount_shlib.o: CFLAGS+=$(CFLAGS_POPCNT) +pg_popcount_srv.o: CFLAGS+=$(CFLAGS_POPCNT) # # Shared library versions of object files diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c index aac394fe927..fc8de518791 100644 --- a/src/port/pg_bitutils.c +++ b/src/port/pg_bitutils.c @@ -10,7 +10,6 @@ * *------------------------------------------------------------------------- */ - #include "postgres.h" #ifdef HAVE__GET_CPUID @@ -23,61 +22,52 @@ #include "port/pg_bitutils.h" -#if defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE__GET_CPUID) +#ifdef HAVE__BUILTIN_POPCOUNT static bool pg_popcount_available(void); static int pg_popcount32_choose(uint32 word); -static int pg_popcount32_sse42(uint32 word); +static int pg_popcount32_builtin(uint32 word); static int pg_popcount64_choose(uint64 word); -static int pg_popcount64_sse42(uint64 word); -#endif -static int pg_popcount32_slow(uint32 word); -static int pg_popcount64_slow(uint64 word); - -#if defined(HAVE__GET_CPUID) && (defined(HAVE__BUILTIN_CTZ) || defined(HAVE__BUILTIN_CLZ)) -static bool pg_lzcnt_available(void); -#endif - -#if defined(HAVE__BUILTIN_CTZ) && defined(HAVE__GET_CPUID) -static int pg_rightmost_one32_choose(uint32 word); -static int pg_rightmost_one32_abm(uint32 word); -static int pg_rightmost_one64_choose(uint64 word); -static int pg_rightmost_one64_abm(uint64 word); -#endif -static int pg_rightmost_one32_slow(uint32 word); -static int pg_rightmost_one64_slow(uint64 word); - -#if defined(HAVE__BUILTIN_CLZ) && defined(HAVE__GET_CPUID) -static int pg_leftmost_one32_choose(uint32 word); -static int pg_leftmost_one32_abm(uint32 word); -static int pg_leftmost_one64_choose(uint64 word); -static int pg_leftmost_one64_abm(uint64 word); -#endif -static int pg_leftmost_one32_slow(uint32 word); -static int pg_leftmost_one64_slow(uint64 word); - -#if defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE__GET_CPUID) +static int pg_popcount64_builtin(uint64 word); int (*pg_popcount32) (uint32 word) = pg_popcount32_choose; int (*pg_popcount64) (uint64 word) = pg_popcount64_choose; #else +static int pg_popcount32_slow(uint32 word); +static int pg_popcount64_slow(uint64 word); int (*pg_popcount32) (uint32 word) = pg_popcount32_slow; int (*pg_popcount64) (uint64 word) = pg_popcount64_slow; #endif -#if defined(HAVE__BUILTIN_CTZ) && defined(HAVE__GET_CPUID) +#if defined(HAVE__BUILTIN_CTZ) || defined(HAVE__BUILTIN_CLZ) +static bool pg_lzcnt_available(void); +#endif + +#ifdef HAVE__BUILTIN_CTZ +static int pg_rightmost_one32_choose(uint32 word); +static int pg_rightmost_one32_abm(uint32 word); +static int pg_rightmost_one64_choose(uint64 word); +static int pg_rightmost_one64_abm(uint64 word); int (*pg_rightmost_one32) (uint32 word) = pg_rightmost_one32_choose; int (*pg_rightmost_one64) (uint64 word) = pg_rightmost_one64_choose; #else int (*pg_rightmost_one32) (uint32 word) = pg_rightmost_one32_slow; int (*pg_rightmost_one64) (uint64 word) = pg_rightmost_one64_slow; #endif +static int pg_rightmost_one32_slow(uint32 word); +static int pg_rightmost_one64_slow(uint64 word); -#if defined(HAVE__BUILTIN_CLZ) && defined(HAVE__GET_CPUID) +#ifdef HAVE__BUILTIN_CLZ +static int pg_leftmost_one32_choose(uint32 word); +static int pg_leftmost_one32_abm(uint32 word); +static int pg_leftmost_one64_choose(uint64 word); +static int pg_leftmost_one64_abm(uint64 word); int (*pg_leftmost_one32) (uint32 word) = pg_leftmost_one32_choose; int (*pg_leftmost_one64) (uint64 word) = pg_leftmost_one64_choose; #else int (*pg_leftmost_one32) (uint32 word) = pg_leftmost_one32_slow; int (*pg_leftmost_one64) (uint64 word) = pg_leftmost_one64_slow; #endif +static int pg_leftmost_one32_slow(uint32 word); +static int pg_leftmost_one64_slow(uint64 word); /* Array marking the number of 1-bits for each value of 0-255. */ static const uint8 number_of_ones[256] = { @@ -147,27 +137,27 @@ static const uint8 leftmost_one_pos[256] = { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; -#if defined(HAVE__GET_CPUID) && defined(HAVE__BUILTIN_POPCOUNT) static bool pg_popcount_available(void) { +#if defined(HAVE__GET_CPUID) || defined(HAVE__CPUID) unsigned int exx[4] = { 0, 0, 0, 0 }; #if defined(HAVE__GET_CPUID) __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]); #elif defined(HAVE__CPUID) __cpuid(exx, 1); -#else -#error cpuid instruction not available #endif return (exx[2] & (1 << 23)) != 0; /* POPCNT */ -} +#else /* HAVE__GET_CPUID || HAVE__CPUID */ + + return false; #endif +} -#if defined(HAVE__GET_CPUID) && defined(HAVE__BUILTIN_POPCOUNT) - +#ifdef HAVE__BUILTIN_POPCOUNT /* * This gets called on the first call. It replaces the function pointer * so that subsequent calls are routed directly to the chosen implementation. @@ -178,18 +168,19 @@ pg_popcount32_choose(uint32 word) if (pg_popcount_available()) pg_popcount32 = pg_popcount32_sse42; else - pg_popcount32 = pg_popcount32_slow; + pg_popcount32 = pg_popcount32_builtin; return pg_popcount32(word); } static int -pg_popcount32_sse42(uint32 word) +pg_popcount32_builtin(uint32 word) { return __builtin_popcount(word); } #endif +#ifndef HAVE__BUILTIN_POPCOUNT /* * pg_popcount32_slow * Return the number of 1 bits set in word @@ -207,6 +198,7 @@ pg_popcount32_slow(uint32 word) return result; } +#endif /* * pg_popcount @@ -254,8 +246,7 @@ pg_popcount(const char *buf, int bytes) return popcnt; } -#if defined(HAVE__GET_CPUID) && defined(HAVE__BUILTIN_POPCOUNT) - +#ifdef HAVE__BUILTIN_POPCOUNT /* * This gets called on the first call. It replaces the function pointer * so that subsequent calls are routed directly to the chosen implementation. @@ -266,26 +257,26 @@ pg_popcount64_choose(uint64 word) if (pg_popcount_available()) pg_popcount64 = pg_popcount64_sse42; else - pg_popcount64 = pg_popcount64_slow; + pg_popcount64 = pg_popcount64_builtin; return pg_popcount64(word); } static int -pg_popcount64_sse42(uint64 word) +pg_popcount64_builtin(uint64 word) { #if defined(HAVE_LONG_INT_64) return __builtin_popcountl(word); #elif defined(HAVE_LONG_LONG_INT_64) return __builtin_popcountll(word); #else - /* shouldn't happen */ #error must have a working 64-bit integer datatype #endif } -#endif +#endif /* HAVE__BUILTIN_POPCOUNT */ +#ifndef HAVE__BUILTIN_POPCOUNT /* * pg_popcount64_slow * Return the number of 1 bits set in word @@ -303,28 +294,28 @@ pg_popcount64_slow(uint64 word) return result; } - -#if defined(HAVE__GET_CPUID) && (defined(HAVE__BUILTIN_CTZ) || defined(HAVE__BUILTIN_CLZ)) +#endif static bool pg_lzcnt_available(void) { - +#if (defined(HAVE__GET_CPUID) || defined(HAVE__CPUID)) unsigned int exx[4] = { 0, 0, 0, 0 }; #if defined(HAVE__GET_CPUID) __get_cpuid(0x80000001, &exx[0], &exx[1], &exx[2], &exx[3]); #elif defined(HAVE__CPUID) __cpuid(exx, 0x80000001); -#else -#error cpuid instruction not available #endif return (exx[2] & (1 << 5)) != 0; /* LZCNT */ -} -#endif +#else /* HAVE__GET_CPUID || HAVE__CPUID */ -#if defined(HAVE__GET_CPUID) && defined(HAVE__BUILTIN_CTZ) + return false; +#endif +} + +#ifdef HAVE__BUILTIN_CTZ /* * This gets called on the first call. It replaces the function pointer * so that subsequent calls are routed directly to the chosen implementation. @@ -346,7 +337,7 @@ pg_rightmost_one32_abm(uint32 word) return __builtin_ctz(word); } -#endif +#endif /* HAVE__BUILTIN_CTZ */ /* * pg_rightmost_one32_slow @@ -370,7 +361,7 @@ pg_rightmost_one32_slow(uint32 word) return result; } -#if defined(HAVE__GET_CPUID) && defined(HAVE__BUILTIN_CTZ) +#ifdef HAVE__BUILTIN_CTZ /* * This gets called on the first call. It replaces the function pointer * so that subsequent calls are routed directly to the chosen implementation. @@ -394,11 +385,10 @@ pg_rightmost_one64_abm(uint64 word) #elif defined(HAVE_LONG_LONG_INT_64) return __builtin_ctzll(word); #else - /* shouldn't happen */ #error must have a working 64-bit integer datatype #endif } -#endif +#endif /* HAVE_BUILTIN_CTZ */ /* * pg_rightmost_one64_slow @@ -422,7 +412,7 @@ pg_rightmost_one64_slow(uint64 word) return result; } -#if defined(HAVE__GET_CPUID) && defined(HAVE__BUILTIN_CLZ) +#ifdef HAVE__BUILTIN_CLZ /* * This gets called on the first call. It replaces the function pointer * so that subsequent calls are routed directly to the chosen implementation. @@ -443,7 +433,7 @@ pg_leftmost_one32_abm(uint32 word) { return 31 - __builtin_clz(word); } -#endif +#endif /* HAVE__BUILTIN_CLZ */ /* * pg_leftmost_one32_slow @@ -463,7 +453,7 @@ pg_leftmost_one32_slow(uint32 word) return shift + leftmost_one_pos[(word >> shift) & 255]; } -#if defined(HAVE__GET_CPUID) && defined(HAVE__BUILTIN_CLZ) +#ifdef HAVE__BUILTIN_CLZ /* * This gets called on the first call. It replaces the function pointer * so that subsequent calls are routed directly to the chosen implementation. @@ -487,12 +477,10 @@ pg_leftmost_one64_abm(uint64 word) #elif defined(HAVE_LONG_LONG_INT_64) return 63 - __builtin_clzll(word); #else - /* shouldn't happen */ #error must have a working 64-bit integer datatype #endif - } -#endif +#endif /* HAVE__BUIILTIN_CLZ */ /* * pg_leftmost_one64_slow diff --git a/src/port/pg_popcount.c b/src/port/pg_popcount.c new file mode 100644 index 00000000000..5254c41273f --- /dev/null +++ b/src/port/pg_popcount.c @@ -0,0 +1,36 @@ +/*------------------------------------------------------------------------- + * + * pg_popcount.c + * CPU-optimized implementation of pg_popcount + * + * This file must be compiled with a compiler-specific flag to enable the + * POPCOUNT instruction. + * + * Portions Copyright (c) 2019, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/pg_popcount.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "port/pg_bitutils.h" + +int +pg_popcount32_sse42(uint32 word) +{ + return __builtin_popcount(word); +} + +int +pg_popcount64_sse42(uint64 word) +{ +#if defined(HAVE_LONG_INT_64) + return __builtin_popcountl(word); +#elif defined(HAVE_LONG_LONG_INT_64) + return __builtin_popcountll(word); +#else +#error must have a working 64-bit integer datatype +#endif +} --Q68bSM7Ycu6FN28Q--