public inbox for [email protected]
help / color / mirror / Atom feedFrom: Alexandre Felipe <[email protected]>
To: Andres Freund <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Subject: Re: Addressing buffer private reference count scalability issue
Date: Sun, 15 Mar 2026 02:37:07 +0000
Message-ID: <CAE8JnxOKxayW45mO0_8Ee1-BvJ=+Og+kOsD_5O7VYrG7_wargw@mail.gmail.com> (raw)
In-Reply-To: <CAE8JnxP=oPCmZs70VQ6U=35uLceqJVBQz3qakPR79cSkt7HU-g@mail.gmail.com>
References: <CAE8JnxNTETEUiAOF31=_yo=pvyAi9npOeJfcTvEJJbi4vomtYA@mail.gmail.com>
<krknshnvus4qhehtoqtwnroemgxqwlfmykark6umd6hf64xnku@ibxx734ds3ga>
<CAE8JnxNqf=sYB-hfeHBEtXi+aC8jezqqukdgRuR2=t8nbetL=w@mail.gmail.com>
<rfjyce5hmfkp2pbgjaxvmc76zy33kpokigbkwnounxfmz6uyd5@vt7yxibmfy6n>
<CAE8JnxP=oPCmZs70VQ6U=35uLceqJVBQz3qakPR79cSkt7HU-g@mail.gmail.com>
Hi Andres,
> I don't think it's a good idea to introduce new simplehash infrastructure
as
> part of this larger change.
I am submitting the change I did before on simplehash for empty entry
detection.
> You also haven't documented the new stuff.
What and where I was supposed to document?
01 the change I did before, 02 I applied it to refcount in bufmgr.
Exploring a bit more the code base, 03 removes status from nodeMemoize
04 remove status from pg_rewind/filemap.c
05 was a bit trickier because InvalidBlockNumber is not 0, then I had to
make entries empty after allocation that uses memset 0 by default.
I grepped for a list and there are 21 files in total so I will stop here.
> In my benchmarks allowing vectorization helped a decent amount in real
> queries, because it does away with all the branch misses.
Interesting, did you compile with the default configuration?
I used gcc11 (a bit old I know) and and yes, it remove some branches
but still use a loop with cmove (conditional copy), in some cases it unrolls
but still uses cmove for each entry (the machine I tested is quite feature
rich
e.g. avx512cd avx512bw avx512vl avx512_vnni.
So I am wondering if the impact I see is not the same impact as you see.
If we go for vectorisation we could do a vectorized loop
e.g. 16 iterations on 16 x 32-bit vectors with early exit.
but that would inevitably make the few entries case slightly slower.
Do you have any other localised issue like this that could be worth looking
into?
Regards,
Alexandre
Attachments:
[application/octet-stream] v3-0004-Use-null-pointer-as-an-empty-marker.patch (1.9K, 3-v3-0004-Use-null-pointer-as-an-empty-marker.patch)
download | inline diff:
From f457eb9c232f0e35143aa3ce37480b7b07148c53 Mon Sep 17 00:00:00 2001
From: Bob <[email protected]>
Date: Sun, 15 Mar 2026 01:23:12 +0000
Subject: [PATCH 4/5] Use null pointer as an empty marker
Here simplehash is being simply used as a Set
NULL pointer is a natural empty marker.
---
src/bin/pg_rewind/filemap.c | 7 ++++++-
src/bin/pg_rewind/filemap.h | 2 --
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index b79c47f925..574a282d84 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -45,6 +45,9 @@
#define SH_KEY path
#define SH_HASH_KEY(tb, key) hash_string(key)
#define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
+#define SH_ENTRY_EMPTY(entry) ((entry)->path == NULL)
+#define SH_MAKE_EMPTY(entry) ((entry)->path = NULL)
+#define SH_MAKE_IN_USE(entry) ((void)0)
#define SH_SCOPE static inline
#define SH_RAW_ALLOCATOR pg_malloc0
#define SH_DECLARE
@@ -68,7 +71,6 @@ static file_entry_t *lookup_filehash_entry(const char *path);
typedef struct keepwal_entry
{
const char *path;
- uint32 status;
} keepwal_entry;
#define SH_PREFIX keepwal
@@ -77,6 +79,9 @@ typedef struct keepwal_entry
#define SH_KEY path
#define SH_HASH_KEY(tb, key) hash_string(key)
#define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
+#define SH_ENTRY_EMPTY(entry) ((entry)->path == NULL)
+#define SH_MAKE_EMPTY(entry) ((entry)->path = NULL)
+#define SH_MAKE_IN_USE(entry) ((void)0)
#define SH_SCOPE static inline
#define SH_RAW_ALLOCATOR pg_malloc0
#define SH_DECLARE
diff --git a/src/bin/pg_rewind/filemap.h b/src/bin/pg_rewind/filemap.h
index 4c6dd8740d..b3e67be1da 100644
--- a/src/bin/pg_rewind/filemap.h
+++ b/src/bin/pg_rewind/filemap.h
@@ -56,8 +56,6 @@ typedef enum
*/
typedef struct file_entry_t
{
- uint32 status; /* hash status */
-
const char *path;
file_content_type_t content_type;
--
2.34.1
[application/octet-stream] v3-0001-Custom-simplehash-empty-value-detection.patch (6.1K, 4-v3-0001-Custom-simplehash-empty-value-detection.patch)
download | inline diff:
From db04956b20ba4324a0cd4d391a2d2e3e2a358831 Mon Sep 17 00:00:00 2001
From: Alexandre Felipe <[email protected]>
Date: Sun, 15 Mar 2026 00:00:00 +0000
Subject: [PATCH 1/5] Custom simplehash empty value detection
Changes the empty value identification in simplehash allowing custom
values to be used. The default continues to use the status field.
For types where the key value already has an "invalid" value,
the macros SH_ENTRY_EMPTY, SH_MAKE_EMPTY and SH_MAKE_IN_USE can be
overridden to elliminate the need for a separate status field.
---
src/include/lib/simplehash.h | 59 +++++++++++++++++++++++-------------
1 file changed, 38 insertions(+), 21 deletions(-)
diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index 848719232a..3c03a7e9c9 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -287,6 +287,20 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
#define SH_COMPARE_KEYS(tb, ahash, akey, b) (SH_EQUAL(tb, b->SH_KEY, akey))
#endif
+/*
+ * Macros to check/set entry status. Users can override these to avoid
+ * needing a separate status field if their key type has an "invalid" value.
+ */
+#ifndef SH_ENTRY_EMPTY
+#define SH_ENTRY_EMPTY(entry) ((entry)->status == SH_STATUS_EMPTY)
+#endif
+#ifndef SH_MAKE_EMPTY
+#define SH_MAKE_EMPTY(entry) ((entry)->status = SH_STATUS_EMPTY)
+#endif
+#ifndef SH_MAKE_IN_USE
+#define SH_MAKE_IN_USE(entry) ((entry)->status = SH_STATUS_IN_USE)
+#endif
+
/*
* Wrap the following definitions in include guards, to avoid multiple
* definition errors if this header is included more than once. The rest of
@@ -544,7 +558,7 @@ SH_GROW(SH_TYPE * tb, uint64 newsize)
uint32 hash;
uint32 optimal;
- if (oldentry->status != SH_STATUS_IN_USE)
+ if (SH_ENTRY_EMPTY(oldentry))
{
startelem = i;
break;
@@ -566,7 +580,7 @@ SH_GROW(SH_TYPE * tb, uint64 newsize)
{
SH_ELEMENT_TYPE *oldentry = &olddata[copyelem];
- if (oldentry->status == SH_STATUS_IN_USE)
+ if (!SH_ENTRY_EMPTY(oldentry))
{
uint32 hash;
uint32 startelem2;
@@ -582,7 +596,7 @@ SH_GROW(SH_TYPE * tb, uint64 newsize)
{
newentry = &newdata[curelem];
- if (newentry->status == SH_STATUS_EMPTY)
+ if (SH_ENTRY_EMPTY(newentry))
{
break;
}
@@ -653,14 +667,14 @@ restart:
SH_ELEMENT_TYPE *entry = &data[curelem];
/* any empty bucket can directly be used */
- if (entry->status == SH_STATUS_EMPTY)
+ if (SH_ENTRY_EMPTY(entry))
{
tb->members++;
entry->SH_KEY = key;
#ifdef SH_STORE_HASH
SH_GET_HASH(tb, entry) = hash;
#endif
- entry->status = SH_STATUS_IN_USE;
+ SH_MAKE_IN_USE(entry);
*found = false;
return entry;
}
@@ -675,7 +689,7 @@ restart:
if (SH_COMPARE_KEYS(tb, hash, key, entry))
{
- Assert(entry->status == SH_STATUS_IN_USE);
+ Assert(!SH_ENTRY_EMPTY(entry));
*found = true;
return entry;
}
@@ -699,7 +713,7 @@ restart:
emptyelem = SH_NEXT(tb, emptyelem, startelem);
emptyentry = &data[emptyelem];
- if (emptyentry->status == SH_STATUS_EMPTY)
+ if (SH_ENTRY_EMPTY(emptyentry))
{
lastentry = emptyentry;
break;
@@ -748,7 +762,7 @@ restart:
#ifdef SH_STORE_HASH
SH_GET_HASH(tb, entry) = hash;
#endif
- entry->status = SH_STATUS_IN_USE;
+ SH_MAKE_IN_USE(entry);
*found = false;
return entry;
}
@@ -810,12 +824,12 @@ SH_LOOKUP_HASH_INTERNAL(SH_TYPE * tb, SH_KEY_TYPE key, uint32 hash)
{
SH_ELEMENT_TYPE *entry = &tb->data[curelem];
- if (entry->status == SH_STATUS_EMPTY)
+ if (SH_ENTRY_EMPTY(entry))
{
return NULL;
}
- Assert(entry->status == SH_STATUS_IN_USE);
+ Assert(!SH_ENTRY_EMPTY(entry));
if (SH_COMPARE_KEYS(tb, hash, key, entry))
return entry;
@@ -868,10 +882,10 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key)
{
SH_ELEMENT_TYPE *entry = &tb->data[curelem];
- if (entry->status == SH_STATUS_EMPTY)
+ if (SH_ENTRY_EMPTY(entry))
return false;
- if (entry->status == SH_STATUS_IN_USE &&
+ if (!SH_ENTRY_EMPTY(entry) &&
SH_COMPARE_KEYS(tb, hash, key, entry))
{
SH_ELEMENT_TYPE *lastentry = entry;
@@ -894,9 +908,9 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key)
curelem = SH_NEXT(tb, curelem, startelem);
curentry = &tb->data[curelem];
- if (curentry->status != SH_STATUS_IN_USE)
+ if (SH_ENTRY_EMPTY(curentry))
{
- lastentry->status = SH_STATUS_EMPTY;
+ SH_MAKE_EMPTY(lastentry);
break;
}
@@ -906,7 +920,7 @@ SH_DELETE(SH_TYPE * tb, SH_KEY_TYPE key)
/* current is at optimal position, done */
if (curoptimal == curelem)
{
- lastentry->status = SH_STATUS_EMPTY;
+ SH_MAKE_EMPTY(lastentry);
break;
}
@@ -957,9 +971,9 @@ SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
curelem = SH_NEXT(tb, curelem, startelem);
curentry = &tb->data[curelem];
- if (curentry->status != SH_STATUS_IN_USE)
+ if (SH_ENTRY_EMPTY(curentry))
{
- lastentry->status = SH_STATUS_EMPTY;
+ SH_MAKE_EMPTY(lastentry);
break;
}
@@ -969,7 +983,7 @@ SH_DELETE_ITEM(SH_TYPE * tb, SH_ELEMENT_TYPE * entry)
/* current is at optimal position, done */
if (curoptimal == curelem)
{
- lastentry->status = SH_STATUS_EMPTY;
+ SH_MAKE_EMPTY(lastentry);
break;
}
@@ -997,7 +1011,7 @@ SH_START_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
{
SH_ELEMENT_TYPE *entry = &tb->data[i];
- if (entry->status != SH_STATUS_IN_USE)
+ if (SH_ENTRY_EMPTY(entry))
{
startelem = i;
break;
@@ -1063,7 +1077,7 @@ SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
if ((iter->cur & tb->sizemask) == (iter->end & tb->sizemask))
iter->done = true;
- if (elem->status == SH_STATUS_IN_USE)
+ if (!SH_ENTRY_EMPTY(elem))
{
return elem;
}
@@ -1140,7 +1154,7 @@ SH_STAT(SH_TYPE * tb)
elem = &tb->data[i];
- if (elem->status != SH_STATUS_IN_USE)
+ if (SH_ENTRY_EMPTY(elem))
continue;
hash = SH_ENTRY_HASH(tb, elem);
@@ -1205,6 +1219,9 @@ SH_STAT(SH_TYPE * tb)
#undef SH_STORE_HASH
#undef SH_USE_NONDEFAULT_ALLOCATOR
#undef SH_EQUAL
+#undef SH_ENTRY_EMPTY
+#undef SH_MAKE_EMPTY
+#undef SH_MAKE_IN_USE
/* undefine locally declared macros */
#undef SH_MAKE_PREFIX
--
2.34.1
[application/octet-stream] v3-0003-Use-NULL-key-as-empty-in-nodeMemoize.c.patch (1.3K, 5-v3-0003-Use-NULL-key-as-empty-in-nodeMemoize.c.patch)
download | inline diff:
From e091fd06217aae30cf73697156b22bc66c382445 Mon Sep 17 00:00:00 2001
From: Bob <[email protected]>
Date: Sun, 15 Mar 2026 01:19:05 +0000
Subject: [PATCH 3/5] Use NULL key as empty in nodeMemoize.c
MemoizeKey *key is describeed as Hash key for hash table lookups
But it is not as one would expect.
MemoizeHash_hash computes hash from tb->private_data ignoring the key
Similarly Memoize_equal doesn't use key2, and uses probeslot from tb->private_data.
At line 545
memoize_insert(mstate->hashtable, NULL, found);
if not found we hold a pointer to an entry still empty in
the simplehash, until we assign it a key (line 561)
---
src/backend/executor/nodeMemoize.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c
index fdca97d742..44154ad25a 100644
--- a/src/backend/executor/nodeMemoize.c
+++ b/src/backend/executor/nodeMemoize.c
@@ -143,6 +143,9 @@ static bool MemoizeHash_equal(struct memoize_hash *tb,
#define SH_KEY key
#define SH_HASH_KEY(tb, key) MemoizeHash_hash(tb, key)
#define SH_EQUAL(tb, a, b) MemoizeHash_equal(tb, a, b)
+#define SH_ENTRY_EMPTY(entry) ((entry)->key == NULL)
+#define SH_MAKE_EMPTY(entry) ((entry)->key = NULL)
+#define SH_MAKE_IN_USE(entry) ((void)0)
#define SH_SCOPE static inline
#define SH_STORE_HASH
#define SH_GET_HASH(tb, a) a->hash
--
2.34.1
[application/octet-stream] v3-0005-Use-InvalidBlockNumber-as-empty-marker.patch (5.2K, 6-v3-0005-Use-InvalidBlockNumber-as-empty-marker.patch)
download | inline diff:
From 210916bcc236856e63b258a03573da5319b3e244 Mon Sep 17 00:00:00 2001
From: Bob <[email protected]>
Date: Sun, 15 Mar 2026 01:48:09 +0000
Subject: [PATCH 5/5] Use InvalidBlockNumber as empty marker
This one required an update on simplehash implementation
InvalidBlockNumber defined as ((BlockNumber) 0xFFFFFFFF)
in ./src/include/storage/block.h by default SimpleHash simply
zeroes the memory and that makes everything empty.
For this case we have to call SH_MAKE_EMPTY on each
entry after allocating.
Removing the status field also a the hacks
old_status = entry->status
modify entry, corrupts status
entry->status = old_status
where the status initialized by simplehash had to be
saved and restored when updating the entry.
---
src/backend/nodes/tidbitmap.c | 19 ++++++-------------
src/include/lib/simplehash.h | 22 ++++++++++++++++++++++
2 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index f1f925cb13..207f27f0ca 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -92,7 +92,6 @@
typedef struct PagetableEntry
{
BlockNumber blockno; /* page number (hashtable key) */
- char status; /* hash entry status */
bool ischunk; /* T = lossy storage, F = exact */
bool recheck; /* should the tuples be rechecked? */
bitmapword words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)];
@@ -237,6 +236,12 @@ static int tbm_shared_comparator(const void *left, const void *right,
#define SH_KEY blockno
#define SH_HASH_KEY(tb, key) murmurhash32(key)
#define SH_EQUAL(tb, a, b) a == b
+#define SH_ENTRY_EMPTY(entry) ((entry)->blockno == InvalidBlockNumber)
+#define SH_MAKE_EMPTY(entry) ((entry)->blockno = InvalidBlockNumber)
+#define SH_MAKE_IN_USE(entry) ((void)0)
+// Since the empty marker is non-zero, we need to reset the entries
+// after allocation using the custom SH_MAKE_EMPTY macro.
+#define SH_NONZERO_EMPTY
#define SH_SCOPE static inline
#define SH_DEFINE
#define SH_DECLARE
@@ -291,15 +296,12 @@ tbm_create_pagetable(TIDBitmap *tbm)
{
PagetableEntry *page;
bool found;
- char oldstatus;
page = pagetable_insert(tbm->pagetable,
tbm->entry1.blockno,
&found);
Assert(!found);
- oldstatus = page->status;
memcpy(page, &tbm->entry1, sizeof(PagetableEntry));
- page->status = oldstatus;
}
tbm->status = TBM_HASH;
@@ -1230,10 +1232,7 @@ tbm_get_pageentry(TIDBitmap *tbm, BlockNumber pageno)
/* Initialize it if not present before */
if (!found)
{
- char oldstatus = page->status;
-
MemSet(page, 0, sizeof(PagetableEntry));
- page->status = oldstatus;
page->blockno = pageno;
/* must count it too */
tbm->nentries++;
@@ -1317,10 +1316,7 @@ tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno)
/* Initialize it if not present before */
if (!found)
{
- char oldstatus = page->status;
-
MemSet(page, 0, sizeof(PagetableEntry));
- page->status = oldstatus;
page->blockno = chunk_pageno;
page->ischunk = true;
/* must count it too */
@@ -1329,11 +1325,8 @@ tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno)
}
else if (!page->ischunk)
{
- char oldstatus = page->status;
-
/* chunk header page was formerly non-lossy, make it lossy */
MemSet(page, 0, sizeof(PagetableEntry));
- page->status = oldstatus;
page->blockno = chunk_pageno;
page->ischunk = true;
/* we assume it had some tuple bit(s) set, so mark it lossy */
diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index 3c03a7e9c9..c890a09b76 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -301,6 +301,12 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
#define SH_MAKE_IN_USE(entry) ((entry)->status = SH_STATUS_IN_USE)
#endif
+/*
+ * If the empty marker is non-zero (e.g., InvalidBlockNumber = 0xFFFFFFFF),
+ * define SH_NONZERO_EMPTY to explicitly initialize entries. When unset,
+ * zero-initialization via memset is sufficient (the default).
+ */
+
/*
* Wrap the following definitions in include guards, to avoid multiple
* definition errors if this header is included more than once. The rest of
@@ -481,6 +487,11 @@ SH_CREATE(MemoryContext ctx, uint32 nelements, void *private_data)
tb->data = (SH_ELEMENT_TYPE *) SH_ALLOCATE(tb, sizeof(SH_ELEMENT_TYPE) * size);
+#ifdef SH_NONZERO_EMPTY
+ for (uint64 i = 0; i < size; i++)
+ SH_MAKE_EMPTY(&tb->data[i]);
+#endif
+
SH_UPDATE_PARAMETERS(tb, size);
return tb;
}
@@ -497,7 +508,12 @@ SH_DESTROY(SH_TYPE * tb)
SH_SCOPE void
SH_RESET(SH_TYPE * tb)
{
+#ifdef SH_NONZERO_EMPTY
+ for (uint32 i = 0; i < tb->size; i++)
+ SH_MAKE_EMPTY(&tb->data[i]);
+#else
memset(tb->data, 0, sizeof(SH_ELEMENT_TYPE) * tb->size);
+#endif
tb->members = 0;
}
@@ -526,6 +542,11 @@ SH_GROW(SH_TYPE * tb, uint64 newsize)
tb->data = (SH_ELEMENT_TYPE *) SH_ALLOCATE(tb, sizeof(SH_ELEMENT_TYPE) * newsize);
+#ifdef SH_NONZERO_EMPTY
+ for (uint64 j = 0; j < newsize; j++)
+ SH_MAKE_EMPTY(&tb->data[j]);
+#endif
+
/*
* Update parameters for new table after allocation succeeds to avoid
* inconsistent state on OOM.
@@ -1222,6 +1243,7 @@ SH_STAT(SH_TYPE * tb)
#undef SH_ENTRY_EMPTY
#undef SH_MAKE_EMPTY
#undef SH_MAKE_IN_USE
+#undef SH_NONZERO_EMPTY
/* undefine locally declared macros */
#undef SH_MAKE_PREFIX
--
2.34.1
[application/octet-stream] v3-0002-Use-InvalidBuffer-to-indicate-empty-slot.patch (1.5K, 7-v3-0002-Use-InvalidBuffer-to-indicate-empty-slot.patch)
download | inline diff:
From 056b123ae41d8ee8da5fdf5d4ee9cd886a52b2d9 Mon Sep 17 00:00:00 2001
From: Bob <[email protected]>
Date: Sun, 15 Mar 2026 00:13:55 +0000
Subject: [PATCH 2/5] Use InvalidBuffer to indicate empty slot
Simple hash requires a mechanism to distinguish
empty slots. The previous implementation of refcount
simplehash was using the default `status` field, adding a
char to the PrivateRefCountEntr.
The buffer already has an reserved value InvalidBuffer that
can be used to mark an entry as empty. Making use of that
removes one field from PrivateRefCountEntry and keep it
32bit aligned, without padding required.
---
src/backend/storage/buffer/bufmgr.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 00bc609529..db911327f9 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -125,8 +125,6 @@ typedef struct PrivateRefCountEntry
*/
Buffer buffer;
- char status;
-
PrivateRefCountData data;
} PrivateRefCountEntry;
@@ -136,6 +134,9 @@ typedef struct PrivateRefCountEntry
#define SH_KEY buffer
#define SH_HASH_KEY(tb, key) murmurhash32((uint32) (key))
#define SH_EQUAL(tb, a, b) ((a) == (b))
+#define SH_ENTRY_EMPTY(entry) ((entry)->buffer == InvalidBuffer)
+#define SH_MAKE_EMPTY(entry) ((entry)->buffer = InvalidBuffer)
+#define SH_MAKE_IN_USE(entry) ((void)0) /* key assignment implies in use */
#define SH_SCOPE static inline
#define SH_DECLARE
#define SH_DEFINE
--
2.34.1
view thread (6+ messages)
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected]
Subject: Re: Addressing buffer private reference count scalability issue
In-Reply-To: <CAE8JnxOKxayW45mO0_8Ee1-BvJ=+Og+kOsD_5O7VYrG7_wargw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox