On Tue, Jun 30, 2026 at 11:03 AM Haibo Yan <[email protected]> wrote:
>
> On Tue, Jun 30, 2026 at 10:53 AM Masahiko Sawada <[email protected]> 
> wrote:
> >
> > On Mon, Jun 29, 2026 at 5:53 PM Haibo Yan <[email protected]> wrote:
> > >
> > > On Mon, Jun 29, 2026 at 2:55 PM Masahiko Sawada <[email protected]> 
> > > wrote:
> > > >
> > > > On Sun, Jun 28, 2026 at 7:20 PM Haibo Yan <[email protected]> wrote:
> > > > >
> > > > > On Thu, Jun 25, 2026 at 3:16 PM Masahiko Sawada 
> > > > > <[email protected]> wrote:
> > > > > >
> > > > > > On Thu, Jun 25, 2026 at 2:31 PM Haibo Yan <[email protected]> 
> > > > > > wrote:
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > On Thu, Jun 25, 2026 at 11:28 AM Masahiko Sawada 
> > > > > > > <[email protected]> wrote:
> > > > > > >>
> > > > > > >> Hi all,
> > > > > > >>
> > > > > > >> I'd like to propose the $subject.
> > > > > > >>
> > > > > > >> Since commit ec8719ccbfcd made hex_decode_safe() SIMD-aware, 
> > > > > > >> decoding
> > > > > > >> a run of hex digits is now fast. The attached patch reuses
> > > > > > >> hex_decode_safe() in the UUID input function to speed up parsing.
> > > > > > >>
> > > > > > >> We accept several textual forms of a UUID[1]. The fast path 
> > > > > > >> handles
> > > > > > >> the common ones: 32 hex digits, the canonical 8x-4x-4x-4x-12x 
> > > > > > >> form
> > > > > > >> (where "nx" means n hex digits), and either of those wrapped in
> > > > > > >> braces. Otherwise, it falls back to the ordinary scalar UUID 
> > > > > > >> parse.
> > > > > > >>
> > > > > > >> I've benchmarked the parse speed using the following query:
> > > > > > >>
> > > > > > >> CREATE TEMP TABLE u AS SELECT gen_random_uuid()::text AS t FROM
> > > > > > >> generate_series(1, 1000000);
> > > > > > >> EXPLAIN (ANALYZE, TIMING OFF) SELECT t::uuid FROM u;
> > > > > > >>
> > > > > > >> I compared the execution time of the second query, which measures
> > > > > > >> uuid_in() alone, with/without SIMD optimization. Here are 
> > > > > > >> results (the
> > > > > > >> median of 5 runs):
> > > > > > >>
> > > > > > >> HEAD: 208.879 ms
> > > > > > >> Patched: 40.983 ms
> > > > > > >>
> > > > > > >> The improvements look promising to me. But in a realistic 
> > > > > > >> pipeline the
> > > > > > >> parse is a small fraction of the work, so end-to-end gains could 
> > > > > > >> be
> > > > > > >> much smaller.
> > > > > > >>
> > > > > > >> Feedback is very welcome.
> > > > > > >>
> > > > > > > I may be missing something, but I wonder whether the fast path is 
> > > > > > > relying on
> > > > > > > slightly different input semantics from the existing UUID parser.
> > > > > > >
> > > > > > > In particular, hex_decode_safe() is not a strict “32 hex 
> > > > > > > characters only”
> > > > > > > decoder.  It skips whitespace, which is fine for its existing 
> > > > > > > callers, but I
> > > > > > > don’t think UUID input should treat whitespace inside the UUID 
> > > > > > > body as
> > > > > > > ignorable.
> > > > > >
> > > > > > Good catch! hex_decode_safe() skips whitespaces so the patch accepts
> > > > > > the following UUID value, which is bad:
> > > > > >
> > > > > > select '019f00b5-7f8a-722f-b707-59f0ed25cd  '::uuid;
> > > > > >                  uuid
> > > > > > --------------------------------------
> > > > > >  019f00b5-7f8a-722f-b707-59f0ed25cd00
> > > > > > (1 row)
> > > > > >
> > > > > > > Also, since hex_decode_safe() returns void, the UUID fast path
> > > > > > > cannot verify that exactly UUID_LEN bytes were produced.
> > > > > >
> > > > > > IIUC hex_decode_safe() does return the output length in bytes. So I
> > > > > > think we can fallback to the scalar UUID parser if
> > > > > > esctx.error_occurred is true or if the returned value is not 16.
> > > > > >
> > > > >
> > > > > You’re right, I misread that part.  Checking both 
> > > > > esctx.error_occurred and
> > > > > the returned length sounds good to me.
> > > > >
> > > > > > >
> > > > > > > So I think it would be safer either to pre-validate that the 32 
> > > > > > > source
> > > > > > > characters are all hex digits before calling hex_decode_safe(), 
> > > > > > > or to use a
> > > > > > > UUID-specific strict hex decoder for this path.  After that, a 
> > > > > > > comment
> > > > > > > explaining why hex_decode_safe() is safe here would make the 
> > > > > > > invariant much
> > > > > > > clearer.
> > > > > >
> > > > > > IIUC hex_decode_simd_helper() accepts only hex digits so we could
> > > > > > re-use it for UUID parsing. Let me check if the above idea of using
> > > > > > the return value works for us first.
> > > > > >
> > > > >
> > > > > That sounds reasonable.  My main concern was to keep the fast path’s 
> > > > > accepted
> > > > > input set identical to the scalar UUID parser.  Falling back when the 
> > > > > decoded
> > > > > length is not UUID_LEN, together with regression tests for whitespace 
> > > > > cases,
> > > > > should address that.
> > > > >
> > > > > > >
> > > > > > > Could you also add a few regression tests for invalid inputs that 
> > > > > > > contain
> > > > > > > whitespace inside otherwise fast-path-looking UUID strings?  For 
> > > > > > > example:
> > > > > > >
> > > > > > > ---------------------------------------------------------------
> > > > > > >
> > > > > > > SELECT 'a0eebc99 9c0b4ef8bb6d6bb9bd380a11'::uuid;
> > > > > > > SELECT 'a0eebc999c0b4ef8bb6d6bb9bd380a1 '::uuid;
> > > > > > > SELECT '{a0eebc999c0b4ef8bb6d6bb9bd380a1 }'::uuid;
> > > > > > > SELECT 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a1 '::uuid;
> > > > > > > ---------------------------------------------------------------
> > > > > > >
> > > > > > > These should continue to be rejected in the same way as the 
> > > > > > > scalar parser.
> > > > > > > Regards,
> > > > > >
> > > > > > Agreed.
> > > > > >
> > > >
> > > > I've attached the updated patch.
> > > >
> > > > Regards,
> > > >
> > > > --
> > > > Masahiko Sawada
> > > > Amazon Web Services: https://aws.amazon.com
> > >
> > > I noticed a few typos in the comments:
> > >
> > > src/backend/utils/adt/uuid.c
> > > line 56: “scalar implmentation” -> “scalar implementation”
> > > line 109: “swalled” -> “swallowed”
> > > line 110: “kepping” -> “keeping”
> > > line 118: “grammer” -> “grammar”
> > > line 119: “whitespaces” -> “whitespace”
> > >
> > > Could you fix them ?
> >
> > Oops, I fixed them and rechecked other places.
> >
> > I've attached the updated patch.
> >
> > Regards,
> >
> > --
> > Masahiko Sawada
> > Amazon Web Services: https://aws.amazon.com
>
> The code looks good to me now.  I only noticed one small typo in the
> commit trailer: Reviwed-by should be Reviewed-by.
>
> Otherwise, it looks good.  Thank you for fixing these issues.
>

