Re: Speed up lpad() and rpad() for one-byte padding strings - Mailing list pgsql-hackers

From Sehrope Sarkuni
Subject Re: Speed up lpad() and rpad() for one-byte padding strings
Date
Msg-id CAH7T-ao_8pUW0gKs0aX0He-zff-fxPe7sd1PZOCX91=Xi6T=qg@mail.gmail.com
Whole thread
In response to Re: Speed up lpad() and rpad() for one-byte padding strings  (Nathan Bossart <nathandbossart@gmail.com>)
List pgsql-hackers
On Wed, Sep 23, 2026 at 10:41 AM Nathan Bossart
<nathandbossart@gmail.com> wrote:
>
> On Wed, Sep 23, 2026 at 10:10:09AM -0400, Sehrope Sarkuni wrote:
> > lpad() and rpad() pad one character at a time, calling
> > pg_mblen_range() and memcpy() once per padding char.  When the padding
> > string is a single byte, e.g., lpad(x, n, '0') or rpad(x, n, ' '),
> > the padding is that byte repeated, so the attached patch fills it with
> > one memset().
>
> I wonder if we could expand these gains by using SIMD whenever the vector
> length is divisible by the padding string length.  My hunch is that's where
> a lot of the memset() gains come from.

I think the repeated pg_mblen_range() calls for every iteration are a
big factor too.  I tried a different route that covers every pad
string (not just divisible) without explicit SIMD.

The attached v2 walks the pad string once to count its characters,
copies the whole repetitions as byte sequences, and runs the
per-character loop only for the partial final repetition.  The whole
repetitions are written by copying one and then doubling the copied
region, so it takes log2(repetitions) memcpy() calls and memcpy()
does the vectorizing.

Validation is unchanged from master.  The counting pass stops at the
number of characters needed, so a pad string ending in a lone lead
byte still errors only if the padding reaches that byte.  The two
identical loops in lpad() and rpad() become a static pad_fill().

This replaces the memset() patch.  On the one-byte case memset() was
5-7% faster than doubling (0.100 vs 0.107 ms at 1M), which does not
seem worth a separate fast path, and the multi-character controls that
were 5% slower with v1 now get the same speedup as everything else.

Same setup as before, median ms of 5 rounds:

  SELECT octet_length(rpad('x', N, PAD))

  PAD                N          before    after
  ' '                1000000     4.852    0.108
  'ab'               1000000     4.822    0.111
  'abc'               999999     4.824    0.107   (exact multiple)
  'abc'              1000000     4.793    0.108   (partial tail)
  'abcd'             1000000     4.809    0.110
  16 chars           1000000     5.012    0.107
  100 chars          1000000     4.917    0.110
  'é'                1000000     5.394    0.155
  'aéb'               999999     5.012    0.126   (exact multiple)
  'aéb'              1000000     4.994    0.122   (partial tail)
  ' '               10000000    53.705    5.466
  'ab'              10000000    53.376    5.413
  'abc'                 1000     0.063    0.057
  'abc'                   10     0.056    0.056

This is more of a change than just adding the memset() fastpath, but I
think the end result of the code is easier to follow too with both
lpad() and rpad() sharing the helper.

Regards,
-- Sehrope Sarkuni
Founder & CEO | JackDB, Inc. | https://www.jackdb.com/

Attachment

pgsql-hackers by date:

Previous
From: Nathan Bossart
Date:
Subject: Re: Speed up lpad() and rpad() for one-byte padding strings
Next
From: Nikolay Samokhvalov
Date:
Subject: Re: pg_*_advice: tsv load failure, etc.