Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1voQJD-003bsr-0J for pgsql-hackers@arkaria.postgresql.org; Fri, 06 Feb 2026 18:11:59 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1voQJC-0056oP-0a for pgsql-hackers@arkaria.postgresql.org; Fri, 06 Feb 2026 18:11:58 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1voQJB-0056oH-2d for pgsql-hackers@lists.postgresql.org; Fri, 06 Feb 2026 18:11:57 +0000 Received: from mail-ed1-x52f.google.com ([2a00:1450:4864:20::52f]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1voQJ9-00000001P5k-0ZtO for pgsql-hackers@postgresql.org; Fri, 06 Feb 2026 18:11:57 +0000 Received: by mail-ed1-x52f.google.com with SMTP id 4fb4d7f45d1cf-65808bb859cso3548629a12.2 for ; Fri, 06 Feb 2026 10:11:54 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1770401509; cv=none; d=google.com; s=arc-20240605; b=HpbFEz/H9TQK7zvR7qNhxnbHAT/K0v7HB8q4MoLpC9XwWWoHOZ/f1HFwZleUFMqLXv Vj+bWaWJe6Iuh+iU2rPLiV8a+WRVGEKqDZgKlupHT3avsUfDMLGWesJjMwh4WlTyfUMS owP1ZqquKjnuaIVtInkiaLhhG07O/8ClRYPt0C02J8As4STjiLEzRGY2rMxL68l1sb3X m/sZ9/lw9bx6TPSPFXxcN9dtOD4E7IAyxnJHeZPJXNRCQPy4u0EWH/kVl0Jlc1SRe5tQ vQj22hz/HKxQAw5fekf5Vh+BXKJe1UWVvUittjSv9clyaT+7bZX4Wc+UXwiWA4vOWxhn ivXg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=HtNSIeSOEyfm1zbB+FxPSphrjqvsv3iimltq+L0sZqQ=; fh=EkBO/OKnuJTjPO7FIRNKgCbc195YvO9jZty4Bqk9SCM=; b=dCHlcN8Ue5AuVjxGSSQ+0geHsxqxJFRSK2PuIFqQiTbBiFRNXZtH5OrTmWKJMrzEHo eWx0ljp7ko8ClYUDtzgXAqvx9pqr7uyv7kjj8722FNsJlqzqAkNu9ZTNPm4DWYiCvA9h skMYFC9BVSbKLiLAv5C8XMgIBTeeu4tAzPJonnf7wUpFVPY9vdI78hj7UyU98Wp1slov avVKgPJ46QOQKVNqa6TmXeq6wXtfRL3OE742VdIUzuuaATGQmIkBrq1Nb80IaEA4G+gI btu8zBDMqqE6qdA1qjM3d9zbuP7YWnAtCsGwYbXew2xwBU7IEtQaf4KydrRGJrZEcf2W /xXw==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=esi.dz; s=google; t=1770401509; x=1771006309; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=HtNSIeSOEyfm1zbB+FxPSphrjqvsv3iimltq+L0sZqQ=; b=Wb8jT1kqas+g78qOK74yg4V1gn7oi+aCfs8BobA8siq72V4oXLS+pX9/N/aorWIOvQ DuRL0rLsNSbhRYguAHqvMUb78Ruegmn4Ixp/9kD3DOvQmcCyeWJhJMI/IoRF4Ey7C1KA mqIDMLNFXn7H84ek7+WyDo4t68CHGLM8UBFfDIFOIpO1PHpH0LEG5RMN1ODi96Qknzdk F81JcHN9q/ilU7o6hIRJbd+jD2L+v8vo0AHqHRZDpFTs+ppOEA8oyWHOYwxhfPVxEfJs HhQM/2MD0gPkTseqbMLl3fNAub+ye8TI+ASEftnuYITarGiCo8jNchHpzs429vHL00bA epTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770401509; x=1771006309; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=HtNSIeSOEyfm1zbB+FxPSphrjqvsv3iimltq+L0sZqQ=; b=b7uoIu0+GwrBdwV35QZBYRfxGjLdCs5D3oUzhy5hQRtQJCFkvw3EcSzuz0n3ifnAx0 RVOD9k05nsyjEmNNejERRUQ6g4DgvtFnaN/F1La6fZRNppGHfj9qh+UiJBzRTq+UFrP+ dQY/C8TLd+2NFG4ACCV6154T+M6fn3DZ8/kzCJCRVEN+ky1/1dpNeniDcXzQIWhLkYIp WGX4lallWUiAFRKf40yVEGBZblMO1eGd6wotL0sWsN58+R6hdPCuOMrSL+VR4Fx0tlaC 5Z2aZxnRZ/JhsZLiUC7JjZ1+GUX5KVrDdgogQlFqTyeRY1SLvmYHVIqKrbz9ZUd7SBwv JNow== X-Forwarded-Encrypted: i=1; AJvYcCXE7EnwPhBWNVpdyASXSQA3xiMKQURgtuLmvUHCarK6ZyQrTh5XGGXy2xpqqwuMGyPDW4xSl36cKr982dyO@postgresql.org X-Gm-Message-State: AOJu0YylKWJT5jcJukcQyMA0RgUrlAg72WfJYoJojI6kRHAxsKeCDnlj w+OcT9lqGoV4nn0z1nrS3t3H46PTHW1HV2hRktj+3/pYYTaU8z6GjaD7A89oZFOPNapT7xYFIph EUOpOP+7kzVdMKrZVHPg1DKoUsMggfF82QJkKsVRd X-Gm-Gg: AZuq6aIYYVbSf+VFfRbUDBTVnBVDnjl9Wegt623N5t380dgB2yxh//rtbjj9+JN0PYg u5pdz4JKSvdF5ZOiGdpTZ0QAiMnH0sL0GgettkKQNpHA6wh7eWxAPyh8tXWVSKNNFSMFwmbgEz1 lC5LNOn20+G4p33451YmFs+S9VCugB2/9MXvXOSgN7PQeOvQWFJPhSj3IdqldiZl/nh/e+eWtRk tdebcE838xB4CJ6womlDQ6QLQWcBUA4dizVD7lHmJbxLilDWbZ3TqEc3zbsWsBHwGYZAANpfJkT XNtTCndxRMBWAk+lhZME9slJlXXbjg== X-Received: by 2002:a05:6402:27cd:b0:659:31bd:95f1 with SMTP id 4fb4d7f45d1cf-6598413574cmr2020620a12.13.1770401508955; Fri, 06 Feb 2026 10:11:48 -0800 (PST) MIME-Version: 1.0 References: <5d81fbbb-7609-4445-9bc4-8af211fb7674@dunslane.net> <8e226753-57af-489a-bfbe-caa23dd71286@dunslane.net> In-Reply-To: From: KAZAR Ayoub Date: Fri, 6 Feb 2026 19:11:37 +0100 X-Gm-Features: AZwV_QisHJDZyOE_yx2ODEVpFL0uhb8kyIW5MJWOSa6cOZTsyJtQczckm7wfoe0 Message-ID: Subject: Re: Speed up COPY FROM text/CSV parsing using SIMD To: Nazir Bilal Yavuz Cc: Neil Conway , Manni Wood , Nathan Bossart , Andrew Dunstan , Shinya Kato , PostgreSQL-development Content-Type: multipart/alternative; boundary="0000000000000d2451064a2bb96c" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000000d2451064a2bb96c Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hello, On Fri, Feb 6, 2026 at 2:51=E2=80=AFPM Nazir Bilal Yavuz wrote: > Hi, > > On Sat, 31 Jan 2026 at 19:21, KAZAR Ayoub wrote: > > > > On Wed, Jan 21, 2026 at 9:50=E2=80=AFPM Neil Conway > wrote: > >> > >> * I'm curious if we'll see better performance on large inputs if we > flush to `line_buf` periodically (e.g., at least every few thousand bytes > or so). Otherwise we might see poor data cache behavior if large inputs > with no control characters get evicted before we've copied them over. See > the approach taken in escape_json_with_len() in utils/adt/json.c > >> > > So i gave this a try, attached is the small patch that has v3 + the > suggestion added, here are the results with different threshold for > line_buf refill: > > > > Execution time compared to master: > > Workloadv3v3.1 (2k)v3.1 (4k)v3.1 (8k)v3.1 (16k)v3.1 (20k)v3.1 (28k) > > text/none-16.5%-17.4%-14.3%-12.6%-13.6%-10.5%-16.3% > > text/esc+5.6%+11.1%+3.1%+7.6%+3.0%+4.9%+4.2% > > csv/none-31.0%-29.9%-26.7%-30.1%-27.9%-30.2%-29.6% > > csv/quote+0.2%-0.6%-0.4%-1.0%+0.1%+2.5%-1.0% > > > > L1d cache miss rates: > > WorkloadMasterv3v3.1 (2k)v3.1 (4k)v3.1 (8k)v3.1 (16k)v3.1 (20k)v3.1 (28= k) > > text/none0.20%0.23%0.21%0.22%0.21%0.21%0.21%0.22% > > text/esc0.21%0.22%0.22%0.22%0.22%0.21%0.22%0.22% > > csv/none0.17%0.22%0.21%0.22%0.21%0.21%0.22%0.22% > > csv/quote0.18%0.22%0.19%0.20%0.20%0.19%0.20%0.20% > > > > On my laptop I have 32KB L1 cache per core. > > Results are super close, it is hard to see in the cache misses numbers > but execution times are saying other things, doing the periodic filling o= f > line_buf seems good to do. > > If Manni can rerun the benchmarks on these too, it would be nice to > confirm this. > > I looked at this change and had a couple of points. > > We already have REFILL_LINEBUF at the start of the for loop in the > CopyReadLineText() function (let=E2=80=99s call this refill #1). This ref= ills > when the input_buf_ptr >=3D copy_buf_len check is true. On my end, > copy_buf_len stays at 8191 until the end of the input, and then it > becomes the remaining amount. So when I set LINE_BUF_FLUSH_AFTER to > 8192, the REFILL_LINEBUF you added shouldn=E2=80=99t be called; instead, > refill #1 should be triggered. > > I verified this manually by adding some logging, and the results seem > to confirm this behavior. Based on that, there shouldn=E2=80=99t be a > performance difference when LINE_BUF_FLUSH_AFTER >=3D 8k. > > Could you please take a look and confirm whether you see the same behavio= r? > So just to make sure i understand this correctly, line_buf holds processed bytes of ONE line, so for the periodic flush that i did in: input_buf_ptr - cstate->input_buf_index >=3D LINE_BUF_FLUSH_AFTER if a line in the file is smaller than LINE_BUF_FLUSH_AFTER, the #2 REFILL_LINEBUF is never reached in a CopyReadLine entrance, as line_buf is cleared after a line: CopyReadLine(CopyFromState cstate, bool is_csv) { bool result; resetStringInfo(&cstate->line_buf); .... } So my previous benchmarks (ones that have LINE_BUF_FLUSH_AFTER > 4096) are wrong since I was working with lines of 4096 bytes. If: Line length < LINE_BUF_FLUSH_AFTER < INPUT_BUF_SIZE : This neither hits #1 REFILL_LINEBUF nor #2 PERIODIC REFILL_LINEBUF, this only reaches the #3 REFILL_LINEBUF before CopyReadLineText returns. So flushing here is just mitigating against l1d cache misses for long lines (lines that occupy for example > 70% input_buf size, maybe ?) Does this make sense for your case too with 8k ? So i propose removing REFILL_LINEBUF in #1, as it doesn't make sense anymore since PERIODIC REFILL_LINEBUF already does the job for smaller sizes than INPUT_BUF_SIZE (in accordance to most l1d cache sizes). So basically it becomes If (line length < LINE_BUF_FLUSH_AFTER) then flush at very end if (line length > LINE_BUF_FLUSH_AFTER) flush (line length / LINE_BUF_FLUSH_AFTER) + leftover times > Also, I noticed that json.c uses ESCAPE_JSON_FLUSH_AFTER set to 512, > so it might be worth trying smaller values here as well. If I'm correct about the usage of LINE_BUF_FLUSH_AFTER above, I think smaller values would imply too many memory loads that are unnecessary, as for 512 we aren't battling against l1d cache misses anymore, though I'll try it in the next benchmark. If this sounds right, I'll be re-benchmarking for multiple row sizes with different LINE_BUF_FLUSH_AFTER sizes. Regards, Ayoub --0000000000000d2451064a2bb96c Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hello,

On Fri, Feb 6, 2026 at 2:51=E2= =80=AFPM Nazir Bilal Yavuz <byavuz81@gmail.com> wrote:
Hi,

On Sat, 31 Jan 2026 at 19:21, KAZAR Ayoub <ma_kazar@esi.dz> wrote:
>
> On Wed, Jan 21, 2026 at 9:50=E2=80=AFPM Neil Conway <neil.conway@gmail.com> = wrote:
>>
>> * I'm curious if we'll see better performance on large inp= uts if we flush to `line_buf` periodically (e.g., at least every few thousa= nd bytes or so). Otherwise we might see poor data cache behavior if large i= nputs with no control characters get evicted before we've copied them o= ver. See the approach taken in escape_json_with_len() in utils/adt/json.c >>
> So i gave this a try, attached is the small patch that has v3 + the su= ggestion added, here are the results with different threshold for line_buf = refill:
>
> Execution time compared to master:
> Workloadv3v3.1 (2k)v3.1 (4k)v3.1 (8k)v3.1 (16k)v3.1 (20k)v3.1 (28k) > text/none-16.5%-17.4%-14.3%-12.6%-13.6%-10.5%-16.3%
> text/esc+5.6%+11.1%+3.1%+7.6%+3.0%+4.9%+4.2%
> csv/none-31.0%-29.9%-26.7%-30.1%-27.9%-30.2%-29.6%
> csv/quote+0.2%-0.6%-0.4%-1.0%+0.1%+2.5%-1.0%
>
> L1d cache miss rates:
> WorkloadMasterv3v3.1 (2k)v3.1 (4k)v3.1 (8k)v3.1 (16k)v3.1 (20k)v3.1 (2= 8k)
> text/none0.20%0.23%0.21%0.22%0.21%0.21%0.21%0.22%
> text/esc0.21%0.22%0.22%0.22%0.22%0.21%0.22%0.22%
> csv/none0.17%0.22%0.21%0.22%0.21%0.21%0.22%0.22%
> csv/quote0.18%0.22%0.19%0.20%0.20%0.19%0.20%0.20%
>
> On my laptop I have 32KB L1 cache per core.
> Results are super close, it is hard to see in the cache misses numbers= but execution times are saying other things, doing the periodic filling of= line_buf seems good to do.
> If Manni can rerun the benchmarks on these too, it would be nice to co= nfirm this.

I looked at this change and had a couple of points.

We already have REFILL_LINEBUF at the start of the for loop in the
CopyReadLineText() function (let=E2=80=99s call this refill #1). This refil= ls
when the input_buf_ptr >=3D copy_buf_len check is true. On my end,
copy_buf_len stays at 8191 until the end of the input, and then it
becomes the remaining amount. So when I set LINE_BUF_FLUSH_AFTER to
8192, the REFILL_LINEBUF you added shouldn=E2=80=99t be called; instead, refill #1 should be triggered.

I verified this manually by adding some logging, and the results seem
to confirm this behavior. Based on that, there shouldn=E2=80=99t be a
performance difference when LINE_BUF_FLUSH_AFTER >=3D 8k.

Could you please take a look and confirm whether you see the same behavior?=
So just to make sure i understand this corr= ectly, line_buf holds processed bytes of ONE line, so for the periodic flus= h that i did in:
input_buf_ptr - cstate->input_buf_index &= gt;=3D LINE_BUF_FLUSH_AFTER
if a line in the file is smaller = than=C2=A0LINE_BUF_FLUSH_AFTER, the #2 REFILL_LINEBUF is never reached in a= CopyReadLine entrance, as line_buf is cleared after a line:

CopyRea= dLine(CopyFromState cstate, bool is_csv)
{
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 bool result;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 resetStringInfo(&cstate= ->line_buf);
....
}

So my previous benchmarks (o= nes that have=C2=A0LINE_BUF_FLUSH_AFTER > 4096) are wrong since I was wo= rking with lines of 4096 bytes.
If: Line length < LINE_BUF= _FLUSH_AFTER < INPUT_BUF_SIZE : This neither hits #1 REFILL_LINEBUF nor = #2 PERIODIC REFILL_LINEBUF, this only reaches the #3=C2=A0REFILL_LINEBUF be= fore CopyReadLineText returns.

So flushing here is= just mitigating against l1d cache misses for long lines (lines that occupy= for example=C2=A0 > 70% input_buf size, maybe ?)
Does this ma= ke sense for your case too with 8k ?
So i propose removing RE= FILL_LINEBUF in #1, as it doesn't make sense anymore since=C2=A0PERIODI= C REFILL_LINEBUF already does the job for smaller sizes than INPUT_BUF_SIZE= (in accordance to most l1d cache sizes).
So basically it becomes=
If (line length <=C2=A0LINE_BUF_FLUSH_AFTER) then flush a= t very end
if (line length >=C2=A0LINE_BUF_FLUSH_AFTER) fl= ush (line length /=C2=A0LINE_BUF_FLUSH_AFTER)=C2=A0+ leftover times
=C2=A0
Also, I noticed that json.c uses ESCAPE_JSON_FLUSH_AFTER set to 512,
so it might be worth trying smaller values here as well.=C2=A0
=
If I'm correct about the usage of LINE_BUF_FLUSH_AFTER above, I th= ink smaller values would imply too many memory loads that are unnecessary, = as for 512 we aren't=C2=A0battling against l1d cache misses anymore, th= ough I'll try it in the next benchmark.=C2=A0
If this sou= nds right, I'll be re-benchmarking=C2=A0for multiple row sizes with dif= ferent=C2=A0LINE_BUF_FLUSH_AFTER sizes.

Regards,
Ayoub
--0000000000000d2451064a2bb96c--