After spending more time on this patch, I find out two things:

1. USE_NO_SIMD doesn't work in uuid.c without including port/simd.h.
But including port/simd.h seems wrong as it doesn't use any SIMD
support functions.

2. hex_decode_safe() is faster than the current UUID parse
(isxdigit()+strtoul() approach) even without SIMD. I've created a
small benchmark test tool (attached as 0002 patch, not intended to be
pushed into the core), and measures UUID parsing performance of three
approaches: 'scalar' is the current string_to_uuid() that uses
isxdigit()+strtoul()), 'simd' uses hex_decode_safe() with SIMD, and
'nosimd' uses hex_decode_safe() without SIMD, with different shapes of
UUIDs. Here are results:

=# select path, shape, n_inputs, best_ms::numeric(10,3) from
uuid_parse_bench(100000, 5);
  path  |      shape       | n_inputs | best_ms
--------+------------------+----------+---------
 scalar | canonical        |   100000 |  22.661
 simd   | canonical        |   100000 |   1.400
 nosimd | canonical        |   100000 |   1.652
 scalar | bare32           |   100000 |  15.932
 simd   | bare32           |   100000 |   0.471
 nosimd | bare32           |   100000 |   1.110
 scalar | braced_canonical |   100000 |  17.330
 simd   | braced_canonical |   100000 |   1.088
 nosimd | braced_canonical |   100000 |   1.314
 scalar | braced_bare32    |   100000 |  15.942
 simd   | braced_bare32    |   100000 |   0.488
 nosimd | braced_bare32    |   100000 |   1.141
 scalar | dashed4          |   100000 |  16.185
 simd   | dashed4          |   100000 |  16.493
 nosimd | dashed4          |   100000 |  16.403
 scalar | invalid_hex      |   100000 |   0.199
 simd   | invalid_hex      |   100000 |   1.150
 nosimd | invalid_hex      |   100000 |   0.385
(18 rows)

Each of shape means:
  - 'canonical': 8x-4x-4x-4x-12x, what uuid_out() emits
  - 'bare32': 32 contiguous hex digits
  - 'braced_canonical': {8x-4x-4x-4x-12x}
  - 'braced_bare32': {32 hex digits}
  - 'dashed4': dash after every group of 4
  - 'invalid_hdx': canonical but with a invalid digit

'nosimd' is 10x~ faster than 'scalar' in most cases. All paths are
mostly the same in 'dashed4' and 'invalid_hex' cases because 'simd'
and 'nosimd' fall back to the 'scalar' case. According to these
results, my conclusion is that we can use hex_decode_safe() for
canonical forms and 32 contiguous hex forms anyway, and let
hex_decode_safe() choose whether to use SIMD. We would win in either
case. We still use the current scalar approach for uncommon UUID forms
and error reporting purposes.

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com
From 9d2edad34a1823bab012f5d0593d461a9ec396fb Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <[email protected]>
Date: Thu, 25 Jun 2026 10:03:44 -0700
Subject: [PATCH v4 1/2] Optimize UUID parse using SIMD.

Previously, string_to_uuid() decoded one byte at a time, calling
isxdigit() twice and strtoul() once for every pair of hexadecimal
digits.  That loop dominated the cost of uuid_in().

This commit adds a fast path for the two common shapes: a bare string
of 32 hexadecimal digits, and the canonical 8x-4x-4x-4x-12x form
(where "nx" means n hexadecimal digits), each optionally wrapped in
braces.  Both are compacted into 32 contiguous hexadecimal digits and
decoded with hex_decode_safe().  Any other shape, or any decoding
error, is handed off to the original scalar parser, now
string_to_uuid_scalar(), so the accepted grammar and the error
messages are unchanged.

hex_decode_safe() silently skips whitespace while the UUID grammar
does not, so a decode can succeed and still write fewer than UUID_LEN
bytes.  The fast path therefore treats a short result as a failure,
just like an error, and lets the scalar parser reject the input and
report the syntax error.

The fast path is deliberately not conditional on SIMD support.
hex_decode_safe() selects a vectorized or scalar implementation
itself, and even its scalar implementation is an order of magnitude
faster than decoding a byte at a time, so gating this on USE_NO_SIMD
would only penalize platforms that have neither SSE2 nor NEON.

