Hi,

On Wed, Aug 5, 2026 at 5:20 PM Masahiko Sawada <[email protected]> wrote:
>
> 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.

In v3, uuid.c doesn't include port/simd.h, so USE_NO_SIMD isn't
defined there. That makes the USE_NO_SIMD check in string_to_uuid()
confusing, since it tests a macro that isn't visible in this file. The
check reads as though it sends no-SIMD builds to the scalar parser,
but because the macro is undefined, string_to_uuid() always runs the
fast path, even on a no-SIMD build. That does no harm because the fast
path still falls back to the scalar parser for uncommon shapes and
errors, and hex_decode_safe() picks its own scalar path when SIMD is
off, which is already faster than isxdigit()+strtoul(). So the gate
wasn't buying anything, and v4 drops it, which is the right call.

> 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.

I ran the benchmark locally and see similar numbers. Both paths in
hex_decode_safe() beat the current isxdigit()+strtoul() approach. The
SIMD path is roughly 20-40x faster on the common shapes, and even the
no-SIMD path is about 15x faster.

  path  |      shape       | n_inputs | best_ms
--------+------------------+----------+---------
 scalar | canonical        |   100000 |  31.820
 simd   | canonical        |   100000 |   1.393
 nosimd | canonical        |   100000 |   2.136
 scalar | bare32           |   100000 |  29.585
 simd   | bare32           |   100000 |   0.699
 nosimd | bare32           |   100000 |   2.002
 scalar | braced_canonical |   100000 |  31.906
 simd   | braced_canonical |   100000 |   1.412
 nosimd | braced_canonical |   100000 |   2.143
 scalar | braced_bare32    |   100000 |  29.170
 simd   | braced_bare32    |   100000 |   0.751
 nosimd | braced_bare32    |   100000 |   2.031
 scalar | dashed4          |   100000 |  30.424
 simd   | dashed4          |   100000 |  30.869
 nosimd | dashed4          |   100000 |  30.900
 scalar | invalid_hex      |   100000 |   0.320
 simd   | invalid_hex      |   100000 |   1.752
 nosimd | invalid_hex      |   100000 |   0.708
(18 rows)

The attached v4-0001 patch looks good to me. One comment though. Can
we add a positive test case that hits string_to_uuid_scalar and does
not error out?

--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com


Reply via email to