From 3da65874d99d0b7e5fd7c918f8c31d3ebcaef338 Mon Sep 17 00:00:00 2001
From: Neil Conway <neil.conway@gmail.com>
Date: Fri, 7 Jun 2024 11:25:43 -0400
Subject: [PATCH v4 6/6] Optimize COPY FROM using SIMD

CopyReadLineText() scans the COPY input buffer looking for newlines and escape
sequences (as well as quotes in CSV mode). We can use SIMD instructions to
efficiently skip chunks of text that don't contain any interesting
characters. This yields a significant performance improvement for wide rows
(many attributes and/or wide attribute values).
---
 src/backend/commands/copyfromparse.c | 36 ++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c
index 067a33f924..f1f2415bde 100644
--- a/src/backend/commands/copyfromparse.c
+++ b/src/backend/commands/copyfromparse.c
@@ -73,6 +73,7 @@
 #include "nodes/miscnodes.h"
 #include "pgstat.h"
 #include "port/pg_bswap.h"
+#include "port/simd.h"
 #include "utils/builtins.h"
 #include "utils/rel.h"
 
@@ -1180,6 +1181,7 @@ CopyReadLineText(CopyFromState cstate)
 	bool		need_data = false;
 	bool		hit_eof = false;
 	bool		result = false;
+	Vector8		chunk;
 
 	/* CSV variables */
 	bool		first_char_in_line = true;
@@ -1261,6 +1263,40 @@ CopyReadLineText(CopyFromState cstate)
 			need_data = false;
 		}
 
+		/*
+		 * If there is enough data available, use SIMD to check for special
+		 * characters. This allows us to efficiently skip large chunks of
+		 * text, in the common case that newlines and control characters are
+		 * relatively rare.
+		 */
+		Assert(input_buf_ptr <= cstate->input_buf_len);
+		if (cstate->input_buf_len - input_buf_ptr >= sizeof(Vector8))
+		{
+			vector8_load(&chunk, (const uint8 *) &input_buf[input_buf_ptr]);
+
+			if (!(vector8_has(chunk, (unsigned char) '\\') ||
+				  vector8_has(chunk, (unsigned char) '\r') ||
+				  vector8_has(chunk, (unsigned char) '\n') ||
+				  (cstate->opts.csv_mode && vector8_has(
+					  chunk, (unsigned char) escapec)) ||
+				  (cstate->opts.csv_mode && vector8_has(
+					  chunk, (unsigned char) quotec))))
+			{
+				input_buf_ptr += sizeof(Vector8);
+				continue;
+			}
+
+			/*
+			 * Otherwise, proceed byte-by-byte. Note that on subsequent loop
+			 * iterations, because we will only advance input_buf_ptr by a few
+			 * bytes (usually only one), the SIMD checks above will often be
+			 * repeated on an overlapping range of bytes.
+			 *
+			 * TODO: check whether the bookkeeping required to avoid this is
+			 * worth the cost/complexity.
+			 */
+		}
+
 		/* OK to fetch a character */
 		prev_raw_ptr = input_buf_ptr;
 		c = input_buf[input_buf_ptr++];
-- 
2.39.3 (Apple Git-146)