Reviewed-by: Bharath Rupireddy <[email protected]>
Reviewed-by: Haibo Yan <[email protected]>
Discussion: https://postgr.es/m/cad21aocqer4uqu77q_yomnnzj7aveio5qzt+4hnzpm4wm-e...@mail.gmail.com
---
 src/backend/utils/adt/uuid.c       | 94 ++++++++++++++++++++++++++++--
 src/test/regress/expected/uuid.out | 55 +++++++++++++++++
 src/test/regress/sql/uuid.sql      | 16 +++++
 3 files changed, 160 insertions(+), 5 deletions(-)

diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index 246225a8735..3ecca6d3748 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -19,7 +19,9 @@
 #include "common/hashfn.h"
 #include "lib/hyperloglog.h"
 #include "libpq/pqformat.h"
+#include "nodes/miscnodes.h"
 #include "port/pg_bswap.h"
+#include "utils/builtins.h"
 #include "utils/fmgrprotos.h"
 #include "utils/guc.h"
 #include "utils/skipsupport.h"
@@ -139,13 +141,13 @@ uuid_out(PG_FUNCTION_ARGS)
 }
 
 /*
- * We allow UUIDs as a series of 32 hexadecimal digits with an optional dash
- * after each group of 4 hexadecimal digits, and optionally surrounded by {}.
- * (The canonical format 8x-4x-4x-4x-12x, where "nx" means n hexadecimal
- * digits, is the only one used for output.)
+ * Reference implementation of the UUID grammar, parsing one character at a
+ * time.  string_to_uuid() recognizes the common shapes more cheaply and
+ * defers to this function for everything else, so this is also the only
+ * place that reports a syntax error.
  */
 static void
-string_to_uuid(const char *source, pg_uuid_t *uuid, Node *escontext)
+string_to_uuid_scalar(const char *source, pg_uuid_t *uuid, Node *escontext)
 {
 	const char *src = source;
 	bool		braces = false;
@@ -194,6 +196,88 @@ syntax_error:
 					"uuid", source)));
 }
 
