Thank you for running the profiles.

On Tue, 27 Jun 2023 at 21:11, Yuya Watari <watari.y...@gmail.com> wrote:
> On Sat, Jun 24, 2023 at 1:15 PM David Rowley <dgrowle...@gmail.com> wrote:
> > I think it's also important to check we don't slow anything down for
> > more normal-sized sets.  The vast majority of sets will contain just a
> > single word, so we should probably focus on making sure we're not
> > slowing anything down for those.
>
> I agree with you and thank you for sharing the results. I ran
> installcheck with your patch. The result is as follows. The speedup
> was 0.33%. At least in my environment, I did not observe any
> regression with this test. So, the patch looks very good.
>
> Master:  2.559648 seconds
> Patched: 2.551116 seconds (0.33% faster)

I wondered if the common case could be made slightly faster by
checking the 0th word before checking the word count before going onto
check the remaining words. For bms_equal(), that's something like:

if (a->words[0] != b->words[0] || a->nwords != b->nwords)
    return false;

/* check all the remaining words match */
for (int i = 1; i < a->nwords; i++) ...

I wrote the patch and tried it out, but it seems slightly slower than
the v4 patch.

Linux with AMD 3990x, again using the patch from [1] with make installcheck

master: 1.41720145 seconds
v4: 1.392969606 seconds (1.74% faster than master)
v4 with 0th word check: 1.404199748 seconds (0.93% faster than master)

I've attached a delta patch of what I used to test.  Since it's not
any faster, I don't think it's worth doing. It'll also produce
slightly more compiled code.

David

[1] 
https://postgr.es/m/caaphdvo68m_0juthnehfnsdsjeb2upphk6bwxstj93u_qei...@mail.gmail.com
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 9cda3b1cc1..2ee7385f02 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -9,12 +9,7 @@
  * the minimum possible number of words, i.e, there are never any trailing
  * zero words.  Enforcing this requires that an empty set is represented as
  * NULL.  Because an empty Bitmapset is represented as NULL, a non-NULL
- * Bitmapset always has at least 1 Bitmapword.  We can exploit this fact to
- * speedup various loops over the Bitmapset's words array by using "do while"
- * loops instead of "for" loops.  This means the code does not waste time
- * checking the loop condition before the first iteration.  For Bitmapsets
- * containing only a single word (likely the majority of them) this reduces
- * the loop condition tests by half.
+ * Bitmapset always has at least 1 Bitmapword.
  *
  *
  * Copyright (c) 2003-2023, PostgreSQL Global Development Group
@@ -96,8 +91,6 @@ bms_copy(const Bitmapset *a)
 bool
 bms_equal(const Bitmapset *a, const Bitmapset *b)
 {
-       int                     i;
-
        /* Handle cases where either input is NULL */
        if (a == NULL)
        {
@@ -108,17 +101,20 @@ bms_equal(const Bitmapset *a, const Bitmapset *b)
        else if (b == NULL)
                return false;
 
-       /* can't be equal if the word counts don't match */
-       if (a->nwords != b->nwords)
+       /*
+        * Most Bitmapsets will likely just have 1 word, so check for equality
+        * there before checking the number of words match.  The sets cannot be
+        * equal when they don't have the same number of words.
+        */
+       if (a->words[0] != b->words[0] || a->nwords != b->nwords)
                return false;
 
-       /* check each word matches */
-       i = 0;
-       do
+       /* check all the remaining words match */
+       for (int i = 1; i < a->nwords; i++)
        {
                if (a->words[i] != b->words[i])
                        return false;
-       } while (++i < a->nwords);
+       }
 
        return true;
 }
@@ -353,25 +349,27 @@ bms_difference(const Bitmapset *a, const Bitmapset *b)
 bool
 bms_is_subset(const Bitmapset *a, const Bitmapset *b)
 {
-       int                     i;
-
        /* Handle cases where either input is NULL */
        if (a == NULL)
                return true;                    /* empty set is a subset of 
anything */
        if (b == NULL)
                return false;
 
-       /* 'a' can't be a subset of 'b' if it contains more words */
-       if (a->nwords > b->nwords)
+       /*
+        * Check the 0th word first as many sets will only have 1 word.
+        * Otherwise, 'a' can't be a subset of 'b' if it contains more words, so
+        * there's no need to check remaining words if 'a' contains more words
+        * than 'b'.
+        */
+       if ((a->words[0] & ~b->words[0]) != 0 || a->nwords > b->nwords)
                return false;
 
-       /* Check all 'a' members are set in 'b' */
-       i = 0;
-       do
+       /* Check all remaining words to see 'b' has members not set in 'a'. */
+       for (int i = 1; i < a->nwords; i++)
        {
                if ((a->words[i] & ~b->words[i]) != 0)
                        return false;
-       } while (++i < a->nwords);
+       }
        return true;
 }
 

Reply via email to