+/*
+ * We allow UUIDs as a series of 32 hexadecimal digits with an optional dash
+ * after each group of 4 hexadecimal digits, and optionally surrounded by {}.
+ * (The canonical format 8x-4x-4x-4x-12x, where "nx" means n hexadecimal
+ * digits, is the only one used for output.)
+ *
+ * The two common shapes -- a bare string of 32 hexadecimal digits and the
+ * canonical form, each optionally wrapped in braces -- are compacted into 32
+ * contiguous hex digits and decoded with hex_decode_safe(), which is much
+ * faster than the character-at-a-time loop.  Any other shape, or any decoding
+ * error, is handed off to string_to_uuid_scalar() so that the accepted
+ * grammar and the error messages are unchanged.
+ *
+ * Note that this fast path is not conditional on SIMD support:
+ * hex_decode_safe() picks a vectorized or scalar implementation itself, and
+ * even its scalar implementation is far faster than string_to_uuid_scalar().
+ */
+static void
+string_to_uuid(const char *source, pg_uuid_t *uuid, Node *escontext)
+{
+	const char *body = source;
+	size_t		len = strlen(source);
+	const char *hexsrc = NULL;
+	char		hexbuf[32];
+	uint64		written;
+	ErrorSaveContext esctx = {T_ErrorSaveContext};
+
+	/* Strip one optional surrounding brace pair */
+	if (len >= 2 && source[0] == '{' && source[len - 1] == '}')
+	{
+		body = source + 1;
+		len -= 2;
+	}
+
+	if (len == 32)
+	{
+		/*
+		 * Body is already 32 contiguous hex digits -- decode straight from
+		 * the input. hex_decode_safe() reads exactly body[0..31], so it never
+		 * touches the trailing NULL or '}'.
+		 */
+		hexsrc = body;
+	}
+	else if (len == 36 && body[8] == '-' && body[13] == '-' &&
+			 body[18] == '-' && body[23] == '-')
+	{
+		/*
+		 * Canonical 8x-4x-4x-4x-12x form; compact them into hexbuf with
+		 * fixed-offset copies, dropping the dashes.
+		 */
+		memcpy(&hexbuf[0], &body[0], 8);
+		memcpy(&hexbuf[8], &body[9], 4);
+		memcpy(&hexbuf[12], &body[14], 4);
+		memcpy(&hexbuf[16], &body[19], 4);
+		memcpy(&hexbuf[20], &body[24], 12);
+		hexsrc = hexbuf;
+	}
+
+	if (hexsrc == NULL)
+	{
+		/* Uncommon shape; let the general parse handle it */
+		string_to_uuid_scalar(source, uuid, escontext);
+		return;
+	}
+
+	/*
+	 * Decode the UUID hex data using our hex decoder that is SIMD-aware. We
+	 * give it a private error context so that a decode failure is swallowed
+	 * here and reported by the scalar path instead, keeping the error message
+	 * identical.
+	 */
+	written = hex_decode_safe(hexsrc, 32, (char *) uuid->data, (Node *) &esctx);
+
+	/*
+	 * Fall back to the scalar path on any error. We must also reject a short
+	 * result: hex_decode_safe() skips whitespace, so it can succeed yet write
+	 * fewer than UUID_LEN bytes, whereas the UUID grammar forbids whitespace.
+	 */
+	if (esctx.error_occurred || written != UUID_LEN)
+		string_to_uuid_scalar(source, uuid, escontext);
+}
+
 Datum
 uuid_recv(PG_FUNCTION_ARGS)
 {
diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out
index d542eb14b26..b40f50ac8be 100644
--- a/src/test/regress/expected/uuid.out
+++ b/src/test/regress/expected/uuid.out
@@ -375,5 +375,60 @@ SELECT v = v::bytea::uuid as matched FROM gen_random_uuid() v;
  t
 (1 row)
 
+-- Test UUID shapes that the parser uses the SIMD path.
+SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid;
+                 uuid                 
+--------------------------------------
+ 5b35380a-7143-4912-9b55-f322699c6770
+(1 row)
+
+SELECT '{5b35380a-7143-4912-9b55-f322699c6770}'::uuid;
+                 uuid                 
+--------------------------------------
+ 5b35380a-7143-4912-9b55-f322699c6770
+(1 row)
+
+SELECT '5b35380a714349129b55f322699c6770'::uuid;
+                 uuid                 
+--------------------------------------
+ 5b35380a-7143-4912-9b55-f322699c6770
+(1 row)
+
+SELECT '{5b35380a714349129b55f322699c6770}'::uuid;
+                 uuid                 
+--------------------------------------
+ 5b35380a-7143-4912-9b55-f322699c6770
+(1 row)
+
+-- Test if the UUID parser using SIMD optimization correctly rejects invalid UUID
+-- string format.
+SELECT '5b35380a714349129b55f32  99c6770'::uuid;
+ERROR:  invalid input syntax for type uuid: "5b35380a714349129b55f32  99c6770"
+LINE 1: SELECT '5b35380a714349129b55f32  99c6770'::uuid;
+               ^
+SELECT '5b35380a-7143-4912-9b55-f322699c67  '::uuid;
+ERROR:  invalid input syntax for type uuid: "5b35380a-7143-4912-9b55-f322699c67  "
+LINE 1: SELECT '5b35380a-7143-4912-9b55-f322699c67  '::uuid;
+               ^
+SELECT '  35380a-7143-4912-9b55-f322699c6770'::uuid;
+ERROR:  invalid input syntax for type uuid: "  35380a-7143-4912-9b55-f322699c6770"
+LINE 1: SELECT '  35380a-7143-4912-9b55-f322699c6770'::uuid;
+               ^
+SELECT 'AZ35380a-7143-4912-9b55-f322699c6770'::uuid;
+ERROR:  invalid input syntax for type uuid: "AZ35380a-7143-4912-9b55-f322699c6770"
+LINE 1: SELECT 'AZ35380a-7143-4912-9b55-f322699c6770'::uuid;
+               ^
+SELECT '{AZ35380a-7143-4912-9b55-f322699c6770}'::uuid;
+ERROR:  invalid input syntax for type uuid: "{AZ35380a-7143-4912-9b55-f322699c6770}"
+LINE 1: SELECT '{AZ35380a-7143-4912-9b55-f322699c6770}'::uuid;
+               ^
+SELECT '{AZ35380a714349129b55f322699c6770}'::uuid;
+ERROR:  invalid input syntax for type uuid: "{AZ35380a714349129b55f322699c6770}"
+LINE 1: SELECT '{AZ35380a714349129b55f322699c6770}'::uuid;
+               ^
+SELECT '{AZ35380a714349129b55f322699c67  }'::uuid;
+ERROR:  invalid input syntax for type uuid: "{AZ35380a714349129b55f322699c67  }"
+LINE 1: SELECT '{AZ35380a714349129b55f322699c67  }'::uuid;
+               ^
 -- clean up
 DROP TABLE guid1, guid2, guid3 CASCADE;
diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql
index 54f0d8f8255..bdbeb91fe4c 100644
--- a/src/test/regress/sql/uuid.sql
+++ b/src/test/regress/sql/uuid.sql
@@ -178,5 +178,21 @@ SELECT '\x019a2f859ced7225b99d9c55044a2563'::bytea::uuid;
 SELECT '\x1234567890abcdef'::bytea::uuid; -- error
 SELECT v = v::bytea::uuid as matched FROM gen_random_uuid() v;
 
+-- Test UUID shapes that the parser uses the SIMD path.
+SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid;
+SELECT '{5b35380a-7143-4912-9b55-f322699c6770}'::uuid;
+SELECT '5b35380a714349129b55f322699c6770'::uuid;
+SELECT '{5b35380a714349129b55f322699c6770}'::uuid;
+
+-- Test if the UUID parser using SIMD optimization correctly rejects invalid UUID
+-- string format.
+SELECT '5b35380a714349129b55f32  99c6770'::uuid;
+SELECT '5b35380a-7143-4912-9b55-f322699c67  '::uuid;
+SELECT '  35380a-7143-4912-9b55-f322699c6770'::uuid;
+SELECT 'AZ35380a-7143-4912-9b55-f322699c6770'::uuid;
+SELECT '{AZ35380a-7143-4912-9b55-f322699c6770}'::uuid;
+SELECT '{AZ35380a714349129b55f322699c6770}'::uuid;
+SELECT '{AZ35380a714349129b55f322699c67  }'::uuid;
+
 -- clean up
 DROP TABLE guid1, guid2, guid3 CASCADE;
-- 
2.55.0

From dc286d4772945d1141a19f447c889116042d5b47 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <[email protected]>
Date: Wed, 5 Aug 2026 14:21:24 -0700
Subject: [PATCH v4 2/2] uuid_parse_bench module.

---
 src/test/modules/uuid_parse_bench/Makefile    |  23 +
 src/test/modules/uuid_parse_bench/meson.build |  33 +
 .../uuid_parse_bench--1.0.sql                 |  24 +
 .../uuid_parse_bench/uuid_parse_bench.c       | 641 ++++++++++++++++++
 .../uuid_parse_bench/uuid_parse_bench.control |   5 +
 5 files changed, 726 insertions(+)
 create mode 100644 src/test/modules/uuid_parse_bench/Makefile
 create mode 100644 src/test/modules/uuid_parse_bench/meson.build
 create mode 100644 src/test/modules/uuid_parse_bench/uuid_parse_bench--1.0.sql
 create mode 100644 src/test/modules/uuid_parse_bench/uuid_parse_bench.c
 create mode 100644 src/test/modules/uuid_parse_bench/uuid_parse_bench.control

diff --git a/src/test/modules/uuid_parse_bench/Makefile b/src/test/modules/uuid_parse_bench/Makefile
new file mode 100644
index 00000000000..9919d7c8309
--- /dev/null
+++ b/src/test/modules/uuid_parse_bench/Makefile
@@ -0,0 +1,23 @@
+# src/test/modules/uuid_parse_bench/Makefile
+
+MODULE_big = uuid_parse_bench
+OBJS = \
+	$(WIN32RES) \
+	uuid_parse_bench.o
+PGFILEDESC = "uuid_parse_bench - microbenchmark for UUID input parsing"
+
+EXTENSION = uuid_parse_bench
+DATA = uuid_parse_bench--1.0.sql
+
+REGRESS = uuid_parse_bench
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/uuid_parse_bench
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/uuid_parse_bench/meson.build b/src/test/modules/uuid_parse_bench/meson.build
new file mode 100644
index 00000000000..8633ccd56ba
--- /dev/null
+++ b/src/test/modules/uuid_parse_bench/meson.build
@@ -0,0 +1,33 @@
+# Copyright (c) 2022-2026, PostgreSQL Global Development Group
+
+uuid_parse_bench_sources = files(
+  'uuid_parse_bench.c',
+)
+
+if host_system == 'windows'
+  uuid_parse_bench_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'uuid_parse_bench',
+    '--FILEDESC', 'uuid_parse_bench - microbenchmark for UUID input parsing',])
+endif
+
+uuid_parse_bench = shared_module('uuid_parse_bench',
+  uuid_parse_bench_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += uuid_parse_bench
+
+test_install_data += files(
+  'uuid_parse_bench.control',
+  'uuid_parse_bench--1.0.sql',
+)
+
+tests += {
+  'name': 'uuid_parse_bench',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'regress': {
+    'sql': [
+      'uuid_parse_bench',
+    ],
+  },
+}
diff --git a/src/test/modules/uuid_parse_bench/uuid_parse_bench--1.0.sql b/src/test/modules/uuid_parse_bench/uuid_parse_bench--1.0.sql
new file mode 100644
index 00000000000..d49371fa7c5
--- /dev/null
+++ b/src/test/modules/uuid_parse_bench/uuid_parse_bench--1.0.sql
@@ -0,0 +1,24 @@
+/* src/test/modules/uuid_parse_bench/uuid_parse_bench--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION uuid_parse_bench" to load this file. \quit
+
+--
+-- Time each parsing strategy against each input shape.  Passing NULL for
+-- paths or shapes selects all of them.
+--
+-- Valid paths:  scalar, simd, nosimd
+-- Valid shapes: canonical, bare32, braced_canonical, braced_bare32,
+--               dashed4, invalid_hex
+--
+CREATE FUNCTION uuid_parse_bench(nuuids int DEFAULT 100000,
+								 nloops int DEFAULT 5,
+								 paths text[] DEFAULT NULL,
+								 shapes text[] DEFAULT NULL,
+								 OUT path text,
+								 OUT shape text,
+								 OUT n_inputs int,
+								 OUT best_ms float8,
+								 OUT ns_per_parse float8)
+	RETURNS SETOF record
+	AS 'MODULE_PATHNAME' LANGUAGE C VOLATILE;
diff --git a/src/test/modules/uuid_parse_bench/uuid_parse_bench.c b/src/test/modules/uuid_parse_bench/uuid_parse_bench.c
new file mode 100644
index 00000000000..51e497fe98a
--- /dev/null
+++ b/src/test/modules/uuid_parse_bench/uuid_parse_bench.c
@@ -0,0 +1,641 @@
+/*--------------------------------------------------------------------------
+ *
+ * uuid_parse_bench.c
+ *		Microbenchmark for the UUID input parsing strategies.
+ *
+ * This module times several ways of turning UUID text into a pg_uuid_t, so
+ * that the trade-offs can be argued with measurements rather than intuition.
+ * The paths it measures are:
+ *
+ *	scalar		the character-at-a-time loop using isxdigit() and strtoul()
+ *	simd		shape detection + compaction + hex_decode_safe()
+ *	nosimd		the same, but forced through the scalar hex decoder
+ *
+ * "simd" and "nosimd" differ only in which hex decoder they call, which is
+ * why this module carries its own copy of the scalar decoder: the real
+ * hex_decode_safe() picks its implementation at compile time, so a single
+ * backend binary can otherwise only measure one of the two.
+ *
+ * Copyright (c) 2007-2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *		src/test/modules/uuid_parse_bench/uuid_parse_bench.c
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+
+#include "catalog/pg_type_d.h"
+#include "common/pg_prng.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "lib/stringinfo.h"
+#include "miscadmin.h"
+#include "nodes/miscnodes.h"
+#include "portability/instr_time.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/tuplestore.h"
+#include "utils/uuid.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(uuid_parse_bench);
+
+/* number of hex digits in a UUID body */
+#define UUID_HEX_LEN	(UUID_LEN * 2)
+/* length of the canonical 8x-4x-4x-4x-12x form */
+#define UUID_CANON_LEN	(UUID_HEX_LEN + 4)
+
+/*
+ * The parsing strategies we time.  Keep in sync with path_names[].
+ */
+typedef enum BenchPath
+{
+	PATH_SCALAR = 0,
+	PATH_SIMD,
+	PATH_NOSIMD,
+	NUM_PATHS,
+}			BenchPath;
+
+static const char *const path_names[] = {
+	"scalar", "simd", "nosimd",
+};
+
+/*
+ * The input shapes we time.  The fast path's advantage depends strongly on
+ * the shape, so this has to be an axis of the measurement rather than a
+ * single representative case.  dashed4 and invalid_hex are the shapes that
+ * fall back to the scalar parser, and so measure the cost of the fallback.
+ */
+typedef enum BenchShape
+{
+	SHAPE_CANONICAL = 0,		/* 8x-4x-4x-4x-12x, what uuid_out() emits */
+	SHAPE_BARE32,				/* 32 contiguous hex digits */
+	SHAPE_BRACED_CANONICAL,		/* {8x-4x-4x-4x-12x} */
+	SHAPE_BRACED_BARE32,		/* {32 hex digits} */
+	SHAPE_DASHED4,				/* dash after every group of 4: falls back */
+	SHAPE_INVALID_HEX,			/* canonical but with a bad digit: falls back */
+	NUM_SHAPES,
+}			BenchShape;
+
+static const char *const shape_names[] = {
+	"canonical", "bare32", "braced_canonical", "braced_bare32",
+	"dashed4", "invalid_hex",
+};
+
+/*
+ * Consume parse results so the compiler cannot delete the work we are timing.
+ */
+static volatile uint64 bench_sink = 0;
+
+/*
+ * Copy of string_to_uuid_scalar() from src/backend/utils/adt/uuid.c.
+ */
+static bool
+string_to_uuid_scalar(const char *source, pg_uuid_t *uuid)
+{
+	const char *src = source;
+	bool		braces = false;
+	int			i;
+
+	if (src[0] == '{')
+	{
+		src++;
+		braces = true;
+	}
+
+	for (i = 0; i < UUID_LEN; i++)
+	{
+		char		str_buf[3];
+
+		if (src[0] == '\0' || src[1] == '\0')
+			return false;
+		memcpy(str_buf, src, 2);
+		if (!isxdigit((unsigned char) str_buf[0]) ||
+			!isxdigit((unsigned char) str_buf[1]))
+			return false;
+
+		str_buf[2] = '\0';
+		uuid->data[i] = (unsigned char) strtoul(str_buf, NULL, 16);
+		src += 2;
+		if (src[0] == '-' && (i % 2) == 1 && i < UUID_LEN - 1)
+			src++;
+	}
+
+	if (braces)
+	{
+		if (*src != '}')
+			return false;
+		src++;
+	}
+
+	return *src == '\0';
+}
+
+/*
+ * hexlookup[], get_hex() and hex_decode_safe_scalar() below are copies of the
+ * versions in src/backend/utils/adt/encode.c.  They are static there, and in
+ * any case the exported hex_decode_safe() resolves to either the vectorized
+ * or the scalar implementation at compile time, so a backend built for a SIMD
+ * platform cannot otherwise measure what a non-SIMD platform would do.
+ *
+ * Being copies, these can drift from the originals.  Re-check them against
+ * encode.c before quoting any figure.
+ */
+static const int8 bench_hexlookup[128] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static inline bool
+bench_get_hex(const char *cp, char *out)
+{
+	unsigned char c = (unsigned char) *cp;
+	int			res = -1;
+
+	if (c < 127)
+		res = bench_hexlookup[c];
+
+	*out = (char) res;
+
+	return (res >= 0);
+}
+
+/*
+ * Copy of hex_decode_safe_scalar() in encode.c.
+ */
+static inline uint64
+bench_hex_decode_scalar(const char *src, size_t len, char *dst, bool *ok)
+{
+	const char *s,
+			   *srcend;
+	char		v1,
+				v2,
+			   *p;
+
+	srcend = src + len;
+	s = src;
+	p = dst;
+	*ok = true;
+	while (s < srcend)
+	{
+		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+		{
+			s++;
+			continue;
+		}
+		if (!bench_get_hex(s, &v1))
+		{
+			*ok = false;
+			return p - dst;
+		}
+		s++;
+		if (s >= srcend)
+		{
+			*ok = false;
+			return p - dst;
+		}
+		if (!bench_get_hex(s, &v2))
+		{
+			*ok = false;
+			return p - dst;
+		}
+		s++;
+		*p++ = (v1 << 4) | v2;
+	}
+
+	return p - dst;
+}
+
+/* ----------------------------------------------------------------
+ * Input classification, shared by the fast paths
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * How a given input can be decoded.  Note these are not BenchShape values:
+ * they say which fast path applies, not which shape the corpus was built in.
+ */
+#define FASTPATH_NONE		0	/* no fast path applies; hand off to scalar */
+#define FASTPATH_CONTIGUOUS	1	/* 32 hex digits, decode in place */
+#define FASTPATH_COMPACT	2	/* canonical, must have its dashes removed */
+
+/*
+ * Decide which fast path applies to source and, when one does, hand back the
+ * start of the body.  This is exactly the work string_to_uuid() does before
+ * it decodes anything.
+ */
+static inline int
+classify_input(const char *source, const char **bodyp)
+{
+	const char *body = source;
+	size_t		len = strlen(source);
+
+	/* Strip one optional surrounding brace pair */
+	if (len >= 2 && source[0] == '{' && source[len - 1] == '}')
+	{
+		body = source + 1;
+		len -= 2;
+	}
+
+	*bodyp = body;
+
+	if (len == UUID_HEX_LEN)
+		return FASTPATH_CONTIGUOUS;
+	if (len == UUID_CANON_LEN && body[8] == '-' && body[13] == '-' &&
+		body[18] == '-' && body[23] == '-')
+		return FASTPATH_COMPACT;
+
+	return FASTPATH_NONE;
+}
+
+/*
+ * Compact the canonical form into 32 contiguous hex digits.
+ */
+static inline void
+compact_canonical(const char *body, char *hexbuf)
+{
+	memcpy(&hexbuf[0], &body[0], 8);
+	memcpy(&hexbuf[8], &body[9], 4);
+	memcpy(&hexbuf[12], &body[14], 4);
+	memcpy(&hexbuf[16], &body[19], 4);
+	memcpy(&hexbuf[20], &body[24], 12);
+}
+
+/*
+ * Copy of hex_decode_safe() with SIMD
+ */
+static bool
+hex_decode_safe_simd(const char *source, pg_uuid_t *uuid)
+{
+	const char *body;
+	const char *hexsrc;
+	char		hexbuf[UUID_HEX_LEN];
+	uint64		written;
+	ErrorSaveContext esctx = {T_ErrorSaveContext};
+	int			fastpath = classify_input(source, &body);
+
+	if (fastpath == FASTPATH_NONE)
+		return string_to_uuid_scalar(source, uuid);
+
+	if (fastpath == FASTPATH_COMPACT)
+	{
+		compact_canonical(body, hexbuf);
+		hexsrc = hexbuf;
+	}
+	else
+		hexsrc = body;
+
+	written = hex_decode_safe(hexsrc, UUID_HEX_LEN, (char *) uuid->data,
+							  (Node *) &esctx);
+
+	if (esctx.error_occurred || written != UUID_LEN)
+		return string_to_uuid_scalar(source, uuid);
+
+	return true;
+}
+
+/*
+ * Simulating hex_decode_safe() with USE_NO_SIMD
+ */
+static bool
+hex_decode_safe_nosimd(const char *source, pg_uuid_t *uuid)
+{
+	const char *body;
+	const char *hexsrc;
+	char		hexbuf[UUID_HEX_LEN];
+	uint64		written;
+	bool		ok;
+	int			fastpath = classify_input(source, &body);
+
+	if (fastpath == FASTPATH_NONE)
+		return string_to_uuid_scalar(source, uuid);
+
+	if (fastpath == FASTPATH_COMPACT)
+	{
+		compact_canonical(body, hexbuf);
+		hexsrc = hexbuf;
+	}
+	else
+		hexsrc = body;
+
+	written = bench_hex_decode_scalar(hexsrc, UUID_HEX_LEN,
+									  (char *) uuid->data, &ok);
+
+	if (!ok || written != UUID_LEN)
+		return string_to_uuid_scalar(source, uuid);
+
+	return true;
+}
+
+/*
+ * Render one random UUID in the requested shape.  The caller owns the buffer,
+ * which must hold at least 48 bytes.
+ */
+static void
+render_shape(pg_prng_state *state, BenchShape shape, char *buf)
+{
+	unsigned char b[UUID_LEN];
+	static const char hex[] = "0123456789abcdef";
+	char		hexdigits[UUID_HEX_LEN];
+	char	   *p = buf;
+	int			i;
+
+	for (i = 0; i < UUID_LEN; i++)
+		b[i] = (unsigned char) pg_prng_uint32(state);
+	for (i = 0; i < UUID_LEN; i++)
+	{
+		hexdigits[i * 2] = hex[b[i] >> 4];
+		hexdigits[i * 2 + 1] = hex[b[i] & 0x0f];
+	}
+
+	if (shape == SHAPE_BRACED_CANONICAL || shape == SHAPE_BRACED_BARE32)
+		*p++ = '{';
+
+	switch (shape)
+	{
+		case SHAPE_BARE32:
+		case SHAPE_BRACED_BARE32:
+			memcpy(p, hexdigits, UUID_HEX_LEN);
+			p += UUID_HEX_LEN;
+			break;
+
+		case SHAPE_DASHED4:
+			/* a dash after every group of 4: legal, but not the fast path */
+			for (i = 0; i < UUID_HEX_LEN; i++)
+			{
+				if (i > 0 && i % 4 == 0)
+					*p++ = '-';
+				*p++ = hexdigits[i];
+			}
+			break;
+
+		default:
+			/* canonical 8x-4x-4x-4x-12x, optionally with a bad digit */
+			for (i = 0; i < UUID_HEX_LEN; i++)
+			{
+				if (i == 8 || i == 12 || i == 16 || i == 20)
+					*p++ = '-';
+				*p++ = hexdigits[i];
+			}
+			/* corrupt one digit so the fast path has to hand off */
+			if (shape == SHAPE_INVALID_HEX)
+				buf[1] = 'z';
+			break;
+	}
+
+	if (shape == SHAPE_BRACED_CANONICAL || shape == SHAPE_BRACED_BARE32)
+		*p++ = '}';
+	*p = '\0';
+}
+
+/*
+ * Build nuuids input strings of the given shape in the current memory
+ * context.  Everything is materialized before timing starts so that neither
+ * the PRNG nor the allocator shows up in the measurement.
+ */
+static char **
+build_corpus(BenchShape shape, int nuuids, uint64 seed)
+{
+	pg_prng_state state;
+	char	  **inputs = palloc(sizeof(char *) * nuuids);
+	int			i;
+
+	pg_prng_seed(&state, seed);
+	for (i = 0; i < nuuids; i++)
+	{
+		char		buf[48];
+
+		render_shape(&state, shape, buf);
+		inputs[i] = pstrdup(buf);
+	}
+
+	return inputs;
+}
+
+/* ----------------------------------------------------------------
+ * Timing
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * Run one path over one corpus nloops times and return the best wall time in
+ * milliseconds.  The best of several runs is reported rather than the mean
+ * because it is the least contaminated by scheduling noise.
+ */
+/*
+ * The inner loop, instantiated once per path.  Calling the parser directly
+ * rather than through parse_funcs[] matters: an indirect call would add the
+ * same few cycles to every path and so shrink exactly the differences this
+ * module exists to measure.  uuid_in() calls string_to_uuid() directly, so
+ * direct calls are also the more faithful model.
+ */
+#define TIME_ONE_LOOP(fn) \
+	do { \
+		for (i = 0; i < nuuids; i++) \
+		{ \
+			pg_uuid_t	u; \
+			if (fn(inputs[i], &u)) \
+				local += u.data[0]; \
+			else \
+				local++; \
+		} \
+	} while (0)
+
+static double
+time_path(BenchPath path, char **inputs, int nuuids, int nloops)
+{
+	double		best = -1.0;
+	int			loop;
+
+	for (loop = 0; loop < nloops; loop++)
+	{
+		instr_time	start,
+					stop;
+		uint64		local = 0;
+		double		ms;
+		int			i;
+
+		CHECK_FOR_INTERRUPTS();
+
+		INSTR_TIME_SET_CURRENT(start);
+		switch (path)
+		{
+			case PATH_SCALAR:
+				TIME_ONE_LOOP(string_to_uuid_scalar);
+				break;
+			case PATH_SIMD:
+				TIME_ONE_LOOP(hex_decode_safe_simd);
+				break;
+			case PATH_NOSIMD:
+				TIME_ONE_LOOP(hex_decode_safe_nosimd);
+				break;
+			case NUM_PATHS:
+				elog(ERROR, "unexpected path %d", (int) path);
+				break;
+		}
+		INSTR_TIME_SET_CURRENT(stop);
+		INSTR_TIME_SUBTRACT(stop, start);
+
+		bench_sink += local;
+
+		ms = INSTR_TIME_GET_DOUBLE(stop) * 1000.0;
+		if (best < 0.0 || ms < best)
+			best = ms;
+	}
+
+	return best;
+}
+
+/* ----------------------------------------------------------------
+ * SQL interface
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * Turn a text[] filter into a bool[] over names, or select everything when
+ * the array is NULL.
+ */
+static void
+parse_filter(ArrayType *arr, const char *const *names, int nnames,
+			 bool *selected, const char *what)
+{
+	Datum	   *elems;
+	bool	   *nulls;
+	int			nelems;
+	int			i;
+
+	if (arr == NULL)
+	{
+		for (i = 0; i < nnames; i++)
+			selected[i] = true;
+		return;
+	}
+
+	for (i = 0; i < nnames; i++)
+		selected[i] = false;
+
+	deconstruct_array_builtin(arr, TEXTOID, &elems, &nulls, &nelems);
+	for (i = 0; i < nelems; i++)
+	{
+		char	   *name;
+		int			j;
+
+		if (nulls[i])
+			ereport(ERROR,
+					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					 errmsg("%s array must not contain nulls", what)));
+
+		name = TextDatumGetCString(elems[i]);
+		for (j = 0; j < nnames; j++)
+		{
+			if (strcmp(name, names[j]) == 0)
+			{
+				selected[j] = true;
+				break;
+			}
+		}
+		if (j == nnames)
+		{
+			StringInfoData buf;
+
+			initStringInfo(&buf);
+			for (j = 0; j < nnames; j++)
+				appendStringInfo(&buf, "%s%s", j > 0 ? ", " : "", names[j]);
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized %s \"%s\"", what, name),
+					 errhint("Valid values are: %s.", buf.data)));
+		}
+	}
+}
+
+/*
+ * uuid_parse_bench(nuuids, nloops, paths, shapes)
+ *
+ * Time every selected path against every selected shape and return the
+ * matrix.
+ */
+Datum
+uuid_parse_bench(PG_FUNCTION_ARGS)
+{
+	int			nuuids = PG_GETARG_INT32(0);
+	int			nloops = PG_GETARG_INT32(1);
+	ArrayType  *patharr = PG_ARGISNULL(2) ? NULL : PG_GETARG_ARRAYTYPE_P(2);
+	ArrayType  *shapearr = PG_ARGISNULL(3) ? NULL : PG_GETARG_ARRAYTYPE_P(3);
+	bool		want_path[NUM_PATHS];
+	bool		want_shape[NUM_SHAPES];
+	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	int			shape;
+
+	if (nuuids <= 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("nuuids must be positive")));
+	if (nloops <= 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("nloops must be positive")));
+
+	parse_filter(patharr, path_names, NUM_PATHS, want_path, "path");
+	parse_filter(shapearr, shape_names, NUM_SHAPES, want_shape, "shape");
+
+	InitMaterializedSRF(fcinfo, 0);
+
+	for (shape = 0; shape < NUM_SHAPES; shape++)
+	{
+		MemoryContext corpusctx,
+					oldctx;
+		char	  **inputs;
+		int			path;
+
+		if (!want_shape[shape])
+			continue;
+
+		/*
+		 * Build each corpus in its own context so that the strings for one
+		 * shape are freed before the next is built.  With a large nuuids the
+		 * whole matrix would otherwise be resident at once.
+		 */
+		corpusctx = AllocSetContextCreate(CurrentMemoryContext,
+										  "uuid_parse_bench corpus",
+										  ALLOCSET_DEFAULT_SIZES);
+		oldctx = MemoryContextSwitchTo(corpusctx);
+		inputs = build_corpus(shape, nuuids, 42);
+		MemoryContextSwitchTo(oldctx);
+
+		for (path = 0; path < NUM_PATHS; path++)
+		{
+			Datum		values[5];
+			bool		nulls[5] = {0};
+			double		best_ms;
+
+			if (!want_path[path])
+				continue;
+
+			best_ms = time_path(path, inputs, nuuids, nloops);
+
+			values[0] = CStringGetTextDatum(path_names[path]);
+			values[1] = CStringGetTextDatum(shape_names[shape]);
+			values[2] = Int32GetDatum(nuuids);
+			values[3] = Float8GetDatum(best_ms);
+			values[4] = Float8GetDatum(best_ms * 1000000.0 / nuuids);
+
+			tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
+								 values, nulls);
+		}
+
+		MemoryContextDelete(corpusctx);
+	}
+
+	return (Datum) 0;
+}
diff --git a/src/test/modules/uuid_parse_bench/uuid_parse_bench.control b/src/test/modules/uuid_parse_bench/uuid_parse_bench.control
new file mode 100644
index 00000000000..60e75d3df2d
--- /dev/null
+++ b/src/test/modules/uuid_parse_bench/uuid_parse_bench.control
@@ -0,0 +1,5 @@
+# uuid_parse_bench extension
+comment = 'Microbenchmark for UUID input parsing'
+default_version = '1.0'
+module_pathname = '$libdir/uuid_parse_bench'
+relocatable = true
-- 
2.55.0

Reply via email to